什么是 Bootloader?怎么把它移植到 STM32 上?
一、Bootloader 是啥?它都干了些啥?
想象一下你的 MCU(比如 STM32)是一个小机器人,上电之后第一件事,它不会立马开始“干正事”(运行你的主程序),而是先去运行一个“开场引导程序”——这就是 Bootloader。
它主要负责下面这些事:
- 启动最基础的硬件:比如时钟、串口、Flash 这些,像是“穿衣洗脸”。
- 判断要不要升级程序:比如你按了个按钮、发了个串口命令,它就不跳到主程序,而是“停在门口”,准备接收新程序。
- 接收并写入新程序:它能从串口、USB、CAN、SD 卡等接收固件,然后擦掉旧程序,把新程序烧进去。
- 最后跳转到主程序:一切准备就绪,它就把控制权交给你写的主程序,让它正式运行。
通俗讲,它就像是**“程序界的门房”**,开门、分发信件(固件)、开灯、安排进屋(跳转),全由它负责。
二、Bootloader 跟主程序的位置是怎么安排的?
在 STM32 中,Flash 是按地址划分的。一般我们这样安排:
Bootloader 区域: 0x08000000 ~ 0x08003FFF
主程序区域(App): 0x08004000 ~ Flash 结尾
也就是说,Bootloader 占前面 16KB 的空间(你可以设更多),主程序从后面接着跑。它们不会互相干扰。
Bootloader 的典型流程就像这样:
int main(void)
{HAL_Init(); // 初始化硬件SystemClock_Config(); // 配置时钟MX_USART2_UART_Init(); // 初始化串口if (check_upgrade_flag()) // 判断是否需要升级{enter_upgrade_mode(); // 进入升级模式}jump_to_application(); // 跳到主程序区运行
}
三、Bootloader 做事情的重点是啥?
1. 固件怎么接收?
它可以从很多“入口”接收程序,比如:
- 串口(UART)
- USB(DFU 模式)
- CAN、I2C、SPI
- SD 卡、外部 Flash
接收过程里会做这些事:
- 检查程序是不是完整(比如校验 CRC)
- 擦掉旧程序(把 App 区的 Flash 擦了)
- 把新程序一点点写进去
- 写完了,打个勾:“升级完成了!”
2. 怎么跳转到主程序?
最关键的是要设置好跳转地址和栈指针。示例代码如下:
#define APP_ADDR 0x08004000 // 主程序起始地址typedef void (*pFunction)(void);
pFunction JumpToApplication;
uint32_t JumpAddress;HAL_DeInit(); // 关闭外设
__disable_irq(); // 禁用中断JumpAddress = *(__IO uint32_t*)(APP_ADDR + 4);
JumpToApplication = (pFunction)JumpAddress;__set_MSP(*(__IO uint32_t*)APP_ADDR); // 设置栈
JumpToApplication(); // 跳转!
要注意:
0x08004000
第一个 4 字节是主程序的栈指针(MSP)- 第二个 4 字节是主程序的启动地址(Reset_Handler)
- 跳转之前要把中断关掉、外设关掉,不然主程序容易“误会”你还没交接完
四、怎么把 Bootloader 移植到 STM32 上?
如果你自己写 Bootloader,要让它配合你的主程序跑起来,大概分这几步:
Step 1:规划 Flash 分布
比如我们规划:
- Bootloader 占
0x08000000 ~ 0x08003FFF
(16KB) - 主程序从
0x08004000
开始
这样你就得去你的 App 工程里修改启动地址,不然你主程序会被烧到 Bootloader 区,冲突了。
Step 2:写 Bootloader 的功能
你得实现:
- 初始化串口
- 收数据 + 校验
- 擦除 Flash
- 写入新固件
- 跳转主程序
Step 3:改主程序的配置
- 启动文件
.s
或.ld
文件中,把起始地址改成0x08004000
- 设置中断向量表地址偏移:
SCB->VTOR = 0x08004000;
不改这些,你的主程序可能跳转不了,也收不到中断。
Step 4:验证!
Bootloader 烧进去 → 用它烧主程序 → 看能不能正常跳转、运行
记得保留串口 printf
打印,出问题好排查。
五、移植过程中常见的“坑”和解决方案
问题 | 原因 |
---|---|
跳转失败 | 中断没关 / 地址没设对 / VTOR 没设置 |
Flash 写失败 | 没解锁 / 写保护 / 地址错误 |
主程序不跑 | 启动文件地址没改 / 向量表没偏移 |
烧完程序没反应 | 没跳转、程序错烧、校验失败 |
解决方法:
- 多用串口打印调试信息
- 多测试单步跳转和Flash写入
- 主程序先写一个最小的 Blinky(闪灯)测试跳转是否成功