嵌入式固件更新应全量擦除重刷还是支持分模块部分更新?
嵌入式固件更新的行业通用做法
首先明确两类最常见的更新逻辑:
- 绝大多数量产场景、售后现场更新场景下,行业默认主流方案是刷入包含全部功能的完整
.hex/.bin文件,更新时不会整片擦除MCU Flash,只会擦除预先划分给应用程序的Flash区间,bootloader、出厂校准参数、用户持久化配置这些存放在独立分区的内容会完整保留。 - 调试阶段用JTAG/SWD连接IDE开发时,调试器支持按改动的地址范围只擦写对应扇区,不用每次刷全量包,但这属于开发调试的便利功能,不会用于正式的固件发布。
全量更新成为主流的核心原因非常现实:
- 实现成本极低:bootloader不需要做复杂的差分解算、地址重定位、依赖校验,逻辑越简单出故障的概率越低,量产和售后维护的成本最小。
- 风险可控:嵌入式C代码编译后,所有函数、全局变量的地址都是链接阶段全局分配的,只要任意一个模块的代码长度改动,后续所有符号的地址都会偏移,全量更新可以完全避免地址不匹配、模块版本不兼容导致的程序跑飞问题。
- 效率足够:目前主流MCU的应用固件大小多在几百KB到数MB级别,不管是串口、CAN还是JTAG接口,刷写全量包的耗时也就几秒到几十秒,绝大多数场景下根本没有感知上的效率瓶颈。
独立模块分包刷写的实现条件
你提到的按功能模块拆分独立.hex/.bin、单独刷写的模式完全可行,但不属于通用默认方案,必须提前针对需求做专门的固件架构设计,普通的编译流程没法直接拆出可独立运行的模块包。目前行业里落地的分包刷写主要有三类实现路径:
- 固定地址分区方案:提前把Flash划分为多个大小固定、起始地址固定的独立分区,每个功能模块单独编译、单独指定链接地址,模块之间禁止直接跨区调用函数,只能通过预先放在固定地址的接口函数表、全局消息结构体做交互。这种方案最适配你提到的多模块版本组合测试场景,每个模块可以单独编译生成对应分区的镜像,刷写时只擦除对应分区即可,完全不影响其他模块的运行。缺点是Flash空间利用率低,每个分区要提前留足冗余空间应对后续版本的代码体积增长,模块间交互的性能开销比直接函数调用高。
- 差分增量更新方案:主要面向NB-IoT、LoRa这类低带宽远程更新场景,方案逻辑不是按功能模块拆包,而是对比新旧两个全量固件的二进制差异生成体积很小的差分包,设备端收到差分包后,在本地将旧固件和差分包合并为完整的新固件再写入应用区。这种方案能大幅降低传输的数据量,但最终写入Flash的还是完整的新固件,没法实现不同功能模块的版本自由组合。
- 动态链接加载方案:类似桌面系统的动态链接库机制,需要MCU搭载RTOS或嵌入式Linux系统,支持位置无关代码编译,功能模块被编译为独立的可重入镜像,存放在文件系统或外部Flash中,主程序运行时按需加载到RAM执行。这种方案灵活度最高,模块可以随时单独替换更新,但对MCU的RAM容量、MMU/MPU配置要求很高,资源受限的通用MCU基本不会采用,一般只在高性能嵌入式处理器上落地。
场景化实践建议
针对你提到的多模块版本组合测试需求,可以参考行业的常规选型逻辑:
如果是内部测试阶段追求刷写效率、需要快速切换不同模块的版本组合,优先选择固定地址分区方案,提前约定好各模块的交互接口和分区大小,即可实现单模块独立编译、独立刷写,不用每次烧写全量包。
如果是面向量产用户发布的正式固件,除非有低带宽远程更新的强需求,否则直接选用全量更新方案是性价比最高、风险最低的选择,没必要为了理论上的刷写速度提升搭建复杂的分包更新架构,后续版本迭代的维护隐形成本会非常高。
内容的提问来源于stack exchange,提问作者Nathan
相关产品推荐
相关产品推荐

