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的起始地址:

/* STM32H743 2MB Flash */
#define BANK1_BASE 0x08000000
#define BANK2_BASE 0x08100000
#define BANK1_SIZE 0x100000  // 1MB
#define BANK2_SIZE 0x100000

3.2 初始化Flash控制器

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并行编程函数

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缓存刷新

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升级流程

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提供了硬件级支持,通过合理配置,开发者能实现固件无缝升级,且运行性能不受影响。本文的代码示例可直接应用于实际项目,但需根据具体硬件调整参数。掌握这些技术,将显著提升产品的可维护性与用户体验。