STM32H7双Bank Flash在线升级失败后自动回滚的硬件看门狗联动机制设计
👁 1 阅读 · 2026-08-27 · 嵌入式
在工业级嵌入式产品中,OTA升级的可靠性至关重要。STM32H7系列凭借其双Bank Flash架构,为A/B分区升级提供了硬件基础。然而,升级过程中若发生断电或程序跑飞,可能导致系统变砖。本文深入探讨如何利用硬件独立看门狗(IWDG)与双Bank Flash的自动回滚机制联动,设计一套健壮的在线升级容错方案。通过详细的原理剖析、配置步骤和完整代码示例,帮助开发者构建一个即使升级失败也能自动恢复的可靠系统。
# 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升级的可靠性,还降低了现场维护成本。关键在于合理设计验证流程和超时策略,确保系统在异常情况下能够安全恢复。这种设计模式适用于所有需要高可靠性的嵌入式产品。