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。

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初始化与喂狗

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)存储标志,因为复位后内容不丢失。

#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负责启动流程控制,核心代码如下:

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会复位。

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升级的可靠性,还降低了现场维护成本。关键在于合理设计验证流程和超时策略,确保系统在异常情况下能够安全恢复。这种设计模式适用于所有需要高可靠性的嵌入式产品。