STM32 启动文件详解:理解单片机启动的“引导者”
一、什么是 STM32 的启动文件?
简单说,启动文件就是 STM32 单片机上电后执行的第一段代码。它通常是汇编写的 .s
文件,作用如下:
✅ 设置栈顶(给程序运行分配临时空间)
✅ 初始化中断向量表(告诉 CPU 各类中断的处理函数地址)
✅ 处理全局变量的初始化(.data 和 .bss 段)
✅ 最终跳转到你的 main()
函数,开始运行主程序!
📌 大白话解释:STM32 上电后就像“刚醒的电脑”,启动文件就是它的 BIOS,负责开机、加载环境、把控制权交给你写的代码。
二、启动文件在哪?叫什么名字?
STM32 启动文件通常叫:
startup_stm32f103xb.s // 对应 STM32F1 系列
startup_stm32h743xx.s // 对应 STM32H7 系列
你可以在 STM32 工程中的 startup/
文件夹或 Core/Startup
下找到它。
📌 文件名规律:
startup_ + 芯片型号 + .s
三、启动文件的核心结构解析(带注释+大白话)
下面我们以 STM32F1 系列为例,一步步讲清启动文件里的关键内容。
1⃣ 向量表定义(中断函数地址表)
.section .isr_vector,"a",%progbits
g_pfnVectors:
.word _estack // 栈顶地址
.word Reset_Handler // 复位中断
.word NMI_Handler // 非屏蔽中断
.word HardFault_Handler // 硬错误中断
...
📌 大白话:这是 MCU 上电后看的第一张“导航地图”,告诉它:
-
栈空间在哪里?(_estack)
-
出问题后找谁处理?(各类中断函数)
-
启动后从哪儿开始?(Reset_Handler)
2⃣ 默认中断处理函数(未定义时的兜底方案)
.weak NMI_Handler
.thumb_set NMI_Handler, Default_Handler
📌 大白话:就像备用钥匙一样,如果你没写 NMI_Handler()
函数,就默认跳到 Default_Handler
,进入死循环防止程序乱跑。
3⃣ 复位处理函数 Reset_Handler(程序的真正起点)
Reset_Handler:
LDR R0, =_estack
MOV SP, R0 // 设置栈顶
// 初始化 .data 段(Flash → RAM)
LDR R0, =_sdata
LDR R1, =_edata
LDR R2, =_sidata
CopyData:
CMP R0, R1
LDRLT R3, [R2], #4
STRLT R3, [R0], #4
BLT CopyData
// 清零 .bss 段(RAM)
LDR R0, =_sbss
LDR R1, =_ebss
MOV R2, #0
ZeroBss:
CMP R0, R1
STRLT R2, [R0], #4
BLT ZeroBss
BL SystemInit // 初始化系统时钟
BL __libc_init_array // 初始化 C/C++ 环境
BL main // 跳转到主函数
📌 大白话总结:
-
🥄 装菜之前先摆盘子:复制 .data 段、清空 .bss 段
-
⚙️ 开电前先搭电网:SystemInit 设置时钟
-
🚪 进门后开始正事:跳转到你写的
main()
函数!
四、启动文件和链接脚本(.ld 文件)密不可分!
你在启动文件里看到的这些符号:
_estack, _sdata, _edata, _sidata, _sbss, _ebss
实际上是 链接脚本 .ld
文件定义的地址,比如:
_estack = 0x20005000;
_sdata = 0x20000000;
_edata = 0x20001000;
_sidata = 0x08004000;
🧠 启动文件 ≈ 动作执行者
📜 链接脚本 ≈ 指明地址的地图设计者
五、如何在 C 文件中使用或重写这些中断?
只需写对应名字的函数,就能自动覆盖默认的弱定义中断。
void USART1_IRQHandler(void) {
// 自定义串口中断处理
}
🧩 C 文件自动“接管”中断处理,只要函数名匹配即可。
六、启动文件常见问题排查小贴士
问题类型 | 可能原因 | 排查建议 |
---|---|---|
卡死在 Reset_Handler | 栈地址错误、中断表错 | 检查 |
main() 不执行 | .data / .bss 段未初始化 | 检查 CopyData/ZeroBss 代码段 |
中断不响应 | 函数未定义或未启用 | 检查是否重定义函数且已使能 |
编译器报错找不到符号 | 启动文件用了链接脚本没定义的地址 | 对齐链接脚本与启动文件符号 |
七、建议小白这样学启动文件👇
-
用调试器打断点在
Reset_Handler
、main()
入口 -
动手写最小裸机程序:只保留启动文件 + 一个
main()
-
对照 map 文件,理解 .data/.bss 的内存地址和大小
-
试着添加一个中断处理函数,观察是否自动生效