STM32平台下Delta固件升级的调度与新固件构建方案咨询
STM32 差分OTA固件升级实现方案
1. 前置Flash分区规划
这是整个方案的运行基础,需按STM32的Flash页大小对齐划分:
- Bootloader区:占Flash前16~32KB,固定不擦除,负责补丁校验、固件重组、跳转逻辑
- 旧固件区:存放当前正在运行的完整基准固件,也就是生成差分补丁的比对基准
- 补丁存储区:存放接收到的jDiff生成的补丁包,大小按固件最大差值的1.2倍预留即可,比如256KB的固件预留64KB补丁区足够覆盖绝大多数场景
- 新固件暂存区:存放重组完成的新固件,校验通过后再覆盖旧固件区,避免升级中途掉电变砖
如果Flash空间极度紧张,也可以取消单独的新固件暂存区,用原地升级逻辑,将旧固件的不变段先备份到SRAM/备份Flash再写入新内容,但掉电风险较高,不推荐
2. 补丁调度逻辑实现
所有调度逻辑放在Bootloader中实现,上电后按顺序执行:
- 上电先启动Bootloader,读取补丁存储区的专属标志位,判断是否存在完整的待处理补丁
- 无待升级补丁时,直接跳转到旧固件区运行正常业务
- 有待升级补丁时,先校验补丁包完整性(比如提前写入的CRC32校验值),校验失败直接清空补丁标志、跳转旧固件;校验通过则进入固件重组流程
3. 新固件重组逻辑(适配jDiff补丁包)
首先在Windows端导出jDiff补丁包的格式规则,jDiff的标准输出包含三个核心信息块:不变段偏移、新增段内容、替换段偏移+内容,STM32端只需实现对应解析逻辑即可,参考伪代码如下:
typedef struct { uint32_t patch_total_len; uint32_t old_fw_crc; uint32_t new_fw_crc; uint32_t new_fw_size; } patch_header_t; #define OP_COPY 0x01 // 不变段,从旧固件拷贝 #define OP_INSERT 0x02 // 新增/替换段,从补丁包取内容 void patch_reconstruct(uint32_t old_fw_addr, uint32_t patch_addr, uint32_t new_fw_addr) { // 读取补丁头信息 patch_header_t header = *(patch_header_t *)patch_addr; // 校验当前旧固件是否和补丁匹配,避免刷错 if(crc32((uint8_t *)old_fw_addr, OLD_FW_SIZE) != header.old_fw_crc) { return; } uint32_t patch_offset = sizeof(patch_header_t); uint32_t new_fw_offset = 0; while(patch_offset < header.patch_total_len) { uint8_t op_type = *(uint8_t *)(patch_addr + patch_offset); patch_offset += 1; if(op_type == OP_COPY) { uint32_t old_offset = *(uint32_t *)(patch_addr + patch_offset); uint32_t len = *(uint32_t *)(patch_addr + patch_offset + 4); memcpy((void *)(new_fw_addr + new_fw_offset), (void *)(old_fw_addr + old_offset), len); new_fw_offset += len; patch_offset += 8; } else if(op_type == OP_INSERT) { uint32_t len = *(uint32_t *)(patch_addr + patch_offset); memcpy((void *)(new_fw_addr + new_fw_offset), (void *)(patch_addr + patch_offset + 4), len); new_fw_offset += len; patch_offset += 4 + len; } } // 重组完成后校验新固件是否正确 if(crc32((uint8_t *)new_fw_addr, header.new_fw_size) == header.new_fw_crc) { set_patch_success_flag(); // 标记重组成功 } }
4. 升级后跳转逻辑
重组校验通过后可选择两种升级模式:
- 覆盖升级:将新固件暂存区的内容逐页擦除写入旧固件区,写完后清空补丁标志,直接跳转旧固件地址运行
- 免覆盖切换:将中断向量表重定向到新固件暂存区地址,直接跳转到新固件运行,下次启动再判断是否需要覆盖旧固件
注意事项
- 所有Flash擦写操作都要加步骤标志位,中途掉电后下次上电可继续执行对应步骤,避免变砖
- 补丁包传输时分包加校验,每包128/256字节,收一包校验一包,错包直接重传,避免整包传输完成才发现错误
- Bootloader区域要加写保护,避免误擦除导致只能用烧录器救砖
内容的提问来源于stack exchange,提问作者Amin-nano-sys
相关产品推荐
相关产品推荐

