STM32H7双Bank Flash在线升级失败自动回滚:硬件看门狗联动策略深度解析
👁 1 阅读 · 2026-08-27 · 嵌入式
在工业级嵌入式产品中,OTA升级失败可能导致设备变砖。STM32H7系列凭借其双Bank Flash架构,为安全升级提供了硬件级保障。本文将深入探讨如何利用硬件看门狗(IWDG)与双Bank Flash的原子切换特性,实现升级失败后的自动回滚机制。通过精心设计的联动策略,确保系统在异常情况下能自动恢复至上一版本固件,极大提升设备可靠性。文章将涵盖原理剖析、配置步骤、完整代码示例及关键注意事项,助你打造坚不可摧的OTA升级方案。
# 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校验、固件签名等安全措施,构建完整的升级闭环。