# STM32双Bank切换实现OTA无感升级的坑与规避 ## 为什么需要双Bank? 传统OTA升级通常采用单Bank方案:Bootloader接收固件,写入App区,然后跳转。但这种方式存在致命缺陷——升级过程中断电或写入错误,设备将无法启动,只能依赖外部恢复。 双Bank方案将Flash划分为两个独立的Bank(如Bank0和Bank1),App运行在其中一个Bank,升级时写入另一个Bank,完成后通过切换启动地址实现无缝切换。这样即使升级失败,旧固件仍可运行,实现真正的无感升级。 ## STM32双Bank的硬件支持 并非所有STM32都支持双Bank。以STM32F7、H7系列为例,其Flash支持双Bank模式,每个Bank可独立擦写。关键寄存器为`FLASH_OPTCR`中的`DBANK`位,用于配置双Bank模式。 **注意**:双Bank模式必须在Flash空载时配置,且配置后Flash地址映射会改变。例如,STM32H743在双Bank模式下,Bank0地址为0x08000000~0x080FFFFF,Bank1地址为0x08100000~0x081FFFFF(若总容量2MB)。 ## 核心原理:启动地址切换 STM32的启动地址由`BOOT0`引脚和`FLASH_OPTCR`的`BOR_LEV`等决定,但双Bank切换通常通过修改`FLASH_BOOT`位(在`FLASH_OPTCR`中)实现。当`FLASH_BOOT=0`时,从Bank0启动;`FLASH_BOOT=1`时,从Bank1启动。 **关键点**:切换后,CPU的PC指针会跳转到新Bank的起始地址,但中断向量表(VTOR)仍指向旧Bank,必须手动重映射。 ## 配置步骤 1. **使能双Bank模式**:在系统初始化时,设置`FLASH_OPTCR`的`DBANK=1`,并执行`FLASH_OPTCR_LOCK`解锁。 2. **规划内存布局**:将Bank0和Bank1分别作为App区,Bootloader可放在独立区域(如系统Flash)。 3. **编译链接脚本**:为每个Bank生成独立的固件,链接脚本中设置`FLASH`起始地址为对应Bank地址。 4. **实现跳转函数**:在Bootloader或App中,通过修改`FLASH_BOOT`位并软复位实现切换。 ## 完整代码示例 以下代码演示了从App(Bank0)切换到Bank1的流程: ```c #include "stm32h7xx_hal.h" #define BANK0_START_ADDR 0x08000000 #define BANK1_START_ADDR 0x08100000 void JumpToBank1(void) { // 1. 关闭全局中断 __disable_irq(); // 2. 设置FLASH_BOOT位为1,使能从Bank1启动 FLASH_OBProgramInitTypeDef pOBInit; HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); pOBInit.OptionType = OPTIONBYTE_USER; pOBInit.UserType = OB_USER_BOOT_ADD0; pOBInit.BOOT_ADD0 = BANK1_START_ADDR; // 设置启动地址为Bank1 HAL_FLASH_OB_Program(&pOBInit); // 3. 触发系统复位 HAL_FLASH_OB_Lock(); HAL_FLASH_Lock(); NVIC_SystemReset(); } // 在App中调用此函数,即可切换到Bank1 ``` **注意**:跳转前必须确保新Bank的固件已完整写入并校验通过。 ## 常见坑与规避 ### 坑1:中断向量表未重映射 **现象**:跳转后程序跑飞或死机。 **原因**:CPU复位后,VTOR仍指向旧Bank的向量表,导致中断处理错误。 **规避**:在App的启动代码中,立即设置VTOR为新Bank的地址。例如,在`SystemInit`函数中添加: ```c #define APP_ADDR 0x08100000 SCB->VTOR = APP_ADDR; ``` ### 坑2:Flash擦写期间中断干扰 **现象**:升级过程中,中断触发导致Flash操作失败。 **原因**:Flash擦写时,若CPU执行中断服务程序,可能访问Flash造成冲突。 **规避**:在擦写Flash期间,关闭所有可屏蔽中断,或使用`HAL_FLASH_Program`时确保中断优先级低于Flash操作。 ### 坑3:双Bank模式配置后地址映射错误 **现象**:代码无法运行,调试器连接不上。 **原因**:配置`DBANK=1`后,Flash地址重新映射,但链接脚本未更新。 **规避**:在配置双Bank前,务必确认链接脚本中的`FLASH`起始地址与Bank地址一致。若使用STM32CubeIDE,可在`*.ld`文件中修改。 ### 坑4:跳转后外设状态残留 **现象**:新固件运行异常,如串口乱码。 **原因**:跳转前未复位外设,导致新固件初始化时冲突。 **规避**:在跳转前,调用`HAL_RCC_DeInit()`复位时钟,并重新初始化必要外设。 ### 坑5:升级失败后无回退机制 **现象**:新固件损坏,设备无法启动。 **原因**:未实现回退逻辑。 **规避**:在Bootloader中增加启动计数,若新固件连续启动失败(如看门狗复位),则自动回退到旧Bank。 ## 总结 双Bank切换是OTA无感升级的基石,但细节决定成败。掌握Flash地址映射、VTOR重映射、中断管理和回退机制,才能避开常见陷阱。建议在实际项目中,先在开发板上验证完整流程,再部署到生产环境。