启动文件:0x08000000第一个字是栈顶,第二个字是复位入口
短文标题:启动文件:0x08000000第一个字是栈顶,第二个字是复位入口
你有没有想过一个问题:单片机通电后,执行的第一个指令在哪?为什么程序从main开始跑?不是main,甚至不是Reset_Handler。上电后,硬件自动做两件事:先取栈顶地址,再取复位向量。没有栈,C语言无法运行——所以硬件第一步是设栈,第二步才取第一条指令。上电后硬件自动完成的两件事
- 从地址0x08000000读取栈顶地址(__initial_sp)→ 写入MSP(主栈指针)
- 从地址0x08000004读取复位向量(Reset_Handler地址)→ 跳转执行
这两个地址固定,由启动文件定义,不可更改。中断向量表的结构(存放在Flash起始)
栈顶地址错误会怎样?
- 指向ROM(Flash)→ 压栈操作覆盖代码区 → 立即HardFault
- 超出RAM范围 → 压栈到未映射地址 → BusFault → HardFault
- 未对齐(奇数地址)→ 总线错误
启动文件写错,单片机连启动都启动不了。
Reset_Handler做了什么?
Reset_Handler:
LDR R0, =SystemInit
BLX R0 ; ① 配置时钟
LDR R0, =__main
BX R0 ; ② C库入口(初始化data/bss,调用main)
SystemInit:配置Flash等待周期、设置PLL、切换系统时钟。
__main(不是main):初始化已初始化的全局变量(.data段)→ 清零未初始化的全局变量(.bss段)→ 最后调用main。
VTOR重定位(Bootloader→APP),通过SCB->VTOR寄存器可将向量表重定位到RAM或Flash其他地址:
SCB->VTOR = 0x08004000; // APP从偏移0x4000开始
Bootloader跳转APP前必须重设VTOR,否则APP的中断向量仍指向Bootloader区。
这个故事的启示,main不是CPU执行的第一个函数。栈顶地址和复位向量,在main之前就已经被硬件加载。启动文件写错,main永远跑不起来。写在最后,调试“上电没反应”,最先检查中断向量表:查看0x08000000:第一个字是不是RAM地址?第二个字是不是Reset_Handler地址?
这两个地址对了,单片机才有可能“活”过来。
(本文灵感源于于振南《新概念ARM32单片机》教程第5.5节“ARM32启动流程与中断控制器配置”。)觉得有用?点赞、转发,让更多人看懂启动文件和向量表的底层逻辑。
