# STM32 双Bank切换实现OTA零停机:基于F4/F7的Flash重映射实战 ## 1. 为什么需要双Bank? 传统OTA升级流程:擦除旧固件→写入新固件→跳转重启。这个过程通常需要数秒甚至更久,期间设备无法响应外部请求,造成服务中断。对于工业控制、医疗设备或IoT网关,这种停机可能带来严重后果。 STM32 F4/F7系列(如STM32F427、STM32F746)提供了双Bank Flash架构,将Flash分为两个独立的Bank(如Bank1和Bank2),每个Bank可独立擦写。升级时,CPU从Bank1运行,同时擦写Bank2并写入新固件;完成后,通过修改选项字节或触发硬件重映射,CPU在下次复位后直接从Bank2启动,整个过程无需等待Flash擦写,实现零停机。 ## 2. 双Bank原理与重映射机制 ### 2.1 Flash组织 以STM32F746为例,其Flash大小为1MB,分为两个Bank: - Bank1:地址0x08000000 - 0x0807FFFF(512KB) - Bank2:地址0x08080000 - 0x080FFFFF(512KB) 每个Bank内部又分为多个扇区(Sector),擦除操作以扇区为单位。 ### 2.2 重映射机制 双Bank切换的核心是`FLASH_OPTCR`寄存器中的`SWAP_BANK`位。当该位设置为1时,硬件将Bank1和Bank2的地址空间互换: - 原Bank2的地址变为0x08000000,CPU从该地址启动 - 原Bank1的地址变为0x08080000 这种映射是硬件级的,无需修改向量表或链接脚本,切换后中断向量自动指向新Bank。 ### 2.3 切换流程 1. 在Bank1中运行当前固件 2. 擦除Bank2,写入新固件(通过IAP或外部通信) 3. 设置`SWAP_BANK`位,并触发系统复位 4. 复位后,CPU从Bank2启动,完成切换 注意:切换操作需要解锁Flash选项字节,并且复位后`SWAP_BANK`位保持有效,直到再次修改。 ## 3. 配置步骤 ### 3.1 硬件要求 - 支持双Bank的STM32系列(如F4xx、F7xx) - 外部通信接口(UART、USB、以太网等)用于接收固件 ### 3.2 软件配置 1. **使能Flash预取和缓存**:确保Flash读取性能 2. **解锁Flash**:允许擦写操作 3. **擦除目标Bank**:擦除Bank2的所有扇区 4. **写入新固件**:将接收到的数据写入Bank2对应地址 5. **设置SWAP_BANK位**:通过修改选项字节 6. **复位系统**:触发重映射 ## 4. 完整代码示例 以下代码基于STM32F746 + HAL库,演示从Bank1切换到Bank2的过程。 ```c #include "stm32f7xx_hal.h" /* 定义Bank2起始地址和大小 */ #define BANK2_START_ADDR 0x08080000 #define BANK2_SIZE 512 * 1024 /* 函数:擦除Bank2 */ void Flash_EraseBank2(void) { FLASH_EraseInitTypeDef eraseInit; uint32_t sectorError = 0; eraseInit.TypeErase = FLASH_TYPEERASE_SECTORS; eraseInit.Banks = FLASH_BANK_2; eraseInit.Sector = 0; eraseInit.NbSectors = 8; // 根据实际扇区数调整 eraseInit.VoltageRange = FLASH_VOLTAGE_RANGE_3; if (HAL_FLASHEx_Erase(&eraseInit, §orError) != HAL_OK) { // 错误处理 Error_Handler(); } } /* 函数:写入数据到Bank2 */ void Flash_WriteBank2(uint32_t offset, uint32_t *data, uint32_t len) { uint32_t addr = BANK2_START_ADDR + offset; for (uint32_t i = 0; i < len; i++) { if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i*4, data[i]) != HAL_OK) { Error_Handler(); } } } /* 函数:切换Bank并复位 */ void Flash_SwapBankAndReset(void) { HAL_FLASH_Unlock(); // 设置SWAP_BANK位 FLASH_OBProgramInitTypeDef obInit; obInit.OptionType = OPTIONBYTE_USER; obInit.UserType = OB_USER_SWAP_BANK; obInit.UserConfig = OB_SWAP_BANK_ENABLE; HAL_FLASHEx_OBProgram(&obInit); // 触发系统复位 HAL_NVIC_SystemReset(); } /* 主函数示例 */ int main(void) { HAL_Init(); // 解锁Flash HAL_FLASH_Unlock(); // 擦除Bank2 Flash_EraseBank2(); // 假设通过UART接收固件数据,写入Bank2 uint32_t fw_data[1024]; // 接收数据... Flash_WriteBank2(0, fw_data, 1024); // 切换Bank并复位 Flash_SwapBankAndReset(); while(1); } ``` ## 5. 注意事项 - **扇区大小**:不同型号的扇区大小不同,擦除前需确认目标Bank的扇区布局。 - **写入对齐**:HAL的`HAL_FLASH_Program`要求数据为32位对齐,且地址必须按字对齐。 - **中断处理**:在擦写Flash期间,CPU会暂停执行,但双Bank模式下,从Bank1运行不影响Bank2擦写,但需确保中断向量表在Bank1。 - **固件校验**:写入完成后,建议进行CRC或SHA校验,避免损坏固件导致启动失败。 - **回滚机制**:如果新固件启动失败,应提供回滚到旧Bank的机制(如通过Bootloader检测)。 - **选项字节保护**:设置`SWAP_BANK`位后,需重新上电或复位才生效,且该位会保持,直到再次修改。 ## 6. 总结 双Bank切换技术充分利用了STM32的硬件特性,将OTA升级的停机时间降至零。通过合理设计,可以实现无缝升级,提升系统可用性。本文提供的代码和步骤可作为基础,实际项目中还需结合通信协议、固件校验和回滚策略,构建健壮的OTA系统。