STM32 上电后,通过 BOOT 引脚配置不同的启动方式,决定 MCU 是从 Flash 启动、从 SRAM 启动,还是进入系统 Bootloader。
启动模式 | BOOT 引脚配置 | 启动地址 | 用途 |
---|---|---|---|
Main Flash | BOOT0 = 0 |
0x0800 0000 |
默认从用户程序启动 |
System Memory | BOOT0 = 1, BOOT1 = 0 |
0x1FFF xxxx |
启动内置 Bootloader,支持串口/I2C |
SRAM | BOOT0 = 1, BOOT1 = 1 |
0x2000 0000 |
用于调试程序,通常用于开发阶段 |
STM32 将 0x0000 0000
视为启动地址,它是对真实存储区域的映射(alias):
BOOT模式 | 0x0000 0000 映射地址 |
实际跳转 |
---|---|---|
Flash 启动 | 0x0800 0000 |
用户程序 |
System Memory 启动 | 0x1FFF F000 等 |
ST Bootloader |
SRAM 启动 | 0x2000 0000 |
RAM 中程序 |
BOOT0 = 1, BOOT1 = 0
if ((SCB->VTOR & 0x2FFE0000) == 0x20000000) {
// SRAM 启动
} else if ((SCB->VTOR & 0x2FFE0000) == 0x1FFF0000) {
// Bootloader 启动
} else {
// Flash 启动
}
STM32 裸机开发启动流程是指在没有操作系统参与的情况下,从上电复位到执行 main()
函数的全过程。这个过程涉及启动汇编文件、系统初始化、C运行时初始化以及用户主函数的调用。
上电/复位
↓
读取向量表地址 (栈顶地址, Reset_Handler)
↓
Reset_Handler
├─> 初始化 .data/.bss 段
├─> 调用 SystemInit()
├─> 调用 __libc_init_array()
└─> main()
startup_stm32xxx.s
启动文件是一个汇编文件,包含两部分:中断向量表定义和 Reset_Handler 实现。
.section .isr_vector,"a",%progbits
.word _estack // 栈顶地址(链接脚本定义)
.word Reset_Handler // 复位中断向量
.word NMI_Handler
.word HardFault_Handler
...
Reset_Handler:
LDR R0, =_sidata // flash 中 .data 初始化数据
LDR R1, =_sdata // RAM 中 .data 起始地址
LDR R2, =_edata // RAM 中 .data 结束地址
Loop_Copy_Data:
CMP R1, R2
ITTT LT
LDRLT R3, [R0], #4
STRLT R3, [R1], #4
BLT Loop_Copy_Data
LDR R1, =_sbss
LDR R2, =_ebss
MOVS R3, #0
Loop_Zero_BSS:
CMP R1, R2
IT LT
STRLT R3, [R1], #4
BLT Loop_Zero_BSS
BL SystemInit // 初始化系统时钟
BL __libc_init_array // C库初始化
BL main // 进入主函数
这些地址符号来自链接脚本(.ld
文件):
符号 | 含义 |
---|---|
_sidata |
.data 初始化数据(Flash) |
_sdata |
.data 段起始地址(RAM) |
_edata |
.data 段结束地址(RAM) |
_sbss |
.bss 段起始地址 |
_ebss |
.bss 段结束地址 |
_estack |
栈顶地址 |
SystemInit()
— 系统时钟初始化定义在 system_stm32xxx.c
,由 ST 官方提供,常见初始化内容:
void SystemInit(void)
{
RCC->CR |= RCC_CR_HSION; // 开启内部高速时钟
RCC->CFGR = 0x00000000; // 清除分频设置
RCC->CR &= ~(RCC_CR_HSEON | RCC_CR_PLLON);
RCC->PLLCFGR = ... // 配置 PLL 参数
FLASH->ACR |= FLASH_ACR_LATENCY_5WS; // 设置 Flash 延时
RCC->CR |= RCC_CR_PLLON; // 开启 PLL
while ((RCC->CR & RCC_CR_PLLRDY) == 0); // 等待锁定
RCC->CFGR |= RCC_CFGR_SW_PLL; // 切换系统时钟为 PLL
}
__libc_init_array()
— 初始化 C/C++ 运行环境这个函数位于标准 C 库中(如 newlib),主要功能:
.preinit_array
函数(用于 C++).init_array
中的构造函数void __libc_init_array(void)
{
for (i = 0; i < __preinit_array_end - __preinit_array_start; i++)
__preinit_array_start[i]();
for (i = 0; i < __init_array_end - __init_array_start; i++)
__init_array_start[i]();
}
如果是纯 C 项目,可以选择不使用它,而直接在 Reset_Handler
中清理 .bss、复制 .data。
main()
函数 — 用户应用程序入口int main(void)
{
init_gpio();
init_uart();
while (1) {
toggle_led();
delay_ms(500);
}
}
main()
是用户写的程序入口,往往会初始化外设,然后进入主循环。
阶段 | 执行内容 |
---|---|
启动模式 | 通过 BOOT 引脚或 option bytes 选择启动源 |
启动汇编 | 设置栈,拷贝 .data,清 .bss |
SystemInit | 配置系统时钟、PLL、Flash |
libc init | C/C++构造函数,全局变量初始化 |
main | 用户应用程序 |