4、在线升级核心原理:双镜像启动原理(Golden Image + Update Image),看门狗回滚机制
各位工程师朋友,今天我们来聊聊在线升级里最核心、也最让人头疼的问题——升级失败了怎么办?
你想想看,产品已经部署到现场了,OTA升级包发过去,结果写到一半断电了,或者Flash坏块了,设备直接变砖。这种场景,我在项目里遇到过不止一次。所以,双镜像启动和看门狗回滚机制,就是用来解决这个问题的。
4.1 为什么需要双镜像?
说白了,就是给自己留一条后路。
传统的单镜像方案,升级时直接覆盖原有程序。一旦写入过程中出现任何异常,系统就再也起不来了。你只能派人去现场拆机、烧录,成本极高。
双镜像的思路很简单:保留一个永远不会被破坏的“安全启动区”,也就是Golden Image。另一个区域用来放更新程序,叫Update Image。就算Update Image写坏了,系统还能从Golden Image启动,保证设备不彻底死掉。
4.2 双镜像启动流程
我习惯把启动流程分成三个阶段:引导、校验、切换。下面这张图可以帮你快速理解整体逻辑。
流程其实不复杂,我拆开来讲。
4.2.1 第一阶段:Bootloader 引导
系统上电后,首先执行的是Bootloader。Bootloader很小,放在Flash的最开头,一般只有几十KB。它的任务只有一个:决定启动哪个镜像。
Bootloader会去检查Update Image的头部信息。这个头部里通常包含:
- 镜像有效标志:一个特定的魔数(比如0xA5A5A5A5)
- CRC校验值:对整个镜像的校验
- 版本号:用于判断是否是新版本
- 镜像大小:加载时需要知道长度
4.2.2 第二阶段:镜像校验
Bootloader拿到头部信息后,会做两件事:
- 检查魔数:如果魔数不对,说明Update Image区域是空的或者数据损坏,直接跳转到Golden Image。
- 计算CRC:对整个Update Image做CRC校验。如果CRC不对,说明镜像被破坏了,同样跳转到Golden Image。
只有两项都通过了,Bootloader才会把控制权交给Update Image。
4.2.3 第三阶段:应用启动与看门狗回滚
这里有个关键点:即使CRC校验通过了,也不能保证应用一定能正常运行。
为什么?因为CRC只保证数据完整性,不保证逻辑正确性。比如你升级了一个有bug的固件,启动后系统死机了,怎么办?
这就是看门狗回滚机制发挥作用的地方。
具体做法是这样的:
- Bootloader启动Update Image之前,先开启一个长超时看门狗(比如30秒)。
- Update Image启动后,必须在看门狗超时之前,完成初始化并主动喂狗。
- 如果Update Image死机了,看门狗超时,系统复位。
- 复位后Bootloader再次启动,发现Update Image的“升级完成标志”没有被置位,就知道上次启动失败了。
- 于是Bootloader自动切换到Golden Image,并标记Update Image为无效,下次不再尝试启动它。
4.3 看门狗回滚的工程实现
嗯,这里我直接给出一段伪代码,帮你理解核心逻辑。实际项目中,你根据芯片平台调整即可。
// Bootloader 主流程
void bootloader_main(void) {
// 1. 检查 Update Image 是否有效
if (check_update_image_valid()) {
// 2. 清除升级完成标志
clear_update_complete_flag();
// 3. 设置看门狗超时时间为30秒
watchdog_init(30000);
// 4. 跳转到 Update Image
jump_to_update_image();
// 注意:如果Update Image正常启动,它会主动喂狗
// 如果它死机了,看门狗会复位系统
}
// 5. 如果Update无效,直接启动Golden Image
jump_to_golden_image();
}
// Update Image 启动后
void application_main(void) {
// 1. 初始化硬件
hardware_init();
// 2. 喂狗,告诉Bootloader我活过来了
watchdog_feed();
// 3. 设置升级完成标志
set_update_complete_flag();
// 4. 进入主循环
while(1) {
// 正常业务逻辑
// 定期喂狗
watchdog_feed();
}
}
4.4 Flash分区规划建议
双镜像启动离不开合理的Flash分区。我一般这样规划:
| 分区名称 | 起始地址 | 大小 | 说明 |
|---|---|---|---|
| Bootloader | 0x00000000 | 64KB | 引导程序,只读 |
| Golden Image | 0x00010000 | 512KB | 出厂固件,只读 |
| Update Image | 0x00090000 | 512KB | 升级固件,可读写 |
| 参数区 | 0x00110000 | 64KB | 存储标志位、配置参数 |
这里有个细节:参数区一定要单独划分。不要把标志位放在镜像内部,否则每次升级都会覆盖掉。参数区用来存“升级完成标志”、“回滚次数”、“当前启动镜像编号”等信息。
4.5 回滚次数限制
最后提一个容易被忽略的点:回滚次数限制。
如果Update Image每次启动都失败,系统就会反复在Update和Golden之间切换。每次切换都是一次复位,如果现场设备频繁复位,用户会投诉的。
我的做法是:在参数区记录一个回滚计数器。每次从Update回滚到Golden时,计数器加1。如果连续回滚超过3次,就永久锁定在Golden Image,不再尝试启动Update Image。同时通过日志或指示灯提示用户“升级失败,请联系售后”。
- 双镜像:Golden Image 保底,Update Image 升级
- Bootloader 负责校验和决策
- 看门狗回滚:超时复位 + 标志位判断
- 回滚次数限制:防止无限复位
好了,双镜像启动和看门狗回滚机制就讲到这里。这套方案我在多个工业产品上验证过,稳定可靠。你只要把分区规划好、标志位设计清楚,在线升级的安全性就能上一个台阶。