固件OTA升级全流程

从嵌入式工程师的角度讲清 MCU 固件 OTA 升级全流程:Flash 分区怎么划、固件包头放什么、版本检查、分片传输与写入、完整性校验和签名、切换标志、启动自检与回滚、每个阶段掉电会怎样,以及升级结果怎么上报。

多爱普团队11 分钟阅读
固件OTA升级全流程
本文目录
  1. 全流程一览
  2. Flash 分区:先保证有退路
  3. 固件包头:设备自己判断能不能装
  4. 版本检查
  5. 传输:分片、确认、续传
  6. 校验和签名
  7. 切换和重启
  8. 启动确认和回滚
  9. 每个阶段掉电会怎样
  10. 上报:以设备回读为准
  11. 差分升级要不要做
  12. 常见问题
  13. OTA 设备侧和 App / 平台侧怎么分工?
  14. 没有外部 Flash,空间只够一个应用怎么办?
  15. 升级到一半用户关了 App 怎么办?
  16. 你们能做 OTA 的 App 和平台部分吗?

"OTA 有什么难的,把新固件发过去写进 Flash 不就行了?"

第一次做 OTA 的工程师常这么想,直到某次升级中途断电,设备变砖,得拆机用烧录器救。或者一批设备升级后启动不了,只能一台台返厂。

OTA 真正难的不是传输,而是保证任何一步出问题,设备都还能回到可用状态。这篇按升级的时间顺序,把设备侧和 App / 平台侧要做的事完整走一遍。平台侧的任务管理、灰度和日志,可以另看 设备OTA升级平台怎么做。

全流程一览

1. 打包    固件 + 包头(型号、版本、长度、CRC、签名)
2. 检查    App / 平台读取设备当前版本和型号,判断是否需要升级
3. 准备    设备进入升级模式,擦除下载区
4. 传输    分片发送,逐片或分批确认
5. 校验    整包 CRC / 哈希,必要时验签
6. 切换    写升级标志,重启
7. 启动    Bootloader 检查新固件,跳转运行
8. 确认    新固件自检通过,标记为有效;失败则回滚
9. 上报    设备上报新版本号,App / 平台以此判定成功

Flash 分区:先保证有退路

最常见的三种布局:

布局结构优点代价
A/B 双区Bootloader + A 区 + B 区升级失败直接回到旧固件Flash 要两倍应用空间
下载区 + 搬运Bootloader + 应用区 + 下载区应用区可以大一些搬运过程掉电要能恢复
单区 + Bootloader 接收Bootloader + 应用区最省空间升级中失败只能留在 Bootloader 重新接收

不管哪种,Bootloader 本身不参与升级,任何时候都能运行,这是最后一道保险。资源实在紧张、只能单区的,至少保证升级失败后停在 Bootloader 里等待重新升级,而不是跳进一个半截的固件。

固件包头:设备自己判断能不能装

不要只发裸的 bin 文件。在固件前面加一个包头,设备收到后先检查:

typedef struct {
    uint32_t magic;        // 固定标识,判断是不是合法的升级包
    uint16_t hw_model;     // 硬件型号,防止装错型号
    uint16_t hw_rev_min;   // 支持的最低硬件版本
    uint32_t fw_version;   // 新固件版本号
    uint32_t fw_size;      // 固件长度
    uint32_t fw_crc32;     // 固件 CRC32
    uint8_t  sign[64];     // 签名(可选,ECDSA 等)
} ota_header_t;

型号不匹配直接拒绝。我们的 BMS 项目里电池、仪表和 4G 模组三类固件,升级地址和流程都不一样,包头里的型号信息就是防止"把仪表固件发给电池"的第一道检查。

版本检查

升级前 App 或平台先读设备的型号、硬件版本和当前固件版本:

  • 新版本是否高于当前版本(是否允许降级要单独约定)
  • 硬件版本是否在新固件支持范围内
  • 设备电量、运行状态是否允许升级(比如电机运行中不允许升级)

这些条件不满足,就不要开始,提示用户原因。

传输:分片、确认、续传

固件按片发送,每片带序号或偏移地址。设备收到后写入 Flash 并回确认,确认里带上"下一片要哪个",这样丢片、重复都能处理。

  • 片的大小取决于通道:蓝牙受 MTU 限制,我们的 BMS 蓝牙 OTA 用 64 字节;储能 BMS 走 Modbus 透传用 1KB
  • 单片超时重试,累计失败超过上限就中止,并记录停在哪一片
  • 设备记住已接收的偏移,断开后重新连接可以从断点继续

