STM32 启动文件分析
一、启动文件的核心作用
STM32启动文件(如startup_stm32f10x_hd.s
)是芯片上电后执行的第一段代码,用汇编语言编写,主要完成以下关键任务:
-
初始化堆栈指针(SP)
- 设置主堆栈指针(MSP)和进程堆栈指针(PSP),通过
Stack_Size
宏定义堆栈大小(例如0x00000400
表示1KB)。 - 堆栈地址由
__initial_sp
标号标记,存储在Flash的起始位置(如0x08000000
)。
- 设置主堆栈指针(MSP)和进程堆栈指针(PSP),通过
-
设置中断向量表
- 向量表是中断服务程序入口地址的集合,首地址为
__Vectors
,通过DCD
指令定义每个中断的入口地址。 - 前两项固定为初始堆栈指针和复位中断入口地址(
Reset_Handler
)。
- 向量表是中断服务程序入口地址的集合,首地址为
-
硬件初始化
- 调用
SystemInit()
函数(在system_stm32f10x.c
中)初始化系统时钟、Flash预取和外部存储器(如SRAM)。
- 调用
-
跳转到用户程序
- 通过
__main
函数(C库函数)初始化数据段(.data
)、清零BSS段(.bss
),最终调用用户main()
函数。
- 通过
二、启动文件关键代码段解析
1. 堆栈与堆的配置
Stack_Size EQU 0x00000400 ; 定义栈大小为1KB
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size ; 分配栈空间
__initial_sp ; 栈顶地址(高地址向低地址生长)Heap_Size EQU 0x00000200 ; 定义堆大小为512B
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base ; 堆起始地址
Heap_Mem SPACE Heap_Size ; 分配堆空间
__heap_limit ; 堆结束地址(低地址向高地址生长)
- EQU:宏定义指令,类似于C语言的
#define
。 - AREA:定义内存段属性(如
READWRITE
表示可读写)。 - SPACE:分配指定大小的连续内存空间。
2. 中断向量表
AREA RESET, DATA, READONLY
__VectorsDCD __initial_sp ; 栈顶地址DCD Reset_Handler ; 复位中断入口DCD NMI_Handler ; 不可屏蔽中断DCD HardFault_Handler ; 硬件错误中断... ; 其他中断向量
__Vectors_End
- DCD:每个中断向量占用4字节,存储中断服务函数地址。
- WEAK:允许用户在其他文件中重写默认中断处理函数(如
NMI_Handler
)。
3. 复位中断服务程序
Reset_Handler PROCEXPORT Reset_Handler [WEAK]IMPORT SystemInit ; 引入系统初始化函数IMPORT __main ; 引入C库初始化函数LDR R0, =SystemInit ; 加载SystemInit地址到寄存器BLX R0 ; 调用SystemInitLDR R0, =__main ; 加载__main地址BX R0 ; 跳转到__main
ENDP
- PROC/ENDP:定义子程序边界。
- BLX:带链接的跳转指令,保存返回地址到LR寄存器。
三、启动流程详解(以Flash启动为例)
-
硬件复位
- 从Flash地址
0x08000000
加载初始堆栈指针(MSP)到SP寄存器。 - 从
0x08000004
加载Reset_Handler
地址到PC寄存器,执行复位中断服务程序。
- 从Flash地址
-
软件初始化
- 调用
SystemInit()
配置时钟(如HSE/PLL)、设置中断向量表偏移(通过SCB->VTOR
)。 __main
完成数据段拷贝(从Flash到RAM)、BSS段清零、堆栈初始化。
- 调用
-
用户程序入口
__main
最终跳转到用户main()
函数,进入应用程序逻辑。
四、常见问题与调试技巧
-
堆栈溢出
- 若程序崩溃,检查
Stack_Size
是否过小(默认1KB可能不足),可通过STM32CubeMX调整。
- 若程序崩溃,检查
-
中断服务函数未定义
- 启动文件中默认中断处理函数为死循环(
B .
),需在C文件中实现同名函数(如void USART1_IRQHandler(void)
)。
- 启动文件中默认中断处理函数为死循环(
-
向量表地址错误
- 若使用Bootloader,需通过
SCB->VTOR = APP_ADDRESS
重定位向量表。
- 若使用Bootloader,需通过
五、启动文件选择与移植
- 不同型号对应不同文件:
文件后缀 适用芯片 说明 hd.s
高密度(如F103ZE) Flash ≥ 512KB md.s
中密度(如F103C8) Flash 64-128KB ld.s
低密度(如F103R6) Flash ≤ 32KB - 移植时需修改:堆栈大小、时钟配置(
SystemInit()
)、中断向量表。
六、进阶学习建议
- 阅读链接脚本(.ld/.sct):理解代码段(
.text
)、数据段(.data
)的内存布局。 - 调试启动过程:使用JTAG/SWD单步跟踪
Reset_Handler
,观察寄存器变化。 - 研究C库初始化:了解
__main
如何初始化C运行时环境(如_sbrk
函数实现堆管理)。
通过以上分析,你可以逐步掌握STM32启动机制的核心原理,为后续开发打下坚实基础。