STM32 Bootloader 与启动模式详解

三种启动模式

F407 通过BOOT0BOOT1两个引脚选择启动方式:

BOOT0BOOT1启动位置说明
0任意Flash(0x08000000)正常启动,运行你的程序
10System Memory(0x1FFF0000)出厂 Bootloader,串口 ISP 下载
11SRAM(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
USART2PD5/PD6
USB DFUPA11/PA12
CAN2PB5(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 后死机了?

  1. App 的向量表偏移设了吗?SCB->VTOR = APP_ADDR

  2. App 的链接脚本改了 Flash 起始地址吗?

  3. 跳转前关中断了吗?

  4. App 的 SRAM 起始地址和 Bootloader 冲突了吗?

最简单验证方法:先不写 Bootloader,直接把 App 烧到 0x08000000 测试能不能跑。能跑说明 App 本身没问题,问题在跳转上。