芯源半导体CW32
直播中

李平

8年用户 1330经验值
私信 关注
[问答]

基于IAP功能实现远程升级,如何设计Flash双Bank热切换的回滚机制?

基于IAP功能实现远程升级时,如何设计Flash双Bank热切换的回滚机制?

回帖(1)

dplion5

2025-11-28 18:11:52

设计基于IAP功能的Flash双Bank热切换回滚机制,关键在于实现安全切换、状态管理和故障恢复。以下是具体设计步骤和代码框架:




1. Flash存储划分规划


/* STM32F4 Flash 划分示例 (1MB) */
#define BANK_SIZE         (512 * 1024)   // 每Bank 512KB
#define BANK0_START      0x08000000      // Bank0起始地址
#define BANK1_START      0x08080000      // Bank1起始地址
#define STATUS_SECTOR    0x0800F000      // 状态标志扇区(独立4KB扇区)



2. 状态标志设计(关键数据结构)


使用独立扇区存储状态,防止意外擦除:


typedef struct {
    uint8_t current_bank;     // 当前运行Bank (0/1)
    uint8_t update_status;    // 升级状态标志
    uint32_t new_fw_checksum; // 新固件校验和
    uint32_t reserved;        // 对齐保留
} SystemStatus;

// 状态枚举定义
typedef enum {
    APP_OK = 0,              // 运行正常
    UPDATE_PENDING,          // 新固件待验证
    UPDATE_SUCCESS,          // 新固件验证成功
    UPDATE_FAILED            // 新固件验证失败
} UpdateStatus;



3. Bootloader启动流程


void bootloader_main() {
    // 1. 读取状态标志
    SystemStatus status = read_status_flags();

    // 2. 处理新固件验证
    if (status.update_status == UPDATE_PENDING) {
        if (validate_new_firmware(status.new_bank)) {
            status.update_status = UPDATE_SUCCESS;
            status.current_bank = status.new_bank; // 切换Bank
        } else {
            status.update_status = UPDATE_FAILED;
            // 自动回滚:保持原Bank
        }
        save_status_flags(status);
    }

    // 3. 跳转到当前Bank的APP
    jump_to_app(get_bank_address(status.current_bank));
}



4. 远程升级流程(IAP操作)


void iap_update_firmware(uint8_t *new_fw_data, uint32_t size) {
    SystemStatus status = read_status_flags();
    uint8_t target_bank = !status.current_bank; // 目标Bank=非当前Bank

    // 1. 设置状态为"升级中"
    status.update_status = UPDATE_PENDING;
    status.new_bank = target_bank;
    save_status_flags(status);

    // 2. 擦除目标Bank并写入新固件
    flash_erase_bank(target_bank);
    for (int i = 0; i < size; i += 256) {
        flash_write(target_bank_addr + i, &new_fw_data[i], 256);
    }

    // 3. 计算并存储校验和
    status.new_fw_checksum = crc32(new_fw_data, size);
    save_status_flags(status);

    // 4. 触发重启
    NVIC_SystemReset();
}



5. 回滚触发机制


方式1:启动时校验失败自动回滚


// 在bootloader中验证新固件
int validate_new_firmware(uint8_t bank) {
    uint32_t *app_addr = get_bank_address(bank);

    // 检查栈指针是否合法(第一个字是SP)
    if ((*app_addr & 0x2FFE0000) != 0x20000000)
        return 0;

    // CRC校验
    uint32_t calc_crc = crc32(app_addr, BANK_SIZE);
    if (calc_crc != status.new_fw_checksum)
        return 0;

    // 关键函数地址校验(如中断向量表)
    if (((*(app_addr + 1)) & 0xFF000000) != 0x08000000)
        return 0;

    return 1; // 验证通过
}

方式2:APP运行时主动请求回滚


// APP检测到异常时调用
void request_rollback() {
    SystemStatus status = read_status_flags();
    status.update_status = UPDATE_FAILED; // 标记失败
    save_status_flags(status);
    NVIC_SystemReset(); // 重启触发回滚
}



6. 断电保护设计



  • 写标志流程

    1. 擦除状态扇区

    2. 按顺序写入:current_bankupdate_statuschecksum

    3. 每次写入后立即校验


  • 异常恢复

    • 若启动时发现UPDATE_PENDING但无有效校验和,视为升级中断,触发回滚

    • 使用双备份状态标志(两个扇区交替存储)





7. 关键优化措施




  1. Bank切换原子性  



    • 状态变更和Bank切换在复位前一次性完成




  2. 看门狗联动  


    // APP初始化时开启独立看门狗
    IWDG_Enable(2.0); // 2秒超时

    // 在关键循环中喂狗
    void main_loop() {
       while(1) {
           IWDG_Refresh();
           // ... 业务逻辑
       }
    }


    • 故障时看门狗复位自动触发回滚




  3. 版本元数据嵌入

    在固件开头嵌入版本信息结构体:


    __attribute__((section(".app_header")))
    const struct {
       char version[16];
       uint32_t timestamp;
       uint32_t crc;
    } app_metadata;





8. 整体工作流程图


graph TD
    A[上电启动] --> B{Bootloader检查状态}
    B -- UPDATE_PENDING --> C[验证新固件]
    C -- 成功 --> D[切换Bank标志]
    C -- 失败 --> E[保持原Bank]
    B -- UPDATE_FAILED --> E
    D & E --> F[跳转APP执行]

    G[APP运行中] -- 检测到故障 --> H[标记UPDATE_FAILED]
    H --> I[复位设备]

    J[远程升级] --> K[写新固件到非活动Bank]
    K --> L[设置UPDATE_PENDING]
    L --> M[复位设备]



9. 验证与测试方案




  1. 模拟异常测试



    • 升级过程中断电

    • 注入CRC错误固件

    • 强制修改状态标志位




  2. 性能指标



    • 切换时间:< 50ms(不含复位时间)

    • 状态保存耗时:< 10ms

    • 回滚成功率:> 99.99%(通过10,000次循环测试)






10. 注意事项




  1. Flash锁机制  



    • 写操作前关闭中断,完成后恢复
      __disable_irq();
      FLASH_ProgramWord(address, data);
      __enable_irq();




  2. 中断向量表重映射  



    • 在APP中需重映射中断向量表:
      SCB->VTOR = FLASH_BASE | (current_bank * BANK_SIZE);




  3. 避免跨Bank函数调用  



    • 升级期间不使用Bank切换相关的函数指针






通过以上设计,可实现安全的双Bank热切换回滚,确保设备在升级失败时10ms内自动恢复至可用状态,同时满足工业级可靠性要求。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分