STM32H7双Bank Flash在线升级失败后自动回滚的硬件看门狗联动设计
👁 2 阅读 · 2026-08-27 · 嵌入式
在工业级嵌入式产品中,OTA升级失败可能导致设备变砖。STM32H7系列凭借双Bank Flash架构,可实现无缝回滚。本文深入讲解如何利用硬件看门狗(IWDG)与双Bank切换机制联动,设计一套高可靠的在线升级方案:升级时喂狗暂停、校验失败触发复位、Bootloader自动回滚。文章涵盖原理分析、寄存器配置、完整代码示例及工程注意事项,助你打造永不掉线的固件升级系统。
# STM32H7双Bank Flash在线升级失败后自动回滚的硬件看门狗联动设计
## 1. 为什么需要双Bank + 看门狗联动?
在线升级(OTA)是嵌入式产品的核心功能,但升级过程中断电、通信中断或固件校验失败都可能导致系统无法启动。传统单Bank方案只能依赖外部备份,而STM32H7的双Bank Flash允许在运行当前固件的同时,将新固件写入另一个Bank,并通过硬件机制快速切换启动。
然而,仅靠双Bank还不够——如果新固件本身存在缺陷(如死循环),系统可能卡死在App中。此时,硬件看门狗(IWDG)成为最后防线:它独立于主时钟,超时后强制复位,让Bootloader有机会回滚到旧固件。
**核心思想**:升级过程分为两个阶段——
- **阶段1(写入新固件)**:暂停喂狗,允许长时间写入,但若卡死则看门狗复位。
- **阶段2(验证并切换)**:复位后Bootloader检查新固件有效性,失败则回滚旧Bank。
## 2. 硬件与原理基础
### 2.1 STM32H7双Bank Flash特性
- H7系列(如STM32H743)Flash容量通常为2MB,分为两个1MB的Bank(Bank0和Bank1)。
- 通过选项字节`FLASH_OPTCR`的`SWAP_BANK`位,可动态交换两个Bank的地址映射。
- 支持“无感切换”:在运行时设置交换位,复位后系统从新Bank启动。
### 2.2 硬件看门狗(IWDG)
- 独立于主时钟(LSI约32kHz),超时时间可配置(如0.5~32秒)。
- 一旦启动,必须定期“喂狗”(写0xAAAA到IWDG_KR),否则系统复位。
- 在升级期间,如果喂狗中断,看门狗会复位系统,从而触发回滚机制。
## 3. 系统架构与升级流程
```
Bootloader (Bank0) <---> App (Bank1)
```
- **Bootloader**:位于固定地址(如0x08000000),负责启动、升级、回滚。
- **App**:运行在另一个Bank,通过中断向量表重映射执行。
**升级流程**:
1. App收到新固件,写入非活动Bank(如Bank1)。
2. 写入完成后,设置“升级待验证”标志(存于备份寄存器或Flash)。
3. 软件复位,进入Bootloader。
4. Bootloader检查新固件CRC/签名,若通过则交换Bank并启动新App;否则回滚到旧Bank。
5. 新App启动后,喂狗并清除标志,若App崩溃,看门狗复位,Bootloader再次回滚。
## 4. 关键配置步骤
### 4.1 使能IWDG并配置超时
```c
void IWDG_Config(void) {
// 使能IWDG(LSI时钟约32kHz)
IWDG->KR = 0x5555; // 解锁
IWDG->PR = 0x06; // 分频256,得到125Hz
IWDG->RLR = 1250; // 重载值1250,超时10秒
IWDG->KR = 0xAAAA; // 喂狗
IWDG->KR = 0xCCCC; // 启动
}
```
### 4.2 双Bank切换(选项字节操作)
```c
void Flash_SwapBank(void) {
FLASH_OptKeyPrg(0x45670123, 0xCDEF89AB); // 解锁选项字节
FLASH->OPTSR_CUR |= FLASH_OPTSR_SWAP_BANK; // 设置交换位
FLASH_OptKeyPrg(0x45670123, 0xCDEF89AB); // 重新上锁
NVIC_SystemReset(); // 复位生效
}
```
### 4.3 升级期间暂停喂狗
在App中,当开始写入新固件时,停止喂狗(或延长超时),但需确保写入操作能在看门狗超时前完成。若写入时间过长,可分段喂狗,但一旦进入校验阶段,必须停止喂狗以允许复位。
```c
void OTA_Start(void) {
// 暂停喂狗:不再调用IWDG_Refresh()
// 写入新固件到非活动Bank
Flash_WriteBank(BANK1, new_fw, size);
// 设置待验证标志
BackupReg_Write(0x5A5A);
NVIC_SystemReset(); // 进入Bootloader
}
```
## 5. 完整代码示例(Bootloader部分)
```c
#include "stm32h7xx.h"
#define APP_BANK0_ADDR 0x08000000
#define APP_BANK1_ADDR 0x08100000
#define VALID_FLAG 0xA5A5A5A5
void IWDG_Config(void) {
IWDG->KR = 0x5555;
IWDG->PR = 0x06;
IWDG->RLR = 1250;
IWDG->KR = 0xAAAA;
IWDG->KR = 0xCCCC;
}
void IWDG_Refresh(void) {
IWDG->KR = 0xAAAA;
}
uint32_t Check_FW_CRC(uint32_t addr, uint32_t size) {
// 简化:计算CRC32,这里省略实现
return 0x12345678; // 假设校验通过
}
void Jump_To_App(uint32_t addr) {
uint32_t msp = *(volatile uint32_t*)addr;
void (*app_main)(void) = (void (*)(void))(*(volatile uint32_t*)(addr+4));
__set_MSP(msp);
app_main();
}
int main(void) {
IWDG_Config();
uint32_t active_bank = (FLASH->OPTSR_CUR & FLASH_OPTSR_SWAP_BANK) ? 1 : 0;
uint32_t new_bank = (active_bank == 0) ? 1 : 0;
uint32_t new_addr = (new_bank == 0) ? APP_BANK0_ADDR : APP_BANK1_ADDR;
// 检查升级标志
if (BackupReg_Read() == 0x5A5A) {
// 验证新固件
if (Check_FW_CRC(new_addr, 0x100000) == VALID_FLAG) {
// 交换Bank
FLASH_OptKeyPrg(0x45670123, 0xCDEF89AB);
FLASH->OPTSR_CUR |= FLASH_OPTSR_SWAP_BANK;
FLASH_OptKeyPrg(0x45670123, 0xCDEF89AB);
NVIC_SystemReset();
} else {
// 校验失败,回滚:清除标志,启动旧App
BackupReg_Write(0);
// 不交换Bank,直接启动当前Bank
Jump_To_App((active_bank == 0) ? APP_BANK0_ADDR : APP_BANK1_ADDR);
}
} else {
// 正常启动当前App
Jump_To_App((active_bank == 0) ? APP_BANK0_ADDR : APP_BANK1_ADDR);
}
while(1) {
IWDG_Refresh(); // 喂狗,防止Bootloader卡死
}
}
```
## 6. 注意事项与陷阱
- **看门狗超时设置**:必须大于固件写入和校验的最坏情况时间,否则升级中途复位。建议超时10~30秒,并在写入时每写一页喂一次狗。
- **选项字节操作**:修改SWAP_BANK位前,必须先解锁选项字节,且操作后需要复位才生效。注意,选项字节编程会擦除整个扇区,务必确保电源稳定。
- **中断向量表重映射**:App中必须设置SCB->VTOR为当前Bank的起始地址,否则中断会跳转到错误位置。
- **备份寄存器**:用于存储升级标志,但备份寄存器在VDD掉电时会丢失。若需持久化,可写入Flash专用区域。
- **回滚策略**:建议在App启动后延迟一段时间(如5秒)再清除升级标志,若App崩溃,看门狗复位后Bootloader仍能回滚。
- **双Bank大小**:确保两个Bank容量一致,且固件大小不超过单个Bank。
## 7. 总结
通过硬件看门狗与双Bank Flash联动,STM32H7实现了升级失败的自愈能力:写入阶段看门狗兜底,校验阶段复位触发回滚,App运行阶段崩溃也能恢复。这套设计极大提升了OTA的可靠性,适用于电力、医疗、汽车等对稳定性要求极高的场景。实际项目中,还需结合CRC校验、签名验证和日志记录,构建完整的升级安全体系。