# STM32H7双Bank模式下的固件升级失败自动回滚机制实现 ## 1. 为什么需要自动回滚? 在OTA(空中升级)或现场升级中,固件写入过程可能因断电、通信错误或校验失败而中断。若新固件损坏,设备将无法启动。传统单Bank方案只能依赖外部备份或人工干预,而STM32H7的双Bank闪存允许同时存储两个固件副本,配合硬件支持的切换机制,可实现无缝回滚,极大提升系统可靠性。 ## 2. 双Bank模式原理 STM32H7系列(如H743、H750)内置双Bank闪存,每个Bank容量相同(如1MB时每个512KB)。通过配置选项字节(Option Bytes)中的`DBANK`位,可切换为双Bank模式。此时,闪存地址空间被划分为两个独立区域: - **Bank 1**:通常存放当前运行固件(Active) - **Bank 2**:存放新固件(Pending) 系统复位后,硬件根据`BOOT_ADD0`和`BOOT_ADD1`选项字节决定从哪个Bank启动。若启动失败(如看门狗复位),可通过软件切换启动地址,实现回滚。 ## 3. 实现方案设计 ### 3.1 整体流程 1. 设备运行在Bank 1,接收新固件并写入Bank 2。 2. 写入完成后,设置标志位(如备份寄存器或Flash特定区域),标记新固件待验证。 3. 切换启动地址到Bank 2并软复位。 4. 若Bank 2固件运行正常(如应用层上报心跳),则清除标志位,完成升级。 5. 若Bank 2启动失败(如看门狗超时),系统自动回滚到Bank 1。 ### 3.2 关键配置 - **选项字节设置**:使用STM32CubeProgrammer或代码配置`DBANK=1`,并设置`BOOT_ADD0`和`BOOT_ADD1`。 - **看门狗**:启用独立看门狗(IWDG),在启动早期喂狗,若固件异常则触发复位。 - **标志位存储**:使用备份寄存器(BKP)或Flash末尾扇区存储升级状态。 ## 4. 代码实现 以下代码基于STM32H743,使用HAL库,演示核心逻辑。 ### 4.1 配置双Bank模式(初始化代码) ```c /* 启用双Bank模式 */ void Flash_EnableDualBank(void) { FLASH_OBProgramInitTypeDef ob; HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); ob.OptionType = OPTIONBYTE_BANK; ob.Bank = FLASH_BANK_1; // 配置Bank1选项 ob.BankConfig = FLASH_BANK_DUAL; // 双Bank模式 HAL_FLASH_OBProgram(&ob); HAL_FLASH_OB_Lock(); HAL_FLASH_Lock(); /* 需要重新上电生效 */ HAL_NVIC_SystemReset(); } ``` ### 4.2 固件写入Bank 2 ```c /* 将新固件写入Bank2,地址从0x08100000开始(假设Bank2基址) */ void Write_FirmwareToBank2(uint32_t *data, uint32_t len) { uint32_t addr = 0x08100000; HAL_FLASH_Unlock(); for (uint32_t i = 0; i < len; i += 4) { if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, *(uint64_t*)(data+i)) != HAL_OK) { // 错误处理 } addr += 8; // 双字写入 } HAL_FLASH_Lock(); } ``` ### 4.3 切换启动Bank并复位 ```c /* 设置启动地址为Bank2,并软复位 */ void SwitchToBank2AndReset(void) { /* 设置标志位,表示新固件待验证 */ Set_UpdateFlag(0xAA55); // 写入备份寄存器 /* 修改选项字节BOOT_ADD1为Bank2起始地址 */ FLASH_OBProgramInitTypeDef ob; HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); ob.OptionType = OPTIONBYTE_USER; ob.UserType = OB_USER_BOOT_ADD1; ob.BOOT_ADD1 = 0x08100000; // Bank2地址 HAL_FLASH_OBProgram(&ob); HAL_FLASH_OB_Lock(); HAL_FLASH_Lock(); HAL_NVIC_SystemReset(); } ``` ### 4.4 启动时检查回滚条件 ```c void Check_And_Rollback(void) { uint16_t flag = Get_UpdateFlag(); if (flag == 0xAA55) { // 有未验证的更新 /* 检查当前运行Bank */ if (__HAL_FLASH_GET_BANK() == FLASH_BANK_2) { /* 运行在Bank2,说明新固件已启动,但需验证 */ if (System_Check() == PASS) { /* 验证通过,清除标志 */ Clear_UpdateFlag(); // 正常继续运行 } else { /* 验证失败,回滚到Bank1 */ SwitchToBank1(); } } else { /* 运行在Bank1,说明Bank2启动失败,自动回滚 */ Clear_UpdateFlag(); // 继续运行Bank1固件 } } } void SwitchToBank1(void) { /* 设置BOOT_ADD0为Bank1地址 */ // 类似SwitchToBank2,但地址改为0x08000000 HAL_NVIC_SystemReset(); } ``` ### 4.5 看门狗配合 ```c /* 初始化IWDG,超时1秒 */ void IWDG_Init(void) { IWDG_HandleTypeDef hiwdg; hiwdg.Instance = IWDG; hiwdg.Init.Prescaler = IWDG_PRESCALER_64; hiwdg.Init.Reload = 4095; // 约1秒 HAL_IWDG_Init(&hiwdg); } /* 在主循环中喂狗 */ while (1) { HAL_IWDG_Refresh(&hiwdg); // 应用逻辑 } ``` ## 5. 注意事项 - **地址映射**:确认具体型号的Bank基址(H743的Bank1为0x08000000,Bank2为0x08100000)。 - **选项字节修改**:修改后需复位生效,且注意保护其他选项字节。 - **固件大小**:确保固件不超过Bank容量,否则需压缩或分块。 - **验证机制**:建议在应用层增加CRC校验或版本检查,避免误判。 - **看门狗**:在启动早期喂狗,防止正常启动被误复位。 - **中断向量表**:若固件从Bank2启动,需重定位中断向量表(SCB->VTOR)。 ## 6. 总结 通过STM32H7的双Bank模式,我们实现了升级失败后的自动回滚,无需外部硬件,仅靠内部闪存和看门狗即可保证设备始终可运行。该方案适用于对可靠性要求高的工业、汽车等嵌入式场景。开发者可根据实际需求调整验证逻辑和回滚策略,构建更健壮的OTA系统。