一、双Bank架构与OTA零停机原理

STM32H7系列(如H743/H750)内置双Bank闪存,每个Bank容量相等(如1MB时各512KB)。通过配置选项字节DBANK,可将闪存划分为两个独立区域,支持并行编程/擦除无缝切换

零停机核心机制:

  • 系统从Bank1运行,同时可对Bank2进行擦写(无需暂停应用)。
  • 升级完成后,通过置位FLASH_CRSWAP_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切换配置

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(非活动区)

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 回滚机制实现

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跳转逻辑

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+)。