典型arm32位单片机启动流程(从上电到main.c)

0 启动流程

  1. 复位
  2. 通过boot引脚选择启动模式
  3. 从地址0x00000000读取 __initial_sp 初始栈顶值到MSP
  4. 从地址0x00000004读取 Reset_Handler 地址到PC
  • 32位系统, 字长4字节
  1. 指向复位向量的内容
  2. 执行SystemInit函数, 初始化系统时钟
  3. 执行__main, 软件设置SP指针, 初始化栈空间, 清除bss段, 最后跳转到mian函数

1 启动模式

BOOT0 BOOT1 启动模式
0 X 从FLASH启动0x0800 0000
1 0 从系统存储器启动(bootROM)
1 1 从RAM启动0x2000 0000
  • pc指针在硬件复位后会自动指向0x0000 0000, 而Flash实际起始地址:0x0800 0000, RAM实际起始地址: 0x2000 0000, 硬件会自动将0x0000 0000映射到对应的地址

2 启动流程

2.1 Reset_Handler 复位向量

2.1.1 Reset_Handler 源代码:

c 复制代码
; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

2.1.2 代码分析:

导入 SystemInit 和 __main 两个符号,并LDR数据到R0寄存器,跳转执行(BLX/BX)

2.2 SystemInit

2.2.1 SystemInit源代码:

2.2.1.1HAL库中的SystemInit

c 复制代码
void SystemInit(void)
{
  /* FPU settings ------------------------------------------------------------*/
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  #endif

#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
  SystemInit_ExtMemCtl(); 
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */

  /* Configure the Vector Table location -------------------------------------*/
#if defined(USER_VECT_TAB_ADDRESS)
  SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#endif /* USER_VECT_TAB_ADDRESS */
}

2.2.1.2 STD库中的SystemInit

2.2.2 代码分析:

  1. HAL库中的SystemInit: 根据宏配置初始化FPU, 外部SRAM, 中断向量表(根据用户设置的地址)
  2. STD库中的SystemInit: 根据宏配置初始化FPU, 外部SRAM, 初始化PLL, 设置系统时钟, 根据宏设置中断向量表地址(只能配置FLASH_BASE和SRAM_BASE)

2.3 __main

2.3.1 代码分析:

复制代码
1. _初始化RW段和ZI段
  - RW:程序中已经初始化的变量所占空间
  - ZI:未初始化的static和全局变量以及堆栈所占的空间
2. 调用__rt_entry()函数
  1. __user_initial_stackheap 是 标准C库初始化时自动调用的函数,主要用于向C库传递堆(Heap)和栈(Stack)的地址信息。它的执行时机会在复位处理函数跳转到 __main 后,在 C库初始化阶段自动触发
  2. 初始化堆栈
  3. 初始化库函数
  4. 最后跳转到main函数

3 进入mian函数

4 启动文件分析

4.1 __initial_sp

栈顶地址,复位时 CPU 会把这个值加载到堆栈指针 SP,告诉程序临时数据存哪里。

c 复制代码
Stack_Size      EQU     0x800              ; 定义栈大小为 2KB (2048 bytes)

AREA    STACK, NOINIT, READWRITE, ALIGN=3  ; 定义一个名为 STACK 的内存区域
Stack_Mem       SPACE   Stack_Size         ; 分配连续 2KB 的栈内存空间
__initial_sp                               ; 声明栈顶符号

4.2 __heap_limit

c 复制代码
Heap_Size      EQU     0x400          ; 定义堆大小为 1KB (1024 bytes)                
AREA    HEAP, NOINIT, READWRITE, ALIGN=3  ; 定义可读写、8字节对齐的HEAP段
__heap_base                           ; 堆起始地址符号(链接器识别)
Heap_Mem        SPACE   Heap_Size     ; 分配连续 1KB 堆内存空间
__heap_limit                          ; 堆结束地址符号(Heap_Mem + Heap_Size)

4.3 处理器模式设置

c 复制代码
PRESERVE8             ; 要求堆栈8字节对齐(兼容Cortex-M系列)                
THUMB                 ; 指定使用Thumb指令集

4.4 中断向量表

