## 什么是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。