以最常用的STM32为例说明:

STM32 启动流程的核心是从硬件初始化到执行用户主程序(main 函数) 的有序过程,依赖启动文件(汇编编写)和硬件寄存器协同完成,具体可分为 4 个关键阶段:

1. 硬件复位触发

  • 当 STM32 上电(VDD 稳定)或复位引脚(NRST)被拉低时,芯片触发硬件复位
  • 复位后,芯片自动将   程序计数器(PC)初始化为 0x00000000(存储启动地址),并将                                                 栈指针 (SP)指向启动文件定义的初始栈地址。

2. 启动文件初始化(核心阶段)

复位后首先执行启动文件(如startup_stm32f103xx.s,完成底层初始化,主要做 3 件事:

  • 初始化栈(Stack):设置栈的大小和起始地址(由Stack_Size定义),为函数调用、局部变量分配内存空间。
  • 初始化堆(Heap):设置堆的大小和起始地址(由Heap_Size定义),为malloc等动态内存分配函数提供支持(可选,部分场景可禁用)。
  • 初始化中断向量表(IVT)
    • 中断向量表默认存储在 0x00000000 地址(或通过 BOOT 引脚选择的 Flash/SRAM 地址)。
    • 将各中断服务函数(ISR)的地址填入向量表,确保中断触发时能正确跳转。

       负责初始化栈 / 堆、中断向量表、复位处理,并最终跳转至main函数

; 栈配置(栈大小由Stack_Size定义,栈顶地址初始化为_estack)
Stack_Size      EQU     0x00000400  ; 栈大小1KB
                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
_estack         EQU     .+0         ; 栈顶地址

; 堆配置(堆大小由Heap_Size定义,用于动态内存分配)
Heap_Size       EQU     0x00000200  ; 堆大小512B
                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

; 中断向量表(起始地址0x00000000,复位后PC首先指向这里)
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     _estack                  ; 0: 栈顶地址(复位后SP初始值)
                DCD     Reset_Handler             ; 1: 复位中断服务函数(核心入口)
                DCD     NMI_Handler               ; 2: NMI中断
                DCD     HardFault_Handler         ; 3: 硬fault中断
                ; ... 省略其他中断向量(共60个,因型号而异)
__Vectors_End
__Vectors_Size  EQU     __Vectors_End - __Vectors

; 复位处理函数(核心启动逻辑)
                AREA    |.text|, CODE, READONLY
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                ; 1. 复制数据段(从Flash到SRAM,初始化全局变量)
                LDR     R0, =__initial_sp
                LDR     R1, =__data_start__
                LDR     R2, =__data_end__
copy_data:
                CMP     R1, R2
                ITT     LT
                LDRLT   R3, [R0], #4
                STRLT   R3, [R1], #4
                BLT     copy_data

                ; 2. 初始化BSS段(清零未初始化的全局变量)
                LDR     R1, =__bss_start__
                LDR     R2, =__bss_end__
zero_bss:
                CMP     R1, R2
                IT      LT
                STRLT   R0, [R1], #4
                BLT     zero_bss

                ; 3. 调用系统初始化函数(配置时钟)
                BL      SystemInit

                ; 4. 跳转至用户main函数(启动完成)
                BL      main
                B       .  ; main函数返回后进入死循环
                ENDP

; 其他中断服务函数默认实现(弱定义,用户可重写)
NMI_Handler     PROC
                EXPORT  NMI_Handler               [WEAK]
                B       .
                ENDP

HardFault_Handler PROC
                EXPORT  HardFault_Handler         [WEAK]
                B       .
                ENDP
; ... 省略其他中断服务函数

                ALIGN
                END

3. 执行系统初始化(SystemInit 函数)

启动文件初始化完成后,会调用 C 语言编写的 **SystemInit()函数 **(位于system_stm32xxxx.c),核心是初始化系统时钟(SYSCLK)

SystemInit函数负责配置 STM32 的系统时钟(从默认 HSI 切换到目标频率)。

void SystemInit(void) {
  /* 1. 复位RCC寄存器(恢复默认状态) */
  RCC->CR |= (uint32_t)0x00000001;  // 使能内部高速时钟HSI(8MHz)
  
  /* 2. 配置时钟树(以72MHz为例) */
  // 使能外部高速时钟HSE(8MHz晶振)
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);
  // 等待HSE稳定
  while (!(RCC->CR & RCC_CR_HSERDY));
  
  // 配置PLL(锁相环):HSE作为PLL输入,倍频9倍(8MHz*9=72MHz)
  RCC->CFGR |= RCC_CFGR_PLLSRC;       // PLL时钟源=HSE
  RCC->CFGR |= RCC_CFGR_PLLMULL9;     // PLL倍频系数=9
  
  // 使能PLL
  RCC->CR |= RCC_CR_PLLON;
  // 等待PLL稳定
  while (!(RCC->CR & RCC_CR_PLLRDY));
  
  // 配置系统时钟源为PLL(72MHz)
  RCC->CFGR &= (uint32_t)((uint32_t)~RCC_CFGR_SW);
  RCC->CFGR |= RCC_CFGR_SW_PLL;       // 系统时钟=PLL输出
  while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
  
  /* 3. 配置外设总线时钟 */
  RCC->CFGR |= RCC_CFGR_HPRE_DIV1;    // AHB总线=72MHz(HCLK=SYSCLK)
  RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;   // APB1总线=36MHz(低速外设)
  RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;   // APB2总线=72MHz(高速外设)
  
  /* 4. 其他初始化(如关闭不必要的外设时钟,降低功耗) */
  // ...
}

    4. 跳转至用户主程序(main 函数)

    SystemInit()执行完毕后,启动流程进入用户代码阶段:

    • 启动文件通过bl main(分支跳转指令),将程序控制权移交至用户编写的 **main()函数 **。
    • 此后执行main函数内的逻辑(如外设初始化、业务代码),完成 STM32 的正常启动。

    关键补充:BOOT 引脚的影响

    STM32 的BOOT0/BOOT1 引脚会决定复位后 “程序从哪里启动”,即 PC 初始指向的地址,常见模式如下:

    BOOT0 BOOT1 启动模式 程序起始地址 用途场景
    0 X 主 Flash 启动 0x08000000 正常量产(用户程序存 Flash)
    1 0 系统存储器启动 0x1FFF0000 串口下载程序(ISP 模式)
    1 1 SRAM 启动 0x20000000 调试(程序暂存 SRAM)

    综上,STM32 启动流程的本质是:

    MCU 上电/复位
     

    步骤 动作
    硬件复位 PC指针指向0地址,SP指向启动文件栈地址
    启动文件初始化 初始化栈堆和中断向量
    系统时钟配置 系统时钟配置
    跳转执行main函数 执行业务罗逻辑


     

    • 默认情况下,芯片复位后使用内部高速时钟(HSI,如 8MHz) 运行。
    • SystemInit()会根据配置(如是否使能外部高速时钟 HSE、锁相环 PLL),将系统时钟切换到目标频率(如 72MHz、168MHz,需匹配芯片型号)。
    • 同时初始化 AHB、APB1、APB2 等外设总线时钟,为后续外设驱动提供时钟支持。

    Logo

    智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

    更多推荐