STM32H7双Bank模式下在线固件升级失败后自动回滚的硬件看门狗联动策略
👁 1 阅读 · 2026-08-27 · 嵌入式
在工业级嵌入式系统中,在线固件升级(OTA)的可靠性至关重要。STM32H7系列凭借其双Bank闪存架构,为无缝升级和失败回滚提供了硬件基础。然而,若升级过程中系统崩溃或断电,可能导致设备变砖。本文深入探讨如何利用硬件独立看门狗(IWDG)与双Bank机制联动,实现升级失败后的自动回滚,确保系统在任何异常情况下都能恢复至上一个稳定版本。
# STM32H7双Bank模式下在线固件升级失败后自动回滚的硬件看门狗联动策略
## 1. 背景与挑战
在线固件升级(OTA)是物联网设备的核心功能,但升级过程中的任何意外(如断电、通信中断、程序跑飞)都可能使设备无法启动。传统单Bank方案通常需要Bootloader引导,且升级失败后只能依赖外部干预。STM32H7的双Bank闪存架构(如STM32H743的2MB Flash分为两个1MB Bank)允许在运行Bank A时擦写Bank B,并通过硬件机制快速切换启动Bank,这为自动回滚提供了理想平台。
然而,仅靠双Bank还不够——若升级后的固件本身存在致命Bug(如启动即崩溃),系统将反复重启,无法进入回滚流程。此时,硬件看门狗(IWDG)成为关键:它独立于主时钟,一旦超时未喂狗,将强制复位MCU。通过巧妙设计,IWDG可触发Bootloader执行回滚操作。
## 2. 双Bank机制与IWDG联动原理
### 2.1 双Bank启动逻辑
STM32H7通过选项字节(Option Bytes)中的`nBOOT_SEL`和`BANK_SEL`位控制启动Bank。当`nBOOT_SEL=0`时,启动Bank由`BANK_SEL`决定(0=Bank1,1=Bank2)。此外,`SWAP_BANK`位可动态交换两个Bank的地址映射,无需修改启动引脚。
### 2.2 IWDG回滚策略
设计思路如下:
- **升级流程**:Bootloader将新固件写入非活动Bank,并设置一个“升级标志”(存储在备份寄存器或Flash末尾)。然后,通过`SWAP_BANK`切换启动Bank并复位。
- **运行监控**:新固件启动后,必须在规定时间内(如5秒)完成初始化并喂狗。若新固件崩溃或死机,IWDG超时复位。
- **回滚触发**:Bootloader在启动时检查升级标志。若标志存在且上次复位由IWDG产生,则判定升级失败,清除标志并恢复原启动Bank,从而回滚至旧固件。
该策略无需外部通信,完全由硬件自动完成,可靠性极高。
## 3. 配置步骤(以STM32H743为例)
### 3.1 硬件连接
- 使用STM32H743-Nucleo开发板,板载ST-Link,无需额外硬件。
- IWDG使用LSI时钟(约32kHz),无需外部晶振。
### 3.2 软件架构
- **Bootloader**:位于Bank1起始地址(0x08000000),负责固件接收、写入、启动切换和回滚判断。
- **App1**:位于Bank1偏移0x10000处(假设Bootloader占64KB)。
- **App2**:位于Bank2起始地址(0x08100000)。
### 3.3 关键代码实现
#### 3.3.1 Bootloader中的IWDG初始化与回滚判断
```c
// bootloader.c
#include "stm32h7xx_hal.h"
#define APP1_ADDR 0x08010000
#define APP2_ADDR 0x08100000
#define UPGRADE_FLAG_ADDR 0x0800FF00 // 存储在Bootloader末尾
void IWDG_Init(uint32_t timeout_ms) {
// 使能LSI时钟
RCC->CSR |= RCC_CSR_LSION;
while ((RCC->CSR & RCC_CSR_LSIRDY) == 0);
// 配置IWDG,超时时间约5秒(LSI=32kHz,预分频256,重载值625)
IWDG->KR = 0x5555; // 解锁
IWDG->PR = 6; // 预分频256
IWDG->RLR = 625; // 625*256/32000 ≈ 5s
IWDG->KR = 0xCCCC; // 启动
}
void IWDG_Feed(void) {
IWDG->KR = 0xAAAA;
}
uint32_t CheckUpgradeFlag(void) {
return (*(volatile uint32_t*)UPGRADE_FLAG_ADDR == 0xA5A5A5A5);
}
void ClearUpgradeFlag(void) {
FLASH_Unlock();
FLASH_Erase_Sector(FLASH_SECTOR_1, VOLTAGE_RANGE_3); // 假设标志在Sector1
FLASH_Lock();
}
void JumpToApp(uint32_t app_addr) {
// 设置MSP和跳转
uint32_t msp = *(volatile uint32_t*)app_addr;
void (*app_reset)(void) = (void (*)(void))(*(volatile uint32_t*)(app_addr + 4));
__set_MSP(msp);
app_reset();
}
int main(void) {
HAL_Init();
IWDG_Init(5000);
// 检查升级标志和复位原因
if (CheckUpgradeFlag() && (RCC->CSR & RCC_CSR_IWDGRSTF)) {
// 升级失败,回滚
ClearUpgradeFlag();
// 确保启动Bank为原Bank(假设升级时从Bank1切到Bank2,现在切回)
FLASH_Unlock();
FLASH->OPTSR_CUR &= ~FLASH_OPTSR_SWAP_BANK; // 清除SWAP位
FLASH->CR |= FLASH_CR_OPTSTRT;
while (FLASH->CR & FLASH_CR_OPTSTRT);
FLASH_Lock();
// 复位系统
NVIC_SystemReset();
}
// 正常启动流程:检查标志,若有则跳转到新Bank,否则跳转旧Bank
if (CheckUpgradeFlag()) {
// 升级完成,跳转到新Bank(假设当前Bank1,新固件在Bank2)
JumpToApp(APP2_ADDR);
} else {
JumpToApp(APP1_ADDR);
}
while(1);
}
```
#### 3.3.2 应用程序中的喂狗与升级触发
```c
// app.c(以App1为例)
#include "stm32h7xx_hal.h"
extern void IWDG_Feed(void); // 在Bootloader中实现,但App中需重新初始化IWDG?不,IWDG一旦启动无法关闭,App需继续喂狗。
void SystemClock_Config(void);
int main(void) {
HAL_Init();
SystemClock_Config();
// 重新配置IWDG(因为复位后IWDG会重新加载,但需确保在App中也能喂狗)
// 注意:IWDG在复位后自动启动,但预分频和重载值会保持?实际上复位后IWDG重新加载默认值,需重新配置。
// 因此,App中需再次初始化IWDG,并定期喂狗。
IWDG_Init(5000);
while(1) {
// 业务逻辑
IWDG_Feed();
HAL_Delay(1000);
}
}
// 升级函数示例:接收新固件并写入Bank2,然后设置标志并切换
void OTA_Upgrade(uint8_t* data, uint32_t len) {
// 擦写Bank2(需解锁Flash)
// ...
// 写入完成后,设置升级标志
FLASH_Unlock();
FLASH_Program_Word(UPGRADE_FLAG_ADDR, 0xA5A5A5A5);
FLASH_Lock();
// 切换启动Bank(设置SWAP_BANK位)
FLASH_Unlock();
FLASH->OPTSR_CUR |= FLASH_OPTSR_SWAP_BANK;
FLASH->CR |= FLASH_CR_OPTSTRT;
while (FLASH->CR & FLASH_CR_OPTSTRT);
FLASH_Lock();
// 复位系统
NVIC_SystemReset();
}
```
## 4. 注意事项
- **IWDG配置一致性**:Bootloader和App中必须使用相同的IWDG超时时间,否则可能导致误复位或回滚失效。建议将IWDG初始化代码封装为公共库。
- **升级标志的存储**:标志应放在独立扇区,避免与代码冲突。使用备份寄存器(如RTC的BKP)更安全,但需注意备份域电源。
- **Flash操作时序**:在擦写Bank2时,应关闭全局中断,防止中断服务程序干扰Flash操作,导致写入失败。
- **回滚条件**:仅当IWDG复位且升级标志存在时才回滚。若用户手动复位(如按键),不应回滚,否则可能误判。可通过`RCC->CSR`中的复位标志区分。
- **测试验证**:建议在开发阶段模拟升级失败(如故意在App中死循环),确认回滚机制正常工作。
## 5. 总结
通过STM32H7的双Bank架构与IWDG联动,我们实现了一个无需外部干预的自动回滚系统。该策略不仅适用于工业设备,也可用于消费类产品,显著提升OTA的可靠性。开发者可根据实际需求调整超时时间和回滚逻辑,例如增加重试次数或记录错误日志。希望本文能为你的嵌入式项目提供有价值的参考。