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,通过中断向量表重映射执行。
升级流程:
- App收到新固件,写入非活动Bank(如Bank1)。
- 写入完成后,设置“升级待验证”标志(存于备份寄存器或Flash)。
- 软件复位,进入Bootloader。
- Bootloader检查新固件CRC/签名,若通过则交换Bank并启动新App;否则回滚到旧Bank。
- 新App启动后,喂狗并清除标志,若App崩溃,看门狗复位,Bootloader再次回滚。
4. 关键配置步骤
4.1 使能IWDG并配置超时
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切换(选项字节操作)
void Flash_SwapBank(void) {
FLASH_OptKeyPrg(0x45670123, 0xCDEF89AB); // 解锁选项字节
FLASH->OPTSR_CUR |= FLASH_OPTSR_SWAP_BANK; // 设置交换位
FLASH_OptKeyPrg(0x45670123, 0xCDEF89AB); // 重新上锁
NVIC_SystemReset(); // 复位生效
}
4.3 升级期间暂停喂狗
在App中,当开始写入新固件时,停止喂狗(或延长超时),但需确保写入操作能在看门狗超时前完成。若写入时间过长,可分段喂狗,但一旦进入校验阶段,必须停止喂狗以允许复位。
void OTA_Start(void) {
// 暂停喂狗:不再调用IWDG_Refresh()
// 写入新固件到非活动Bank
Flash_WriteBank(BANK1, new_fw, size);
// 设置待验证标志
BackupReg_Write(0x5A5A);
NVIC_SystemReset(); // 进入Bootloader
}
5. 完整代码示例(Bootloader部分)
#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校验、签名验证和日志记录,构建完整的升级安全体系。