一、为什么要理解启动过程?
STM32 的启动过程就像一台精密仪器的开机自检,它确保所有系统部件按既定方式初始化,才能顺利运行我们的应用代码。对初学者而言,理解启动过程能帮助解决常见"程序跑飞""不进 main""下载后无反应"等问题;而对资深开发者,则有助于进行裸机编程、启动优化、Bootloader 设计等工作。
二、STM32 启动流程概览
STM32 的启动过程大致可以划分为以下几个阶段:
上电 / 复位 ↓启动模式选择(BOOT 引脚) ↓向量表定位(读取复位向量) ↓跳转至启动代码(如 startup_stm32f10x.s) ↓C/C++ 运行时初始化(.data/.bss/init) ↓调用 main 函数

三、详细流程剖析
1. 上电与复位阶段
STM32 上电后,首先进入 复位状态,这是一种硬件初始化状态,保证芯片的稳定性。复位的方式包括:
-
上电复位
-
手动按下 NRST 引脚
-
软件复位(通过 SCB->AIRCR)
接下来,芯片会读取一个叫做 启动引脚(BOOT0 和 BOOT1) 的组合,用于选择芯片从哪个存储介质启动。
2. 启动模式选择
STM32 支持三种启动模式(以 STM32F103 为例):
BOOT1 | BOOT0 | 启动模式 |
---|---|---|
X | 0 | 从主 Flash 启动 |
0 | 1 | 从系统内置 Bootloader 启动 |
1 | 1 | 从 SRAM 启动 |
大多数应用情况下,设置为 主 Flash 启动(BOOT0 = 0),也就是我们的应用程序所在位置。
✅ Tips:学习 Bootloader 时,可以手动将 BOOT0 设置为 1,进入 ISP 模式。
3. 读取向量表 & 跳转
无论从哪启动,STM32 都会从特定地址读取 向量表。向量表是中断和异常的入口地址表,前两个条目尤为关键:
-
地址 0x0000 0000(或其他起始地址):初始主堆栈指针(MSP)
-
地址 0x0000 0004:复位中断向量(即程序入口地址)
芯片读取这两个值,并进行如下动作:
MSP ← *(0x00000000)PC ← *(0x00000004)
STM32 会自动把 MSP 设置为初值,然后跳转到复位处理函数,通常是
Reset_Handler()
。
4. 执行启动文件 startup_xxx.s
Reset_Handler
并不是直接跳到 main()
,而是执行一个启动汇编文件,如 startup_stm32f10x.s
。它的职责包括:
-
初始化堆栈指针
-
初始化中断向量表
-
调用
SystemInit()
(时钟、Flash 等初始化) -
初始化数据段(
.data
)和清零 BSS 段(.bss
) -
最后调用
main()
简化伪代码如下:
Reset_Handler: bl SystemInit ; 初始化系统时钟 bl __libc_init_array ; C 运行时库初始化 bl main ; 跳转到 main 函数
四、C/C++ 运行时初始化详解
在调用 main()
之前,C/C++ 编译器需要做一些关键准备:
✅ .data 段初始化
.data
段是已初始化的全局变量,例如:
int a = 5; // 存储在 .data
这部分变量的值最初存储在 Flash,需要搬运到 RAM 才能读写。启动文件会将其从 ROM 拷贝到 RAM。
✅ .bss 段清零
.bss
段用于存储未初始化的全局变量,例如:
int b; // 存储在 .bss,自动清零
启动代码会将 .bss
区域置为 0。
✅ C++ 构造函数支持
如果使用 C++,启动代码还会调用全局构造函数,这是通过 __libc_init_array()
实现的。
五、然后才是我们的 main()
函数
至此,所有底层初始化完成,CPU 跳转到 C 程序的入口 main()
函数,正式交给开发者写业务逻辑。
int main(void){ // 用户应用代码 while (1) { // 主循环 }}
六、动手实践建议(面向初学者)
如果你是 STM32 小白,不妨通过以下步骤深入理解:
-
查看启动汇编文件 :打开
startup_xxx.s
文件,理解Reset_Handler
的流程。 -
在 SystemInit() 加断点:跟踪是否进入主时钟配置流程。
-
使用
map
文件分析段分布 :看.data``.bss``.text
的地址和大小。 -
学习如何修改向量表位置 :通过
SCB->VTOR
改变中断表地址。 -
尝试裸机启动:不依赖 HAL 库,手动初始化堆栈、中断、系统时钟。
七、总结
STM32 的启动过程虽然隐藏在 HAL 库和 IDE 的自动生成之下,但正是这一套流程,支撑了嵌入式程序的稳定运行。掌握它,不仅有助于开发排错、理解底层逻辑,更是从"会用"到"精通" STM32 的必经之路。