# 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校验、签名验证和日志记录,构建完整的升级安全体系。