复制代码
AREA    RESET, DATA, READONLY  ; 定义只读数据段                
EXPORT  __Vectors      ; 导出向量表起始地址(用于链接脚本)                
EXPORT  __Vectors_End                
EXPORT  __Vectors_Size__Vectors                                              
DCD     __initial_sp  ; 地址0: 主栈顶地址(硬件自动加载到MSP)                
DCD     Reset_Handler ; 地址4: 复位处理函数(程序入口)                 
; ▼ 内核异常向量 ▼                
DCD     NMI_Handler                ; NMI                
DCD     HardFault_Handler          ; 硬件错误                
DCD     MemManage_Handler          ; 内存管理错误                
DCD     BusFault_Handler           ; 总线错误                
DCD     UsageFault_Handler         ; 用法错误                
DCD     0                          ; 保留                
DCD     0                                          
DCD     0                                          
DCD     0                                          
DCD     SVC_Handler                ; 系统调用                
DCD     DebugMon_Handler           ; 调试监控                
DCD     0                                         
 DCD     PendSV_Handler             ; 可挂起系统调用                
 DCD     SysTick_Handler            ; 系统节拍定时器                 
 ; ▼ 外设中断向量 ▼                
 DCD     WWDG_IRQHandler            ; 窗口看门狗                                                     
 DCD     PVD_IRQHandler             ; 电源电压检测                                                
 DCD     TAMP_STAMP_IRQHandler       ; 入侵检测和时间戳                ;
 ...(后续DCD均为具体外设的中断入口)
 __Vectors_End                        ; 向量表结束标识
 __Vectors_Size  EQU  __Vectors_End - __Vectors  ; 计算向量表大小
4.5 Reset_Handler
; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

4.6 异常处理函数

c 复制代码
NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler          [WEAK]
                B       .
                ENDP
MemManage_Handler\
                PROC
                EXPORT  MemManage_Handler          [WEAK]
                B       .
                ENDP
BusFault_Handler\
                PROC
                EXPORT  BusFault_Handler           [WEAK]
                B       .
                ENDP
UsageFault_Handler\
                PROC
                EXPORT  UsageFault_Handler         [WEAK]
                B       .
                ENDP
SVC_Handler     PROC
                EXPORT  SVC_Handler                [WEAK]
                B       .
                ENDP
DebugMon_Handler\
                PROC
                EXPORT  DebugMon_Handler           [WEAK]
                B       .
                ENDP
PendSV_Handler  PROC
                EXPORT  PendSV_Handler             [WEAK]
                B       .
                ENDP
SysTick_Handler PROC
                EXPORT  SysTick_Handler            [WEAK]
                B       .
                ENDP

