# 一、双Bank架构与OTA零停机原理 STM32H7系列(如H743/H750)内置双Bank闪存,每个Bank容量相等(如1MB时各512KB)。通过配置选项字节`DBANK`,可将闪存划分为两个独立区域,支持**并行编程/擦除**与**无缝切换**。 **零停机核心机制:** - 系统从Bank1运行,同时可对Bank2进行擦写(无需暂停应用)。 - 升级完成后,通过置位`FLASH_CR`的`SWAP_BANK`位,硬件自动交换Bank映射,复位后CPU从新固件启动。 - 若新固件自检失败,可再次切换回旧Bank,实现秒级回滚。 **关键优势:** - 升级过程不中断实时控制(如电机、通信)。 - 回滚无需外部备份,仅依赖闪存切换。 - 支持多版本固件驻留,便于A/B测试。 # 二、系统总体设计 ## 2.1 内存布局 | 区域 | 地址范围 | 用途 | |------|----------|------| | Bootloader | 0x08000000 - 0x0800FFFF | 启动引导、FDCAN接收、校验逻辑 | | App_Bank1 | 0x08010000 - 0x0807FFFF | 当前运行固件 | | App_Bank2 | 0x08080000 - 0x080FFFFF | 待升级固件 | ## 2.2 升级流程 1. Bootloader通过FDCAN接收固件包,写入Bank2(非活动区)。 2. 接收完成后,计算CRC32并与发送端校验值比对。 3. 校验通过,置位`SWAP_BANK`并软复位。 4. 新固件启动后,运行自检(如心跳、外设测试),若失败则调用回滚API。 # 三、FDCAN通信协议设计 采用经典CAN 2.0B帧,ID分段: - `0x100`:握手请求 - `0x101`:固件数据(每帧最多8字节) - `0x102`:结束帧(含CRC) - `0x103`:应答帧(ACK/NACK) 数据帧格式:`[包序号(2字节)][数据(6字节)]`,包序号用于重组。 # 四、关键代码实现 ## 4.1 双Bank切换配置 ```c void Flash_SwapBank(void) { FLASH_OBProgramInitTypeDef ob; HAL_FLASH_Unlock(); HAL_FLASH_OB_Unlock(); // 读取当前DBANK配置,确保为双Bank模式 ob.OptionType = OPTIONBYTE_BYTE; ob.BYTE = 0; // 实际需根据芯片调整 HAL_FLASH_OB_GetConfig(&ob); // 置位SWAP_BANK位(需先设置FLASH_CR的SWAP_BANK) FLASH->CR |= FLASH_CR_SWAP_BANK; while (FLASH->SR & FLASH_SR_BSY); HAL_FLASH_OB_Lock(); HAL_FLASH_Lock(); // 软复位生效 NVIC_SystemReset(); } ``` ## 4.2 固件写入Bank2(非活动区) ```c void Write_Firmware_To_Bank2(uint32_t addr, uint8_t *data, uint32_t len) { HAL_FLASH_Unlock(); FLASH_EraseInitTypeDef erase; uint32_t error; // 擦除Bank2(地址0x08080000起,共512KB) erase.TypeErase = FLASH_TYPEERASE_SECTORS; erase.Banks = FLASH_BANK_2; erase.Sector = 0; erase.NbSectors = 8; // 根据扇区大小调整 erase.VoltageRange = FLASH_VOLTAGE_RANGE_3; HAL_FLASHEx_Erase(&erase, &error); // 写入数据(按32位编程) for (uint32_t i = 0; i < len; i += 8) { uint64_t val = *(uint64_t *)(data + i); HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, addr + i, val); } HAL_FLASH_Lock(); } ``` ## 4.3 回滚机制实现 ```c void Rollback_Firmware(void) { // 检查当前运行Bank,若为Bank2则切换回Bank1 if (IS_ACTIVE_BANK2()) { Flash_SwapBank(); // 切换后复位,自动运行Bank1 } // 若为Bank1,则无需操作(旧固件仍有效) } // 在应用启动时调用自检,失败则回滚 void App_Self_Test(void) { if (Check_CRC() != PASS) { Rollback_Firmware(); } } ``` # 五、Bootloader跳转逻辑 ```c void Jump_To_App(uint32_t app_addr) { // 检查栈顶地址合法性 if (((*(volatile uint32_t *)app_addr) & 0x2FFE0000) != 0x20000000) { return; } // 设置MSP并跳转 __set_MSP(*(volatile uint32_t *)app_addr); void (*app_reset)(void) = (void (*)(void))(*(volatile uint32_t *)(app_addr + 4)); app_reset(); } ``` # 六、注意事项与优化建议 - **中断向量表重定向**:应用固件需在启动时设置`SCB->VTOR = 0x08010000`(或当前Bank基址)。 - **FDCAN缓冲区管理**:使用DMA接收,避免CPU占用,确保高速传输不丢帧。 - **CRC校验**:建议使用硬件CRC单元(如CRC32),加速计算。 - **看门狗**:在升级过程中喂狗,防止意外复位导致半写入状态。 - **双Bank切换时序**:切换期间闪存不可访问,需确保无中断干扰。 - **测试覆盖**:模拟断电、通信中断等异常,验证回滚可靠性。 # 七、总结 本文基于STM32H7双Bank特性,结合FDCAN实现了一套零停机OTA方案。通过硬件级Bank切换,避免了传统双区拷贝的时间开销,回滚机制保障了升级失败时的系统可用性。开发者可根据实际需求调整帧格式与校验策略,该架构同样适用于其他支持双Bank的MCU(如STM32L4+)。