# STM32H7 双Bank闪存并行编程:利用ART加速器实现零等待OTA固件切换 ## 引言:OTA升级的痛点与H7的破局 传统嵌入式OTA升级常面临两大痛点:升级期间系统必须停机(单Bank方案),或升级后需重启切换(A/B方案虽可避免停机但切换延迟明显)。STM32H7系列通过双Bank闪存架构与ART(Adaptive Real-Time)加速器的协同设计,实现了真正的**零等待固件切换**:后台Bank可并行写入新固件,而前台Bank继续执行旧代码,切换时仅需修改标志位并复位,ART缓存自动失效,新固件即刻运行,无任何性能回退。 ## 原理剖析:双Bank与ART的协同机制 ### 双Bank闪存架构 STM32H7的闪存分为两个独立的Bank(Bank1和Bank2),每个Bank容量可达1MB(视型号而定)。关键特性: - **独立擦写**:两个Bank可同时进行读写操作,但同一时刻只能有一个Bank被编程/擦除,另一个可被读取执行。 - **无缝切换**:通过设置选项字节或系统控制寄存器中的SWAP位,可将物理Bank映射到固定地址空间(0x08000000),实现固件切换。 - **并行编程**:利用双Bank的独立性,可在Bank2后台写入新固件,同时Bank1继续运行当前程序。 ### ART加速器与缓存一致性 ART加速器是H7的指令缓存和预取单元,它缓存闪存中的指令,减少CPU等待周期。当发生Bank切换时,ART缓存内容可能来自旧Bank,导致执行错误。因此,切换前必须**失效ART缓存**(通过SCB_InvalidateICache或ART外设的CACHE_CR寄存器)。H7的ART支持按Bank独立缓存,但切换后物理地址映射变化,统一失效是最安全策略。 ## 配置步骤:从零到零等待切换 ### 1. 启用双Bank模式 默认情况下,H7可能处于单Bank模式。需在编程选项字节中启用双Bank(若芯片支持)。在代码中可通过读取FLASH_OPTR寄存器确认。若需切换,使用HAL_FLASH_OB_Program配置,但通常出厂已启用,此处略过。 ### 2. 配置Flash控制器 ```c /* 解锁Flash */ HAL_FLASH_Unlock(); /* 设置编程延迟(根据主频调整) */ FLASH_WaitForLastOperation(1000); __HAL_FLASH_SET_LATENCY(FLASH_LATENCY_4); // 主频400MHz时需4个等待周期 /* 启用ART加速器(若未启用) */ __HAL_FLASH_ART_ENABLE(); ``` ### 3. 后台Bank编程(以Bank2为例) ```c #define BANK2_BASE_ADDR 0x08100000 // 假设Bank2起始地址 void Flash_WriteBank2(uint32_t offset, uint32_t *data, uint32_t len) { uint32_t addr = BANK2_BASE_ADDR + offset; /* 擦除Bank2的扇区(按需擦除) */ FLASH_EraseInitTypeDef erase = {0}; erase.TypeErase = FLASH_TYPEERASE_SECTORS; erase.Banks = FLASH_BANK_2; erase.Sector = 0; erase.NbSectors = 8; // 根据实际扇区数 erase.VoltageRange = FLASH_VOLTAGE_RANGE_3; uint32_t error = 0; HAL_FLASHEx_Erase(&erase, &error); /* 编程数据 */ for (uint32_t i = 0; i < len; i += 4) { HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, addr + i, data[i/4]); while (FLASH_WaitForLastOperation(100) != HAL_OK); } } ``` 注意:H7的编程以256位(32字节)为最小单位,数据需对齐。 ### 4. 执行固件切换 ```c void SwitchFirmware(void) { /* 设置切换标志(例如在备份寄存器中) */ HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0xA5A5); /* 失效ART缓存和指令缓存 */ SCB_InvalidateICache(); SCB_InvalidateDCache(); __HAL_FLASH_ART_CACHE_RESET(); /* 触发系统复位 */ NVIC_SystemReset(); } /* 在启动代码或main早期检查标志 */ void CheckAndSwapBank(void) { if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) == 0xA5A5) { /* 清除标志 */ HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0); /* 切换Bank映射 */ FLASH_BankSwap_Enable(); // 自定义函数,设置SWAP位 /* 再次失效缓存,确保新代码加载 */ SCB_InvalidateICache(); __HAL_FLASH_ART_CACHE_RESET(); } } ``` ### 5. 完整示例:后台下载与切换 ```c int main(void) { HAL_Init(); SystemClock_Config(); /* 检查是否需要切换Bank */ CheckAndSwapBank(); /* 正常应用代码 */ while (1) { // 运行当前固件 if (OTA_RequestReceived()) { // 下载新固件到Bank2(通过UART/以太网) uint8_t buffer[1024]; uint32_t offset = 0; while (OTA_ReceiveData(buffer, sizeof(buffer))) { Flash_WriteBank2(offset, (uint32_t*)buffer, sizeof(buffer)); offset += sizeof(buffer); } // 校验固件(CRC等) if (VerifyFirmware(BANK2_BASE_ADDR)) { SwitchFirmware(); // 切换并重启 } } } } ``` ## 注意事项与最佳实践 - **缓存一致性**:切换后必须失效ART和CPU缓存,否则执行旧指令。建议在复位后立即执行失效操作。 - **中断处理**:在后台编程期间,避免中断访问Flash(如中断向量表在Bank1),否则可能因Flash忙导致延迟。可考虑将中断向量表重映射到RAM。 - **电源稳定性**:Flash编程需要较高电压,确保供电稳定,否则可能导致编程失败。 - **固件校验**:切换前务必校验新固件完整性(CRC或签名),防止变砖。 - **ART性能**:ART加速器对顺序执行代码效果显著,但跳转密集代码可能命中率低。合理布局代码可提升性能。 ## 总结 STM32H7的双Bank闪存与ART加速器组合,为OTA升级提供了工业级解决方案。通过后台编程和快速切换,系统可实现近乎零停机的固件更新,且ART缓存管理确保了切换后性能无损。掌握这一技术,你的产品将具备更强大的远程维护能力。