MCU 固件升级是量产后最容易被低估的环节。手机升级失败能进 Recovery,MCU 升级失败变砖,召回成本远高于前期那点设计时间。在 Flash 只有几百 KB、RAM 几十 KB 的资源受限设备上做 OTA,约束比 Linux 设备苛刻得多。
本文讲一套适用于这类 MCU 的 OTA 架构:双 Bank 分区、差分升级、签名校验、断点续传,以及失效保护。不含具体芯片型号,只给可移植的工程思路。
一、为什么 MCU OTA 比手机难
- 存储极小:放不下完整备份就谈不上 A/B,必须精打细算分区。
- 断电离线:设备可能在地下室、田间、设备舱里,网络随时断。
- 无用户干预:没人会去按复位键救砖,一切要靠 bootloader 自愈。
- 资源紧张:解压、验签、接收缓冲都要在有限 RAM 里完成。
这四条决定了 MCU 的 OTA 必须把「失败可恢复」当成一等公民,而不是事后补丁。
二、分区布局
最稳的结构是双 Bank + 独立的 OTA 暂存区与配置区:
|------------|------------------|----------------|
| 区域 | 作用 | 备注 |
| Bootloader | 启动校验、选 Bank、失效回滚 | 只读,极少升级 |
| App A | 当前运行固件 | 出厂烧录 |
| App B | 备用固件 / 升级目标 | 接收新版本 |
| OTA 暂存 | 接收升级包分片 | 可直接复用 App B 空间 |
| 配置区 | 启动计数、版本号、校验标志 | 小块独立扇区 |
双 Bank 的核心好处:新固件写坏也不影响旧固件,bootloader 检测启动失败即可回退。
三、差分升级:省的是带宽也是存储
全量升级每次都传整包,在 NB-IoT 这类窄带链路和按流量计费的蜂窝上极不经济。差分升级只传新旧固件的「差异块」:
- 用 bsdiff 类算法在 PC 端生成补丁;
- 设备端用 bspatch 在 RAM 受限条件下做原地合并;
- 合并目标直接写入 App B。
差分能把升级包体积压到全量的 5%~30%,对低带宽、计费链路价值明显。代价是设备端需要一块足够容纳「旧固件 + 补丁 + 合并缓冲」的临时空间,分区时要预留。
四、签名校验:不验签等于开门揖盗
任何可联网升级的设备,固件包必须验签,否则中间人替换一个恶意包即可控设备。
标准做法是:
公钥烧在 bootloader 里、不联网下发,私钥离线保管。这样即便传输被截获,没有私钥签不出合法包。
五、断点续传:网络随时会断
窄带、移动、弱网环境下,一次升级传一半断线是常态。把升级包按固定大小分片,每片带偏移与哈希:
- 设备用 HTTP
Range请求指定偏移续传,不必从头再来;
- 每片接收后先算哈希,与包头声明的分片哈希比对,错片重传;
- 已落盘分片用「分片位图」记录,重启后从位图第一个 0 继续;
- 全部分片收齐、整包验签通过后,才允许 bootloader 切换 Bank。
分片位图存配置区,断电不丢进度,是弱网设备 OTA 的刚需。
六、回滚与失效保护
即便验签通过,新固件也可能运行期崩溃。加一道启动看门狗:
- bootloader 切到 App B 后,新固件需在 N 次启动内「上报成功运行」;
- 用配置区的启动计数器:每次启动 +1,App 正常运行后清零;
- 若计数超阈值(如连续 3 次未上报),bootloader 判定新固件失效,回退 App A;
- 看门狗硬件复位保证即使固件卡死也能回到 bootloader。
这一套让「升级后变砖」从可能事件变成极小概率事件。
七、最小可行实现清单
立项时把这七项逐项打勾,基本能覆盖量产风险:
八、小结
MCU OTA 的本质不是「把新固件写进去」,而是「在任何失败路径下都能回到可运行态」。分区、验签、续传、回滚四件套缺一不可。前期多花两天把这套架构钉死,量产后能省下无数次现场救砖与一次潜在召回。
如果你正在做的设备 Flash 特别紧(比如连双 Bank 都放不下),欢迎在评论区说一下 Flash 与 RAM 量级,可以一起讨论单 Bank + 外挂 SPI Flash 的折中方案。