Default_Handler PROC

                EXPORT  WWDG_IRQHandler                   [WEAK]                                        
                EXPORT  PVD_IRQHandler                    [WEAK]                      
                EXPORT  TAMP_STAMP_IRQHandler             [WEAK]         
                EXPORT  RTC_WKUP_IRQHandler               [WEAK]                     
                EXPORT  FLASH_IRQHandler                  [WEAK]                                         
                EXPORT  RCC_IRQHandler                    [WEAK]                                            
                EXPORT  EXTI0_IRQHandler                  [WEAK]                                            
                EXPORT  EXTI1_IRQHandler                  [WEAK]                                             
                EXPORT  EXTI2_IRQHandler                  [WEAK]                                            
                EXPORT  EXTI3_IRQHandler                  [WEAK]                                           
                EXPORT  EXTI4_IRQHandler                  [WEAK]                                            
                EXPORT  DMA1_Stream0_IRQHandler           [WEAK]                                
                EXPORT  DMA1_Stream1_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream2_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream3_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream4_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream5_IRQHandler           [WEAK]                                   
                EXPORT  DMA1_Stream6_IRQHandler           [WEAK]                                   
                EXPORT  ADC_IRQHandler                    [WEAK]                                                                        
                EXPORT  EXTI9_5_IRQHandler                [WEAK]                                    
                EXPORT  TIM1_BRK_TIM9_IRQHandler          [WEAK]                  
                EXPORT  TIM1_UP_TIM10_IRQHandler          [WEAK]                
                EXPORT  TIM1_TRG_COM_TIM11_IRQHandler     [WEAK] 
                EXPORT  TIM1_CC_IRQHandler                [WEAK]                                   
                EXPORT  TIM2_IRQHandler                   [WEAK]                                            
                EXPORT  TIM3_IRQHandler                   [WEAK]                                            
                EXPORT  TIM4_IRQHandler                   [WEAK]                                            
                EXPORT  I2C1_EV_IRQHandler                [WEAK]                                             
                EXPORT  I2C1_ER_IRQHandler                [WEAK]                                             
                EXPORT  I2C2_EV_IRQHandler                [WEAK]                                            
                EXPORT  I2C2_ER_IRQHandler                [WEAK]                                               
                EXPORT  SPI1_IRQHandler                   [WEAK]                                           
                EXPORT  SPI2_IRQHandler                   [WEAK]                                            
                EXPORT  USART1_IRQHandler                 [WEAK]                                          
                EXPORT  USART2_IRQHandler                 [WEAK]                                                                                  
                EXPORT  EXTI15_10_IRQHandler              [WEAK]                                  
                EXPORT  RTC_Alarm_IRQHandler              [WEAK]                  
                EXPORT  OTG_FS_WKUP_IRQHandler            [WEAK]                        
                EXPORT  DMA1_Stream7_IRQHandler           [WEAK]                                                                                     
                EXPORT  SDIO_IRQHandler                   [WEAK]                                             
                EXPORT  TIM5_IRQHandler                   [WEAK]                                             
                EXPORT  SPI3_IRQHandler                   [WEAK]                                                               
                EXPORT  DMA2_Stream0_IRQHandler           [WEAK]                                  
                EXPORT  DMA2_Stream1_IRQHandler           [WEAK]                                   
                EXPORT  DMA2_Stream2_IRQHandler           [WEAK]                                    
                EXPORT  DMA2_Stream3_IRQHandler           [WEAK]                                    
                EXPORT  DMA2_Stream4_IRQHandler           [WEAK]                                                                                                     
                EXPORT  OTG_FS_IRQHandler                 [WEAK]                                       
                EXPORT  DMA2_Stream5_IRQHandler           [WEAK]                                   
                EXPORT  DMA2_Stream6_IRQHandler           [WEAK]                                   
                EXPORT  DMA2_Stream7_IRQHandler           [WEAK]                                   
                EXPORT  USART6_IRQHandler                 [WEAK]                                           
                EXPORT  I2C3_EV_IRQHandler                [WEAK]                                              
                EXPORT  I2C3_ER_IRQHandler                [WEAK]                                              
                EXPORT  FPU_IRQHandler                    [WEAK]
                                EXPORT  SPI4_IRQHandler                   [WEAK]
                EXPORT  SPI5_IRQHandler                   [WEAK]

4.7 __user_initial_stackheap

c 复制代码
                IF      :DEF:__MICROLIB  ; 当使用微库时的路径
                ; ▼ 直接导出符号供微库使用 ▼                 
                EXPORT  __initial_sp     ; 导出初始栈顶地址(MICROLIB需要)                 
                EXPORT  __heap_base      ; 导出堆起始地址                 
                EXPORT  __heap_limit     ; 导出堆结束地址                                 
                ELSE      ; 使用标准C库时的路径                 
                ; ▼ 导出动态堆栈初始化函数 ▼                 
                IMPORT  __use_two_region_memory  ; 声明C库内存模型                 
                EXPORT  __user_initial_stackheap  ; 导出初始化函数                 
__user_initial_stackheap  ; 堆栈初始化函数(由C库调用)                 
                LDR     R0, =Heap_Mem    ; R0 = 堆起始地址(用于malloc)                 
                LDR     R1, =(Stack_Mem + Stack_Size)  ; R1 = 栈顶地址                 
                LDR     R2, = (Heap_Mem +  Heap_Size)   ; R2 = 堆结束地址                 
                LDR     R3, =Stack_Mem    ; R3 = 栈底地址(监测栈溢出)                 
                BX      LR                ; 返回调用者(C库)                                  
                ENDIF                 END  ; 文件结束

参考文档

[深入剖析STM32]STM32 启动流程详解
stm32的启动文件详解 Reset_Handler做了什么工作 疑问--初始化pc指针的操作在哪里 ---硬件设置SP 和 PC的值_reset handler-CSDN博客
STM32启动代码分析及其汇编学习-ARM - 蓝天上的云℡ - 博客园
STM32_从SystemInit、__main到main() 已修正 - 蓝天上的云℡ - 博客园
STM32启动过程详解-CSDN博客
关于ARM CM3的启动文件分析 - strongwong - 博客园
15. 启动文件详解 --- [野火]STM32库开发实战指南------基于野火霸天虎开发板 文档