为什么需要双备份升级?

做嵌入式开发的朋友,十有八九都遇到过这样的场景:产品已经量产发货了,突然发现固件有个严重 bug,或者客户要求加个新功能。这时候怎么办?OTA 升级呗。但 OTA 升级本身,就是个让人又爱又恨的东西。

OTA 升级的痛点

我最早做 OTA 的时候,用的是最直接的方案——从服务器下载新固件,直接写到主存储区,然后重启。听起来很简单对吧?但实际跑起来,问题一个接一个。

  • 升级中断电:用户正在升级,啪,断电了。固件写到一半,主存储区既不是旧版本也不是新版本,是个半残品。设备变砖。
  • 传输错误:Wi-Fi 信号不好,或者蓝牙丢包,下载的固件校验不通过。重试几次还行,要是每次都卡在 99%,用户直接退货。
  • 版本回退难:新固件上线后,发现兼容性问题。想回退?对不起,旧固件已经被覆盖了。只能重新烧录。
  • 升级失败恢复成本高:设备变砖后,要么返厂,要么让用户用烧录器自己刷。这两种方式,哪个都不好受。

核心矛盾:OTA 升级的本质是“用新固件替换旧固件”。但替换过程一旦中断,设备就失去了可运行的固件。说白了,就是“把鸡蛋放在一个篮子里”。

我在一个智能家居项目里就吃过这个亏。产品卖了 2 万台,OTA 升级成功率只有 92%。剩下 8% 的设备,用户反馈“升级后无法开机”。最后排查下来,大部分是升级过程中断电导致的。那段时间,售后电话被打爆了。

双备份方案的核心思想

怎么解决这个问题?你想想看,如果设备里同时存着两份固件——一份是当前正在运行的,另一份是待升级的——那会怎样?

这就是双备份方案的核心思想:用空间换可靠性。

具体来说,Flash 里划分出两个独立的存储区:Bank A 和 Bank B。任何时候,至少有一个 Bank 里存放着完整的、可启动的固件。

我个人的习惯:把 Bank A 作为主运行区,Bank B 作为备份/升级区。这样逻辑清晰,不容易搞混。当然,你也可以反过来,看项目需求。

升级流程是这样的:

  1. 设备当前在 Bank A 上运行。
  2. 下载新固件,写入 Bank B。
  3. 校验 Bank B 的完整性(CRC、SHA256 等)。
  4. 校验通过后,设置启动标志,指向 Bank B。
  5. 重启设备,从 Bank B 启动。
  6. 如果启动成功,标记 Bank B 为“可用”。
  7. 如果启动失败,回退到 Bank A。

你看,整个过程里,Bank A 始终没有被覆盖。就算 Bank B 写坏了,设备还能从 Bank A 正常启动。用户甚至感觉不到升级失败过。

注意:双备份不是简单的“存两份”。它需要配套的启动逻辑、版本管理、回退策略。否则,两份固件互相冲突,反而更麻烦。我曾经见过一个团队,双备份做成了“双砖”——两个 Bank 都坏了,谁也启动不了。

双备份 vs 单备份:直观对比

对比项 单备份(传统 OTA) 双备份
Flash 占用 1 份固件空间 2 份固件空间
升级中断后果 设备变砖 自动回退,无影响
版本回退 不支持 支持一键回退
升级失败恢复 返厂/烧录器 自动恢复
实现复杂度 低 中
可靠性 低 高

说白了,双备份就是用多一倍的 Flash 空间,换来接近 100% 的升级可靠性。对于量产产品来说,这个 trade-off 非常划算。你想想,一台设备返厂的物流成本、人工成本,可能比 Flash 芯片本身贵多了。

双备份方案的适用场景

不是所有项目都需要双备份。我一般这样判断:

  • 必须用:远程升级、无人值守设备、医疗设备、工业控制器、车联网终端。这些场景下,设备变砖的代价极高。
  • 建议用:消费电子产品、智能家居、IoT 节点。虽然成本敏感,但用户体验更重要。
  • 可以不用:开发板、实验室原型、一次性设备。这些场景下,变砖了直接重新烧录就行。

嗯,这里要注意:如果你的 MCU Flash 空间实在紧张,连两份固件都放不下,那可以考虑“压缩升级”或者“外部 Flash”方案。但那是另一个话题了,我们后面会讲到。

双备份方案的核心逻辑图

下面这张图,展示了双备份升级的核心流程。我建议你仔细看看,后面所有章节都是围绕这个逻辑展开的。

双备份升级核心流程 当前运行 Bank A 下载固件到 Bank B 校验 Bank B 完整性 切换启动标志 → Bank B 重启,从 Bank B 启动 启动失败 → 回退 Bank A 启动成功 → 标记可用 关键点:Bank A 在整个升级过程中始终未被覆盖,确保任何时候都有可启动的固件

这张图里,最关键的其实是那个“回退”分支。我见过不少方案,只做了升级,没做回退。结果新固件有 bug,设备启动不了,又回不去旧版本。嗯,那还不如不做双备份。

双备份方案的代价

说了这么多好处,也得聊聊代价。双备份不是银弹,它有几个硬伤:

  • Flash 空间翻倍:如果你的固件是 512KB,那至少需要 1MB 的 Flash。对于小容量 MCU,这可能是个门槛。
  • 升级时间变长:下载完固件后,还要做完整性校验。校验时间取决于算法和固件大小,有时候会多花几秒钟。
  • 启动逻辑复杂:Bootloader 需要判断从哪个 Bank 启动,还要处理各种异常情况。代码量比单备份多 30%~50%。
  • 版本管理麻烦:两个 Bank 的版本号需要同步维护,不能出现“Bank A 是 v2.0,Bank B 是 v1.5”这种混乱情况。

但说实话,这些代价跟“设备变砖”比起来,都不算什么。我在一个车载项目里,客户明确要求“升级失败率必须低于 0.01%”。不用双备份,根本做不到。

一句话总结:双备份升级,就是用一份固件的空间冗余,换取 100% 的升级安全。对于量产产品,这笔投资值得做。