对于车载控制器这类已经下线并完成封装的产品,无论是在供应商还是客户手中进行测试,通常只能通过控制器内置的客户引导程序来刷新应用程序。如果需要更新引导程序本身,就必须借助一些特殊方法。
规范与背景
整车厂的规范通常只涉及应用程序的刷新,而没有针对引导程序自更新的规范。这是因为规范面向量产车,售后环节只负责应用程序的升级,不涉及(也不允许)对引导程序的升级。
因此,引导程序的自更新只存在于项目开发阶段,并由供应商自行设计方案。本文将详细分析五种引导程序自更新方式的原理、优势与不足。
方式一:依赖二级启动程序更新
在某些软件架构中,存在两级引导程序:启动引导程序 和客户引导程序。其中, 仅负责检查CPU最小系统,与具体项目的外围电路无关。它独立于客户需求,由供应商自行维护。
程序启动顺序是: -> -> 应用程序。因此,可以在 中增加刷新逻辑来更新 。通常情况下, 运行以更新应用程序;在特殊情况下,程序启动后可以一直停留在 中,执行对 的更新。
优点:
- 逻辑结构简单清晰,软件分工明确。
- 一次性刷新完成,操作简便。
缺点:
- 需要在 中占用较大的Flash空间来存放刷新逻辑。项目量产后又要禁止此功能,造成资源浪费。
- 软件分为三级启动,结构复杂,开发和维护成本较高。对于不需要 的控制器是一种负担。
- 如果 自身也需要更新怎么办?按照此策略,难道还需要做一个 ?显然不现实。

方式二:借助RAM运行重启程序进行更新
在没有 的情况下,程序启动顺序是 -> 应用程序。当需要刷新 时:
- 首先将一个“重启”程序下载到闲置的RAM中。
- 然后在RAM环境中运行该“重启”程序。
- “重启”程序负责下载并更新 到Flash中。
优点:
- 不需要额外的Flash空间。“重启”程序运行仅需少量RAM,应用程序设计时预留的RAM空间可临时用来保存该程序。
- RAM擦写速度极快,因此下载“重启”程序的过程会非常迅速。
缺点:
在 更新过程中,如果CPU意外掉电,重新上电后RAM中的“重启”程序内容将全部丢失,而 区域可能已被部分擦写而损坏。这将导致程序无法正常启动,控制器“变砖”,只能开盖使用JTAG等工具重新烧写程序,涉及复杂的网络通信协议与调试流程。

方式三:RAM加载完整更新包(方式二的改进)
此方式是对方式二的改进:
- 将“重启”程序和新的 作为一个完整更新包,一同下载到RAM中。
- 运行RAM中的“重启”程序。
- 该程序擦除Flash中的旧 区域,并将RAM中完整的新 复制到Flash中。
- 最后,执行重新上电复位,RAM中的内容自动丢失,程序从新的 开始运行。
优点:
- 相比方式二,减少了一次与上位机的通信和下载步骤(因为“重启”程序和新CB已绑定在一起)。
- 相比方式二, 的更新完全在CPU内部执行,不受外部通信干扰,耗时更短,可靠性更高。
缺点:
- 相比方式二,需要更大的RAM空间来存储“重启”程序+新 。
- 与方式二相同,在 更新阶段掉电后,控制器仍有“变砖”风险。

方式四:借助应用程序的Flash空间
此方式的核心是利用应用程序占用的Flash空间作为临时操作区,更新过程分为三步:
- 运行旧的 ,擦除原应用程序区域,将“重启”程序下载到该区域。
- 运行“重启”程序,擦除旧的 ,刷入新的 。
- 运行新的 ,将原应用程序刷回其所属区域。
优点:
- 不需要额外的Flash或RAM资源,完全利用现有空间。
- 稳定可靠。通过优化设计(见后文附录),可以保证在任何步骤突然掉电后,重新上电仍能继续操作,控制器不会“刷死”。
- 对 稍加改造即可成为“重启”程序,开发速度快。
缺点:
- 步骤繁多。为更新 ,必须先擦除应用程序,最后还要恢复应用程序,至少涉及三次完整的刷新操作。对不熟悉流程的操作者容易造成混乱。
- 整体刷新时间较长,因为需要完成两次 和一次应用程序的烧写。

