# STM32H7双Bank Flash在线升级失败后自动回滚的硬件看门狗联动机制设计 ## 一、为什么需要回滚机制? 在线升级(OTA)是嵌入式产品迭代的核心功能,但升级过程充满风险:传输错误、校验失败、写入中断(如断电)等,都可能导致新固件无法启动。若没有回滚机制,设备将永久变砖,对于现场部署的设备,这将造成巨大损失。STM32H7系列的双Bank Flash为此提供了硬件级支持,允许在运行时切换启动区域,配合独立看门狗(IWDG),可以实现全自动的失败恢复。 ## 二、双Bank Flash与IWDG联动原理 ### 2.1 双Bank Flash架构 STM32H7的Flash被划分为两个独立的Bank(Bank0和Bank1),每个Bank大小相同(例如H743的1MB Flash,每个Bank 512KB)。通过设置选项字节中的`nDBANK`位,可以配置为单Bank或双Bank模式。在双Bank模式下,两个Bank可以独立擦写,且支持**无缝切换启动**:通过修改`SYSCFG_MEMRMP`寄存器的`SWP_FB`位,可以将Bank0和Bank1的地址映射互换,从而实现从另一个Bank启动。 ### 2.2 硬件看门狗(IWDG)的作用 IWDG是独立于主时钟的看门狗,使用LSI(约32kHz)作为时钟源,一旦启动无法停止(除非复位)。它的核心价值在于:**如果固件在设定时间内未“喂狗”,系统将强制复位**。这为检测固件运行异常提供了最后一道防线。 ### 2.3 联动机制设计 整个机制的核心思想是: 1. **升级前**:将新固件写入非活动Bank(例如当前运行Bank0,则写入Bank1)。 2. **升级完成后**:设置一个“升级待验证”标志(存储在备份寄存器或Flash的特定区域),然后切换启动Bank并复位。 3. **启动后**:Bootloader检查该标志,若存在,则启动新固件并启动IWDG,同时开启一个“验证定时器”(例如5秒)。 4. **验证成功**:新固件在定时器超时前正常运行并“喂狗”,则清除“升级待验证”标志,升级完成。 5. **验证失败**:若新固件崩溃或卡死,IWDG超时复位。复位后Bootloader检测到“升级待验证”标志仍存在,则自动切换回旧Bank,并清除标志,实现回滚。 ## 三、硬件与软件准备 ### 3.1 硬件要求 - STM32H743/H750等双Bank系列芯片 - 外部复位电路(可选,但推荐) - 调试串口(用于打印日志) ### 3.2 软件配置 使用STM32CubeIDE或HAL库,需要配置: - Flash双Bank模式(通过选项字节) - IWDG(独立看门狗) - 备份寄存器(用于存储标志) ## 四、关键代码实现 ### 4.1 选项字节配置(双Bank模式) 在系统初始化时,检查并配置双Bank模式。注意:修改选项字节需要解锁Flash。 ```c void Flash_EnableDualBank(void) { FLASH_OBProgramInitTypeDef obInit; HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); obInit.OptionType = OPTIONBYTE_USER; obInit.USERType = OB_USER_BANK_DUAL; obInit.USERConfig = OB_BANK_DUAL_ENABLE; // 使能双Bank HAL_FLASH_OBProgram(&obInit); HAL_FLASH_OB_Lock(); HAL_FLASH_Lock(); // 需要复位生效 HAL_NVIC_SystemReset(); } ``` ### 4.2 IWDG初始化与喂狗 ```c void IWDG_Init(uint8_t prescaler, uint16_t reload) { // 例如:prescaler=64, reload=4095 => 超时约8秒 (LSI=32kHz) IWDG_HandleTypeDef hiwdg; hiwdg.Instance = IWDG; hiwdg.Init.Prescaler = prescaler; hiwdg.Init.Reload = reload; hiwdg.Init.Window = 0; // 无窗口 HAL_IWDG_Init(&hiwdg); } void IWDG_Feed(void) { HAL_IWDG_Refresh(&hiwdg); // 喂狗 } ``` ### 4.3 升级标志管理(备份寄存器) 使用RTC备份寄存器(如BKP->REG1)存储标志,因为复位后内容不丢失。 ```c #define UPGRADE_PENDING_FLAG 0xA5A5 #define UPGRADE_DONE_FLAG 0x5A5A void SetUpgradeFlag(uint16_t flag) { // 使能备份寄存器访问 HAL_PWR_EnableBkUpAccess(); // 写入备份寄存器1 HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, flag); } uint16_t GetUpgradeFlag(void) { return HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1); } ``` ### 4.4 Bootloader中的回滚逻辑 Bootloader负责启动流程控制,核心代码如下: ```c void Bootloader_CheckAndRun(void) { uint16_t flag = GetUpgradeFlag(); if (flag == UPGRADE_PENDING_FLAG) { // 上次升级未验证,回滚到旧Bank printf("Upgrade failed, rollback to old bank\n"); // 切换启动Bank到旧Bank(假设当前是Bank1,切回Bank0) SYSCFG->MEMRMP &= ~SYSCFG_MEMRMP_SWP_FB; // 清除交换位,恢复Bank0 SetUpgradeFlag(UPGRADE_DONE_FLAG); // 清除待验证标志 // 复位启动旧固件 HAL_NVIC_SystemReset(); } else { // 正常启动,检查当前运行Bank if (SYSCFG->MEMRMP & SYSCFG_MEMRMP_SWP_FB) { // 当前从Bank1启动,说明是升级后的新固件 // 启动新固件,并启动IWDG和验证定时器 printf("New firmware boot, start verification\n"); IWDG_Init(64, 4095); // 8秒超时 // 跳转到新固件入口(Bank1的起始地址) JumpToApp(0x08000000 + BANK_SIZE); // 假设Bank1起始地址 } else { // 正常从Bank0启动,直接跳转 JumpToApp(0x08000000); } } } void JumpToApp(uint32_t app_addr) { // 跳转函数,设置MSP和PC // ... } ``` ### 4.5 新固件中的验证逻辑 新固件启动后,需要在一定时间内完成自检并清除待验证标志,否则IWDG会复位。 ```c int main(void) { // 初始化系统、时钟、外设等 // ... // 启动IWDG(与Bootloader中相同的配置) IWDG_Init(64, 4095); // 启动验证定时器(例如5秒) uint32_t start_tick = HAL_GetTick(); // 主循环 while (1) { // 执行关键自检(如检查外设、通信等) if (SelfTestPassed()) { // 自检通过,清除待验证标志,升级完成 SetUpgradeFlag(UPGRADE_DONE_FLAG); printf("Upgrade verified, clear flag\n"); break; // 退出验证模式,进入正常应用 } // 喂狗,防止复位 IWDG_Feed(); // 超时检查(5秒) if (HAL_GetTick() - start_tick > 5000) { // 超时未通过自检,故意不喂狗,让IWDG复位 printf("Self-test timeout, reset\n"); while(1); // 等待复位 } } // 正常应用代码 // ... } ``` ## 五、注意事项 - **IWDG超时时间**:必须大于新固件自检所需时间,否则可能误复位。建议设置为自检时间的1.5倍以上。 - **备份寄存器初始化**:确保RTC时钟已启用,否则备份寄存器无法访问。 - **Flash擦写保护**:在升级过程中,注意不要擦除Bootloader所在的区域。 - **双Bank切换时机**:切换Bank必须在复位前完成,且要确保新固件已完整写入。 - **日志输出**:在Bootloader和固件中增加日志,便于调试回滚原因。 - **测试覆盖**:模拟各种失败场景(如写入中断、校验失败、运行崩溃)进行充分测试。 ## 六、总结 通过STM32H7的双Bank Flash和硬件看门狗联动,我们实现了一个无需外部干预的自动回滚机制。该机制不仅提升了OTA升级的可靠性,还降低了现场维护成本。关键在于合理设计验证流程和超时策略,确保系统在异常情况下能够安全恢复。这种设计模式适用于所有需要高可靠性的嵌入式产品。