三种启动模式
F407 通过BOOT0和BOOT1两个引脚选择启动方式:
| BOOT0 | BOOT1 | 启动位置 | 说明 |
|---|---|---|---|
| 0 | 任意 | Flash(0x08000000) | 正常启动,运行你的程序 |
| 1 | 0 | System Memory(0x1FFF0000) | 出厂 Bootloader,串口 ISP 下载 |
| 1 | 1 | SRAM(0x20000000) | 调试用,掉电丢失 |
平时 BOOT0 = 0,程序从 Flash 启动。这是最常用的模式。
启动位置做了什么
芯片读向量表的前两个值:栈顶地址 + PC 指针。所以:
Flash 启动:读 0x08000000 → 取 MSP,读 0x08000004 → 取 PC
System Memory 启动:读 0x1FFF0000 → 取 MSP,读 0x1FFF0004 → 取 PC(进入出厂 Bootloader)
SRAM 启动:读 0x20000000 → 取 MSP,读 0x20000004 → 取 PC
出厂 Bootloader 是什么
STM32 出厂时,在 System Memory 区域固化了一段不可擦除的程序,它支持:
| 接口 | 引脚 |
|---|---|
| USART1(常用) | PA9/PA10 |
| USART2 | PD5/PD6 |
| USB DFU | PA11/PA12 |
| CAN2 | PB5(RX)/ PB6(TX) |
进入条件:BOOT0=1, BOOT1=0 → 上电/复位 → 芯片从 System Memory 启动 → 检测串口信号(必须在特定时间内收到 0x7F 字节)→ 进入 ISP 模式。
这就是为什么做串口 ISP 下载时要复位 + 点击"开始编程"——bootloader 只在启动后短暂几毫秒内监听串口信号。
自己写一个 Bootloader
如果你要实现 IAP(In-Application Programming),就需要自己写一个 bootloader:
Flash 布局: 0x08000000 ─── Bootloader(~16KB 就够) 0x08004000 ─── App(用户程序)
Bootloader 代码(精简版):
#define APP_ADDR 0x08004000 #define APP_STACK *(volatile uint32_t *)APP_ADDR #define APP_RESET *(volatile uint32_t *)(APP_ADDR + 4) void JumpToApp(void) { // 1. 关全局中断 __disable_irq(); // 2. 复位所有外设 HAL_DeInit(); // 3. 关 SysTick SysTick->CTRL = 0; // 4. 设置向量表偏移 SCB->VTOR = APP_ADDR; // 5. 设置栈顶指针 __set_MSP(APP_STACK); // 6. 跳转到 app 的复位向量 void (*app_main)(void) = (void (*)(void))APP_RESET; app_main(); }App 工程需要修改链接脚本,把起始地址改为 0x08004000。修改 APP 起始地址需手动编辑 .ld 链接脚本(CubeMX 的 Project Manager → Linker Settings 仅可查看路径,不能在 GUI 中直接修改 Flash 起始地址)。
在代码中也要设置向量表偏移:
// app 的 main 函数最开头加 SCB->VTOR = 0x08004000;
常见问题
写了 Bootloader,但跳转到 App 后死机了?
App 的向量表偏移设了吗?
SCB->VTOR = APP_ADDRApp 的链接脚本改了 Flash 起始地址吗?
跳转前关中断了吗?
App 的 SRAM 起始地址和 Bootloader 冲突了吗?
最简单验证方法:先不写 Bootloader,直接把 App 烧到 0x08000000 测试能不能跑。能跑说明 App 本身没问题,问题在跳转上。