写 Flash 要注意:大多数 Flash 必须先按扇区擦除才能写入,擦除比写入慢得多。可以在准备阶段一次擦完下载区,也可以边收边擦,但要把擦除耗时算进确认超时里。

不同通道的具体做法,见 蓝牙OTA升级怎么实现 和 4G设备远程固件升级怎么做。

校验和签名

收完以后,对下载区整包算 CRC32 或 SHA-256,和包头对比:

  • CRC / 哈希:保证数据完整,没有传错
  • 签名:保证来源可信,防止有人伪造固件刷进设备。设备里只存公钥,私钥留在出包的服务器上

对外销售、能被用户接触到升级入口的产品,建议做签名校验。

切换和重启

校验通过后,写一个升级标志(放在单独的 Flash 扇区或者备份寄存器里),然后重启。Bootloader 读到标志:

  • A/B 双区:把启动目标切到新区
  • 下载区方案:把下载区搬到应用区,搬完清除标志

搬运过程是最危险的阶段。要记录搬运进度,掉电重启后 Bootloader 能从中断的位置继续搬,而不是跳进一个只搬了一半的应用。

启动确认和回滚

新固件能启动,不代表能正常工作。比较稳妥的做法是"试运行 + 确认":

  1. Bootloader 第一次启动新固件时,把它标记为"试运行",并记一次启动计数
  2. 新固件启动后做自检:外设初始化、关键参数读取、通讯模组联网
  3. 自检通过,新固件自己把状态改成"有效"
  4. 如果新固件反复重启、没能完成确认,Bootloader 发现启动计数超限,回滚到旧固件

开源的 MCUboot 就是类似的 test / confirm 机制,可以参考它的设计。

每个阶段掉电会怎样

设计完以后,按阶段过一遍掉电:

阶段掉电后要求
传输中旧固件正常运行下载区数据作废或断点续传
校验中旧固件正常运行重新校验或重新下载
写标志标志写一半标志要有校验,无效标志按"不升级"处理
搬运中应用区不完整Bootloader 按进度续搬
新固件试运行未确认按启动计数回滚

上线前,每个阶段都要真机断电测试。

上报:以设备回读为准

升级完成的判定,不是"最后一片发送成功",而是设备重启后上报的版本号等于目标版本。我们的储能 BMS 远程升级平台,升级完成后会自动回读版本号,确认设备真的跑在新固件上。参考 储能 BMS 远程固件升级平台。

差分升级要不要做

差分升级只传新旧固件的差异,能大幅减少传输量,对 4G 流量和蓝牙升级时间都有帮助。代价是设备端要能还原(需要额外的 RAM 和 Flash),出包时也要针对每个旧版本生成差分包。固件不大、升级不频繁的项目,整包升级更简单可靠。

常见问题

OTA 设备侧和 App / 平台侧怎么分工?

一般是:Bootloader、分区、写入、校验、回滚由嵌入式工程师实现;App 和平台负责版本检查、分片发送、重试、进度展示和升级记录。双方要共同定一份升级协议。

没有外部 Flash,空间只够一个应用怎么办?

可以用"单区 + Bootloader 接收"的方案:升级时跳回 Bootloader,由 Bootloader 直接接收新固件写入应用区。失败了停在 Bootloader 等待重试,不会变砖,只是升级期间设备不能正常工作。

升级到一半用户关了 App 怎么办?

旧固件还在运行(双区或下载区方案),下次打开 App 可以从断点继续,或者重新开始。

你们能做 OTA 的 App 和平台部分吗?

可以,参考 OTA 升级平台开发。如果设备侧还没设计,也可以 联系我们 一起定升级协议。

项目咨询

把项目情况发过来,先帮你理清下一步

发来设备类型、协议资料、样机状态和计划上线时间,我们会结合对应的服务和案例给出推进建议。

如果你暂时不方便填写完整信息,也可以直接前往 联系页面 获取电话、邮箱与微信方式。

提交项目需求

适用于 固件OTA升级全流程 场景,提交后默认进入业务咨询通道。

微信、电话、邮箱至少填写一项

点击发送即表示您同意我们的隐私政策和服务条款