一、固件升级系统概述

1.1 什么是固件升级

固件升级,说白了就是把设备里跑的程序换一版新的。你想想看,手机、路由器、智能家电,这些嵌入式设备出厂时烧录的代码,总会有bug要修、功能要加、性能要调。这时候就需要升级。

我刚开始做嵌入式那会儿,升级还得靠串口线或者JTAG。拿根线连上电脑,打开烧录工具,等个几分钟。要是现场有几十台设备,那就得一台一台插线操作,累得够呛。后来OTA(Over-The-Air)升级普及了,才算是解放了双手。

固件升级的本质,其实就三件事:

  • 获取新固件 —— 从服务器、本地存储或者外部介质拿到新的二进制文件
  • 写入存储 —— 把新固件写到Flash、eMMC或者SD卡里
  • 切换运行 —— 让设备从旧固件切换到新固件,正常跑起来

核心要点:固件升级不是简单的文件拷贝。它要保证设备在升级过程中断电、断网、写错数据后,还能恢复回来。这才是真正的难点。

1.2 OTA升级原理

OTA升级,就是通过网络远程给设备换固件。它的工作流程大致是这样的:

  1. 设备联网 —— 通过Wi-Fi、蜂窝网络、以太网等方式连接到升级服务器
  2. 版本检查 —— 设备上报当前固件版本,服务器判断是否需要升级
  3. 下载固件 —— 如果有新版本,设备下载固件包(可能是全量包,也可能是差分包)
  4. 校验完整性 —— 下载完成后,用MD5、SHA256或者签名验证固件有没有被篡改
  5. 写入备用分区 —— 把新固件写到Flash的备用区域,不覆盖当前运行的系统
  6. 切换启动 —— 重启设备,Bootloader检测到新固件,加载运行
  7. 回滚机制 —— 如果新固件启动失败,自动切回旧版本

这里我特别想强调一下「双分区」的设计。我在一个智能家居项目里吃过亏——当时为了省Flash空间,只用了单分区,升级时直接覆盖。结果有一次下载到一半断电了,设备直接变砖。后来老老实实加了双分区,再也没出过这种问题。

我的经验:OTA升级最怕的不是下载慢,而是下载完成后写Flash时断电。所以一定要保证写入操作的原子性——要么全写成功,要么一点不改。

下面这张图,把OTA升级的完整流程画出来了:

OTA升级流程图 OTA升级完整流程 设备联网 版本检查 下载固件 校验完整性 校验通过? 失败,重新下载 写入备用分区 切换启动 启动失败自动回滚

1.3 固件升级的挑战与解决方案

做了这么多年固件升级,我踩过的坑真不少。总结下来,核心挑战就这几个:

挑战 风险 解决方案
升级中断 设备变砖,无法恢复 双分区 + 原子写入 + Bootloader回滚
固件损坏 运行异常,功能失效 CRC/MD5/SHA256校验 + 数字签名验证
存储空间不足 无法下载或写入新固件 差分升级(Delta OTA)+ 压缩传输
网络不稳定 下载失败,反复重试 断点续传 + 分片下载 + 超时重试机制
版本兼容性 新固件与旧配置不匹配 版本号管理 + 配置迁移脚本 + 兼容性检查
安全攻击 固件被篡改,植入恶意代码 HTTPS传输 + 签名验证 + 安全启动链

⚠️ 我曾经踩过的坑:有一次做智能门锁的OTA升级,没做签名验证。结果有人抓包替换了固件,门锁被远程开了。从那以后,我所有项目的固件升级都必须过签名校验,哪怕只是内部测试版本。

1.4 双分区升级架构详解

双分区(A/B分区)是目前最可靠的升级方案。它的核心思想是:Flash里放两份固件,一份在跑,一份备用。

具体来说:

  • 分区A —— 当前运行的系统
  • 分区B —— 备用分区,用来写入新固件
  • Bootloader —— 启动时判断该从哪个分区加载
  • 元数据区 —— 记录当前活跃分区、升级状态、回滚次数

升级时,新固件写入备用分区,不碰正在运行的分区。写完后,Bootloader在下次启动时尝试加载新分区。如果启动成功,就标记为活跃分区;如果启动失败,就切回旧分区。

我的建议:双分区虽然多占一倍的Flash空间,但换来的是「永不砖机」的可靠性。对于量产产品来说,这点成本完全值得。我经手的项目里,凡是用了双分区的,从来没有因为升级出过批量事故。

下面这张图展示了双分区的布局:

双分区Flash布局 双分区Flash布局 Bootloader(启动加载器) 分区A(当前运行) 固件版本 v2.1.0 分区B(备用/升级目标) 固件版本 v2.2.0(新) 元数据区(活跃标记 + 回滚计数) 升级时写入分区B → 重启尝试加载分区B → 成功则标记活跃,失败则回滚到分区A

1.5 升级策略的选择

不同的产品,升级策略也不一样。我整理了几种常见方案:

策略 适用场景 优点 缺点
全量升级 存储空间充足,网络带宽好 实现简单,兼容性好 下载量大,耗时较长
差分升级 存储空间有限,带宽受限 下载量小,升级快 需要差分算法,版本关联性强
压缩升级 中等存储,中等带宽 比全量小,比差分简单 解压占用CPU和内存
流式升级 实时性要求高,边下边写 无需额外存储空间 中断风险高,恢复复杂

我的选择建议:如果是量产产品,我首选「全量升级 + 双分区」。虽然下载量大一点,但胜在稳定可靠。差分升级虽然省流量,但版本之间的依赖关系容易出问题——我曾经遇到过差分补丁打错了,导致设备反复重启的惨案。

1.6 安全升级的底线

安全这块,我吃过亏,所以特别重视。固件升级的安全底线至少要做到:

  • 传输加密 —— 用HTTPS或者TLS,别用明文HTTP
  • 签名验证 —— 固件包必须带数字签名,设备端验签后再写入
  • 防回滚 —— 防止攻击者刷回有漏洞的旧版本
  • 安全启动 —— Bootloader验证固件签名,签名不对就不加载

⚠️ 血的教训:我早期做的一个IoT项目,固件升级只做了CRC校验,没做签名。结果产品上市后被黑客破解,批量刷入了挖矿程序。那次事故让我明白——安全不是可选项,是必选项。

好了,这一章的内容就到这里。固件升级看起来简单,但真正要做好,涉及的东西不少。后面的章节,我会一步步带你从零搭建一套完整的固件升级系统。