# STM32H7双Bank Flash在线升级失败自动回滚:硬件看门狗联动策略深度解析 ## 一、为什么需要双Bank Flash与看门狗联动? 在嵌入式OTA升级场景中,最令人头疼的问题莫过于升级过程中断电、通信中断或新固件本身存在缺陷,导致设备无法启动。传统单Bank方案通常依赖Bootloader+App分区,但升级过程中若App区被破坏,系统将陷入死循环。 STM32H7系列(如H743、H750)内置双Bank Flash(每个Bank独立,可分别擦写),配合硬件看门狗(IWDG),可实现**原子级固件切换**: - **双Bank特性**:两个Bank可独立执行,支持从任一Bank启动。 - **IWDG特性**:一旦启用,若主程序未及时喂狗,系统将强制复位,且不受软件干扰。 联动策略的核心思想:**升级新固件时,先写入非活动Bank,校验通过后,利用IWDG触发复位,在Bootloader中切换启动Bank。若新固件运行异常(如死机、崩溃),IWDG将不断复位,Bootloader检测到连续复位后自动回滚至旧Bank。** ## 二、系统架构与升级流程 ### 2.1 内存布局 假设使用H743,Flash起始地址0x08000000,每个Bank大小为1MB(共2MB)。 | 区域 | 地址范围 | 用途 | |------|----------|------| | Bootloader | 0x08000000 - 0x0800FFFF | 启动引导、升级控制 | | Bank0 (App1) | 0x08010000 - 0x080FFFFF | 当前运行固件 | | Bank1 (App2) | 0x08100000 - 0x081FFFFF | 待升级固件或备份 | ### 2.2 升级流程 1. **Bootloader** 启动,检查标志位(如备份寄存器或Flash特定地址)。 2. 若存在待升级固件,则将其写入非活动Bank(如Bank1)。 3. 写入完成后,设置启动Bank切换标志,并触发软件复位。 4. 复位后,Bootloader根据标志位从新Bank启动。 5. 新固件运行,若正常则清除标志位;若异常,IWDG复位,Bootloader检测到连续复位次数超限,自动回滚至旧Bank。 ## 三、硬件看门狗配置详解 IWDG使用独立时钟(LSI),即使主时钟故障也能工作。配置要点: - **预分频器**:设置分频系数,典型值64,则计数时钟为LSI/64(LSI约32kHz,则约0.5kHz)。 - **重装载值**:决定超时时间,公式:`Timeout = (重装载值 / 计数频率) * 1000 ms`。 - **使能**:一旦写入0x5555到IWDG_KR,再写入0xCCCC,即启动,且不可关闭。 ### 配置代码(HAL库) ```c void IWDG_Config(void) { IWDG_HandleTypeDef hiwdg; hiwdg.Instance = IWDG; hiwdg.Init.Prescaler = IWDG_PRESCALER_64; // 分频64 hiwdg.Init.Reload = 4095; // 重装载值,超时约8.2秒 hiwdg.Init.Window = IWDG_WINDOW_DISABLE; if (HAL_IWDG_Init(&hiwdg) != HAL_OK) { Error_Handler(); } } // 喂狗函数,在主循环中周期性调用 void IWDG_Feed(void) { HAL_IWDG_Refresh(&hiwdg); } ``` ## 四、双Bank Flash切换实现 ### 4.1 关键寄存器与选项字节 - **Flash选项字节**:通过修改`FLASH_OPTR`寄存器中的`BOR_LEV`和`DBANK`位,可控制双Bank模式。默认H7为双Bank,无需额外配置。 - **启动Bank选择**:通过`FLASH_OPTR`的`SWAP_BANK`位,置1则从Bank1启动,置0从Bank0启动。修改后需复位生效。 ### 4.2 切换Bank的代码示例 ```c void Flash_SwitchBank(void) { FLASH_OBProgramInitTypeDef pOBInit; HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); // 读取当前选项字节 HAL_FLASHEx_OBGetConfig(&pOBInit); // 切换Bank:若当前从Bank0启动,则改为Bank1,反之亦然 if (pOBInit.USERConfig & FLASH_OPTR_SWAP_BANK) { pOBInit.USERConfig &= ~FLASH_OPTR_SWAP_BANK; // 清除位,回Bank0 } else { pOBInit.USERConfig |= FLASH_OPTR_SWAP_BANK; // 置位,切到Bank1 } HAL_FLASHEx_OBProgram(&pOBInit); HAL_FLASH_OB_Lock(); HAL_FLASH_Lock(); // 触发系统复位,使选项字节生效 NVIC_SystemReset(); } ``` ## 五、完整回滚策略代码示例 ### 5.1 Bootloader中的核心逻辑 ```c #define APP1_START_ADDR 0x08010000 #define APP2_START_ADDR 0x08100000 #define BOOT_FLAG_ADDR 0x0800FF00 // 存放升级标志和复位计数 // 检查复位原因,判断是否因看门狗复位 uint8_t CheckResetCause(void) { if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET) { __HAL_RCC_CLEAR_RESET_FLAGS(); return 1; // IWDG复位 } return 0; } // 读取复位计数,若连续超过3次,则回滚 void CheckAndRollback(void) { uint32_t reset_cnt = *(volatile uint32_t*)BOOT_FLAG_ADDR; uint8_t is_iwdg_reset = CheckResetCause(); if (is_iwdg_reset) { reset_cnt++; // 写入新计数(需先擦除标志区) FLASH_EraseSector(BOOT_FLAG_ADDR); FLASH_WriteWord(BOOT_FLAG_ADDR, reset_cnt); if (reset_cnt >= 3) { // 回滚:切换Bank到旧版本 Flash_SwitchBank(); // 内部会复位 } } else { // 正常启动,清零计数 FLASH_EraseSector(BOOT_FLAG_ADDR); FLASH_WriteWord(BOOT_FLAG_ADDR, 0); } } int main(void) { HAL_Init(); SystemClock_Config(); IWDG_Config(); CheckAndRollback(); // 根据当前启动Bank跳转至对应App if (READ_BIT(FLASH->OPTR, FLASH_OPTR_SWAP_BANK)) { JumpToApp(APP2_START_ADDR); } else { JumpToApp(APP1_START_ADDR); } while(1) { IWDG_Feed(); } } ``` ### 5.2 App端升级触发与喂狗 ```c // App中接收新固件,写入非活动Bank void OTA_Update(uint8_t* data, uint32_t len) { // 1. 擦除目标Bank(例如当前从Bank0运行,则擦除Bank1) // 2. 写入数据 // 3. 校验CRC // 4. 设置升级标志(如写入0xA5A5到BOOT_FLAG_ADDR) // 5. 调用Flash_SwitchBank() 复位 } // 主循环中周期性喂狗 while(1) { IWDG_Feed(); // 其他任务 } ``` ## 六、关键注意事项 - **喂狗时机**:IWDG超时时间需大于主循环最坏执行时间,但不宜过长(建议2~10秒),否则无法及时检测死机。 - **标志区可靠性**:存放复位计数的Flash区域需具备擦写寿命,建议使用独立扇区,并采用磨损均衡(如轮换地址)。 - **选项字节编程**:修改`SWAP_BANK`位时,必须确保Flash已解锁,且编程后需复位,期间不可断电,否则可能损坏选项字节。 - **App启动后清标志**:新固件首次启动时,应主动清除升级标志,避免Bootloader误判。 - **回滚条件**:连续复位次数阈值不宜过小(如3次),防止正常启动时偶发复位导致误回滚。 - **双Bank一致性**:确保两个Bank的固件版本兼容,避免回滚后因外设配置不匹配而异常。 ## 七、总结 通过STM32H7的双Bank Flash与IWDG联动,我们构建了一个健壮的OTA升级回滚机制。核心在于利用IWDG的不可屏蔽性,将系统异常转化为可检测的复位事件,再通过Bootloader中的计数逻辑实现自动回滚。此方案无需外部存储,成本低且可靠性高,适用于工业控制、智能仪表等对稳定性要求严苛的场景。实际项目中,还需结合CRC校验、固件签名等安全措施,构建完整的升级闭环。