# STM32H7双Bank闪存并行编程:利用ART加速实现OTA零等待切换 ## 1. 双Bank架构与ART加速原理 STM32H7系列(如STM32H743/750)内置高达2MB的双Bank闪存,每个Bank独立可擦写。传统单Bank方案在OTA时需暂停应用,而双Bank允许在Bank1运行旧固件的同时,对Bank2写入新固件。关键在于**ART(Adaptive Real-Time)加速器**,它通过指令缓存和预取机制,消除CPU访问闪存的等待状态,使代码执行速度接近零等待。 ART的核心优势: - **指令缓存**:缓存最近执行的指令,减少闪存访问次数。 - **预取策略**:智能预取分支目标,提高缓存命中率。 - **双Bank支持**:当切换Bank时,ART自动刷新缓存,确保新代码一致性。 ## 2. 双Bank并行编程硬件机制 STM32H7的闪存接口支持**RWW(Read-While-Write)**特性,即一个Bank在编程/擦除时,另一个Bank可正常读取。这为OTA提供了硬件基础: - **Bank1**:运行当前固件(旧版本)。 - **Bank2**:接收新固件写入。 并行编程的时序要求: - 编程操作需通过FLASH控制器寄存器触发,且**不能中断**,但CPU可继续从另一个Bank取指。 - 擦除操作按扇区进行,H7的扇区大小可配置(8KB/16KB/32KB/128KB),需合理规划。 ## 3. 配置步骤:从零到双Bank OTA ### 3.1 内存布局规划 在链接脚本中定义两个Bank的起始地址: ```c /* STM32H743 2MB Flash */ #define BANK1_BASE 0x08000000 #define BANK2_BASE 0x08100000 #define BANK1_SIZE 0x100000 // 1MB #define BANK2_SIZE 0x100000 ``` ### 3.2 初始化Flash控制器 ```c void Flash_Init(void) { // 解锁Flash HAL_FLASH_Unlock(); // 设置编程延迟(根据主频调整) FLASH_ACR_LATENCY = FLASH_LATENCY_7; // 480MHz时需7个等待周期 // 使能ART加速器 FLASH_ACR_ARTEN = 1; // 使能预取 FLASH_ACR_PRFTEN = 1; HAL_FLASH_Lock(); } ``` ### 3.3 双Bank并行编程函数 ```c void Flash_WriteBank2(uint32_t addr, uint32_t *data, uint32_t len) { HAL_FLASH_Unlock(); // 确保目标地址在Bank2范围 if (addr < BANK2_BASE || addr >= (BANK2_BASE + BANK2_SIZE)) return; // 擦除目标扇区(以128KB扇区为例) FLASH_EraseInitTypeDef erase = {0}; erase.TypeErase = FLASH_TYPEERASE_SECTORS; erase.Banks = FLASH_BANK_2; erase.Sector = (addr - BANK2_BASE) / 128KB; erase.NbSectors = 1; uint32_t error = 0; HAL_FLASHEx_Erase(&erase, &error); // 编程数据(每次64位) for (uint32_t i = 0; i < len; i += 2) { uint64_t val = data[i] | ((uint64_t)data[i+1] << 32); HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, addr + i, val); } HAL_FLASH_Lock(); } ``` ### 3.4 Bank切换与ART缓存刷新 ```c void SwitchToBank2(void) { // 禁用全局中断,防止切换期间干扰 __disable_irq(); // 设置启动Bank为Bank2 FLASH_OPTCR_BOR_LEV = 0; // 清除选项字节 FLASH_OPTCR_BOR_LEV = 0x2; // 设置BOR级别(示例) // 关键:切换Bank后,ART缓存失效,需重置 SCB_InvalidateICache(); // 重新定位向量表 SCB->VTOR = BANK2_BASE; // 跳转到新固件复位向量 uint32_t msp = *(uint32_t*)BANK2_BASE; uint32_t reset = *(uint32_t*)(BANK2_BASE + 4); __set_MSP(msp); ((void(*)(void))reset)(); } ``` ## 4. 完整示例:OTA升级流程 ```c int main(void) { HAL_Init(); Flash_Init(); // 检查是否有新固件(例如通过UART接收) if (OTA_CheckNewFirmware()) { uint32_t new_fw[1024]; // 假设固件大小4KB OTA_ReceiveFirmware(new_fw, sizeof(new_fw)); // 写入Bank2 Flash_WriteBank2(BANK2_BASE, new_fw, sizeof(new_fw)/4); // 验证校验和 if (OTA_VerifyCRC(BANK2_BASE, sizeof(new_fw))) { // 切换Bank SwitchToBank2(); } } // 正常运行旧固件 while(1) { // 应用逻辑 } } ``` ## 5. 注意事项与优化技巧 - **ART缓存一致性**:切换Bank后,必须刷新指令缓存(`SCB_InvalidateICache()`),否则可能执行旧指令。 - **中断处理**:在编程Bank2时,确保中断服务函数位于Bank1且不访问Bank2,否则可能触发总线错误。 - **电源稳定性**:并行编程时电流需求增大,需确保供电充足,否则可能导致编程失败。 - **擦除时间**:H7擦除128KB扇区约需1秒,期间可继续运行,但需避免长时间阻塞。 - **使用DMA**:可通过DMA从外设(如UART/SPI)直接写入Flash,减少CPU负载。 - **回滚机制**:若新固件启动失败,可通过看门狗或外部信号回滚到Bank1。 ## 6. 总结 STM32H7的双Bank闪存与ART加速器为OTA提供了硬件级支持,通过合理配置,开发者能实现固件无缝升级,且运行性能不受影响。本文的代码示例可直接应用于实际项目,但需根据具体硬件调整参数。掌握这些技术,将显著提升产品的可维护性与用户体验。