前言在单片机如 STM32的开发中大部分人的认知是代码是从0x08000000这个 Flash 首地址开始执行的。但如果我们要实现无线升级单片机里就必须同时装下两套程序一套是专门负责接收新代码并写进 Flash 的“快递员”Bootloader另一套才是你真正跑业务的小车代码APP。如何让这两个程序在一块芯片里和平共处并实现灵魂的切换核心就在以下三个步骤。一、 Flash 内存的“楚河汉界”通常STM32 的 Flash 起始地址是0x08000000。 我们需要在 Keil (或 CubeIDE) 的魔术棒设置中人为地把 Flash 劈成两半Bootloader 领地分配前 16KB0x08000000到0x08003FFF。单片机上电后永远先运行这里的代码。它的任务是检查无线串口有没有发来新的.bin文件如果有就把新文件写到后半段 Flash 中如果没有就准备交出控制权。APP 领地分配从0x08004000开始的剩余空间。你的小车控制代码全部存放在这里。二、 灵魂跳跃利用函数指针“移花接木”当 Bootloader 决定要把控制权交给 APP 时绝不是简单地写一句goto就能搞定的。你需要通过 C 语言的函数指针强行把 PC程序计数器指针指到 APP 的起始地址。核心跳转代码极度硬核#define FLASH_APP_ADDR 0x08004000 // APP 起始地址 typedef void (*pFunction)(void); // 定义一个函数指针类型 pFunction JumpToApplication; // 声明一个函数指针变量 uint32_t JumpAddress; // 1. 检查 APP 地址的栈顶指针是否合法 (通常 RAM 起始于 0x20000000) if (((*(__IO uint32_t*)FLASH_APP_ADDR) 0x2FFE0000 ) 0x20000000) { // 2. 取出 APP 的复位中断服务函数的地址 (APP 地址 4 个字节) JumpAddress *(__IO uint32_t*) (FLASH_APP_ADDR 4); JumpToApplication (pFunction) JumpAddress; // 3. 初始化主堆栈指针 MSP __set_MSP(*(__IO uint32_t*) FLASH_APP_ADDR); // 4. 信仰一跃执行跳转 JumpToApplication(); }#define FLASH_APP_ADDR 0x08004000 // APP 起始地址 typedef void (*pFunction)(void); // 定义一个函数指针类型 pFunction JumpToApplication; // 声明一个函数指针变量 uint32_t JumpAddress; // 1. 检查 APP 地址的栈顶指针是否合法 (通常 RAM 起始于 0x20000000) if (((*(__IO uint32_t*)FLASH_APP_ADDR) 0x2FFE0000 ) 0x20000000) { // 2. 取出 APP 的复位中断服务函数的地址 (APP 地址 4 个字节) JumpAddress *(__IO uint32_t*) (FLASH_APP_ADDR 4); JumpToApplication (pFunction) JumpAddress; // 3. 初始化主堆栈指针 MSP __set_MSP(*(__IO uint32_t*) FLASH_APP_ADDR); // 4. 信仰一跃执行跳转 JumpToApplication(); }三、 致命深坑中断向量表的重映射SCB-VTOR很多同学写完了跳转代码发现程序能成功跳到 APP 里亮起了一个 LED 灯。但只要一发生任何中断比如滴答定时器中断、串口中断单片机直接死机HardFault这是为什么因为单片机默认的中断向量表IVT固定在0x08000000。 当 APP 里发生中断时CPU 会傻乎乎地跑回到0x08000000附近去找中断服务函数。但那里是 Bootloader 的地盘Bootloader 的中断逻辑和 APP 完全不一样CPU 瞬间就迷失了。绝杀技VTOR 寄存器在你的 APP 代码的main函数开头甚至在SystemInit里必须、务必、一定要加上这一句SCB-VTOR FLASH_APP_ADDR;这句话的意思是告诉 CPU“从现在起如果发生中断不要去0x08000000找了来我0x08004000这里找” 加上这句你的 OTA 升级才算真正大功告成。四、 总结手撕 Bootloader 是一次对计算机底层架构PC指针、堆栈指针、内存映射、中断向量的深度洗礼。当你能看着进度条走完小车自动重启并执行了新代码时你就已经脱离了“调包侠”的身份正式迈入了嵌入式系统架构师的大门。今日互动你在做 IAP 升级或者玩 Bootloader 的时候把单片机变“砖”过几次是被跳跃地址搞晕了还是被中断向量表坑了欢迎在评论区分享你的踩坑日记