方式五:借助额外的独立Flash空间
相比方式四,此方式需要一块与 大小相当的额外独立Flash块(Block),更新分三步:
- 运行旧的 ,将“重启”程序刷入额外Flash块。
- 运行“重启”程序,更新Flash中的 。
- 运行新的 ,破坏/擦除额外Flash块中的“重启”程序(例如擦除其有效性标志)。
优点: 相比方式四,无需破坏和恢复应用程序,省去了这部分操作时间。
缺点: 相比方式四,需要额外的独立Flash空间,对硬件资源有要求。

方案选择小结
从本质上,上述五种方法可归纳为三种思路:
- 依赖启动程序:当CPU的Flash资源非常充裕,且项目本身就需要两级 架构时,采用方式一最节省开发与操作时间。
- 借助RAM:在仅需单级 ,且可以接受因 刷新失败导致控制器“变砖”并需要开盖维修所带来的时间、人力、物料成本损耗时,方式二或方式三较为方便快捷。
- 借助Flash:在仅需单级 ,且不允许或不方便对控制器进行开盖操作,但可以接受更新步骤较多、耗时较长的情况下,方式四或方式五最为稳定可靠。
综上,工程师需要根据产品的整体软件架构、CPU资源情况、以及时间、人力、物料等成本因素,综合考虑选择一种最适合自身项目和产品的自更新方法。
附录:方式四防“变砖”机制的具体实现
方式四“不会刷死”的结论是有前提条件的,即控制器中至少有一个可用的引导程序(即使是部分损坏的)。这里引出一个核心问题:CPU如何判断哪个引导程序是好的,哪个是坏的?
背景:两级启动地址
许多支持Bootloader的单片机(如一些汽车级MCU)设计有两个或更多的启动地址。上电后,CPU按预定顺序检查这些启动地址对应扇区的有效性。如果BOOT_ID等标志合法,则从该地址开始执行;否则检查下一个启动地址。

考虑CPU具备两个启动地址的情况:
- 场景A:仅启动地址1有效(应用程序区域为空),程序启动后自动进入 。
- 场景B:仅启动地址2有效(例如用于不带 的测试),程序启动后自动进入应用程序。
- 场景C:启动地址1和2均有效。程序优先从地址1启动,在 中检查应用程序有效后,通过跳转指令跳转到地址2,运行应用程序。

风险分析与对策
在方式四的步骤2(运行“重启”程序更新 )中,如果在擦除旧 后、写入新 完成前发生掉电,重新上电后,启动地址1的内容可能是一个不完整的 。CPU可能会尝试从这个损坏的 启动并运行出错,而不会跳转到启动地址2运行“重启”程序,从而导致无法继续更新的“变砖”状态。

对策一:Boot有效性标志与启动地址重合
充分利用Flash的擦写特性(通常按块擦除,按字/字节写入)。调整 的刷新顺序:擦除成功后,先写入 的主体代码部分,最后一步才刷新启动地址1的有效性标志。
这样,即使在更新主体代码过程中掉电,由于启动地址1的标志无效,CPU上电后会根据启动顺序,自动从有效的启动地址2(即“重启”程序)启动,从而可以继续完成 的刷新操作。当启动地址1的标志最终被成功写入后,下次上电CPU就会从新的 启动。
对策二:独立的Boot有效性标志与检查逻辑
将 分为两个段(Section)。第一个段 仅存放极少的启动自检逻辑。该逻辑会检查放置在 段末尾的一个独立有效性标志。
- 如果该标志无效,则判定 不完整,程序控制跳转到启动地址2运行“重启”程序,继续更新。
- 如果该标志有效,则判定 完整,程序跳转到 段继续执行。由于此时应用程序(即“重启”程序)的有效性标志是无效的,程序不会错误跳转,接下来便可安全地刷入新的应用程序。
此方法通过将风险集中在刷新很小的 段期间,大大降低了“变砖”概率。但在量产软件中,需要关闭“无效则跳转”的逻辑。

小结: 对策一简单可靠,经过实际项目验证,能有效满足稳定刷新控制器 的需求。
关于固件升级和设备管理的更多深入探讨,欢迎访问云栈社区与其他开发者交流。