单片机(STM32)启动流程
STM32 启动流程的核心是。
·
以最常用的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 等外设总线时钟,为后续外设驱动提供时钟支持。
更多推荐



所有评论(0)