STM32 Bootloader 实现指南:基于IAP的在线升级方案
👁 8 阅读 · 2026-08-15 · STM32 Boot
本文详细讲解STM32 Bootloader的IAP在线升级原理与实现。从内存分区、中断向量表重映射到Bootloader代码编写,提供完整示例,并给出关键配置步骤与常见注意事项,帮助嵌入式开发者快速实现基于串口的固件升级方案。
## 什么是IAP?
IAP(In-Application Programming)是一种在应用内编程的技术,允许设备在运行主程序时,通过通信接口(如UART、USB、CAN)接收新固件并写入Flash,实现现场升级。与ISP相比,IAP不需要外部编程器,只需在启动时运行一段Bootloader引导程序。
STM32的IAP流程通常是:设备上电后先运行Bootloader(位于Flash起始地址),它检测是否有升级请求,如果有则接收固件数据并写入指定Flash区域;如果没有则直接跳转到用户应用程序(APP)。这样,系统的升级灵活性和可维护性大大增强。
## 内存分区设计
以STM32F103系列为例,其Flash起始地址为`0x08000000`。我们规划Bootloader占用前16KB(地址`0x08000000`~`0x08003FFF`),APP放在`0x08004000`起始。也可根据实际需求调整,但需注意扇区大小对齐。
## 中断向量表重映射
APP必须将中断向量表偏移到新地址,否则中断无法响应。对于Cortex-M3/M4,设置`SCB->VTOR`寄存器(例如STM32F1/F4),或者使用`NVIC_SetVectorTable`函数(STM32标准库)。
在APP工程中的`system_stm32f10x.c`或`main.c`早期调用:
```c
SCB->VTOR = 0x08004000;
```
同时,修改APP工程的链接脚本(.ld或散列文件),将FLASH起始地址改为`0x08004000`,并保留适当大小。
## Bootloader代码实现
Bootloader的主要任务:
1. 初始化系统时钟、串口等外设。
2. 检测是否进入升级模式(按键、串口命令等)。
3. 接收固件包,写入Flash。
4. 校验无误后跳转到APP。
### 接收协议
这里使用简单的帧格式:`[0xAA][0x55][长度][数据][CRC8]`。每包数据最大256字节。Bootloader根据帧头识别数据包,接收完成后再进行擦写。
### Flash写入操作
以STM32F1为例,写入前必须擦除扇区(注意:标准库中F1没有扇区概念,页面大小为1KB,擦除按页)。使用`FLASH_ProgramHalfWord`写入半字。
### 跳转函数实现
跳转的核心代码:
```c
typedef void (*pFunction)(void);
void JumpToApp(uint32_t app_addr)
{
uint32_t app_jump_addr = *(volatile uint32_t*)(app_addr + 4);
pFunction jumpF = (pFunction)app_jump_addr;
__disable_irq();
SCB->VTOR = app_addr; // 重设向量表
__set_MSP(*(volatile uint32_t*)app_addr); // 设置栈指针
jumpF(); // 跳转
}
```
## 完整代码示例
以下是一个简化的Bootloader核心代码,演示串口接收和升级过程。
```c
#include "stm32f10x.h"
// 假设串口1接收中断
void Flash_WriteBuf(uint32_t addr, uint8_t *buf, uint16_t len)
{
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
// 擦除页
FLASH_ErasePage(addr);
for (int i = 0; i < len; i += 2) {
uint16_t halfword = buf[i] | (buf[i+1] << 8);
FLASH_ProgramHalfWord(addr + i, halfword);
}
FLASH_Lock();
}
int main(void)
{
SystemInit();
UART_Init();
if (CheckUpgradeCommand()) { // 判断是否进入IAP
uint8_t buf[256];
uint32_t write_addr = APP_ADDR;
while (1) {
// 等待接收包,收到一帧后处理
uint16_t len = UART_ReceivePacket(buf);
if (len > 0) {
Flash_WriteBuf(write_addr, buf, len);
write_addr += len;
// 发送ACK
}
if (LastPacket) break;
}
// 校验完整固件(可选)
}
JumpToApp(APP_ADDR);
}
```
说明:实际项目中需要加入超时、重传、CRC校验等机制,此处仅为演示。
## 配置步骤
- 1. **Bootloader工程**:设置Flash起始为`0x08000000`,大小`16K`。编译生成hex/bin。
- 2. **APP工程**:设置起始地址为`0x08004000`,并在代码开头设置`SCB->VTOR`。编译生成bin文件(如`fromelf --bin --output=app.bin`)。
- 3. **烧录Bootloader**:用ST-Link/J-Link烧录到Flash。
- 4. **执行升级**:通过串口发送APP的bin文件数据,Bootloader接收并写入对应内存后跳转。
## 注意事项
- **向量表偏移**:必须在APP代码最早期设置,且地址需对齐到`0x100`(某些系列)。
- **硬件设计**:确保Bootloader和APP共用同一个时钟树,注意外设冲突。
- **串口升级协议**:一定要有校验和应答,防止传输错误导致变砖。
- **看门狗**:如果开启看门狗,在Bootloader中记得及时喂狗。
- **Flash擦写时序**:写入时不能有其他中断打扰,必要时关闭全局中断。
通过IAP,STM32设备可以实现远程升级,大幅降低维护成本。希望本文能帮助你快速搭建属于自己的Bootloader。