1. 引言
在嵌入式产品实际部署中,固件升级是保障设备功能迭代与缺陷修复的关键能力。对于 STM32F4 系列 MCU,OTA(Over-The-Air)升级通常需要结合 Bootloader 与 App 分区设计。本文围绕「双 App」架构展开,说明如何通过两个独立 App 分区实现升级失败回滚、断电保护与版本切换,提升升级过程的可靠性与安全性。
2. 双 App 方案概述
双 App 方案的核心思想是:在 Flash 中划分两个独立的 App 分区(App A 与 App B),Bootloader 根据标志位或版本信息决定启动哪一个 App。升级时,将新固件写入当前未运行的 App 分区,校验通过后切换启动标志,从而在升级失败时仍可回退到旧版本运行。
该方案相比单 App 方案的优势在于:
- 升级失败可回滚:新固件写入备用分区后先校验,校验失败不切换标志,继续运行旧固件。
- 断电保护:写入过程中断电,未完成的分区不参与启动,系统仍可正常启动。
- 版本切换灵活:支持 A/B 分区交替升级,便于灰度发布与版本回退。
3. Flash 分区规划
以 STM32F407 为例,其 Flash 大小为 1MB,起始地址 0x08000000。典型分区规划如下:
| 分区名称 | 起始地址 | 大小 | 说明 |
|---|---|---|---|
| Bootloader | 0x08000000 | 64KB | 引导程序,负责校验与跳转 |
| App A | 0x08010000 | 448KB | 当前运行固件 |
| App B | 0x08080000 | 448KB | 备用固件分区 |
| Flag 区 | 0x080FF000 | 4KB | 存储启动标志与版本信息 |
分区大小可根据实际固件体积调整,但需保证 Bootloader 与两个 App 分区互不重叠,且各分区起始地址按 Flash 扇区边界对齐。
4. Bootloader 设计
Bootloader 是双 App 方案的核心,负责完成以下工作:
- 读取启动标志:从 Flag 区读取当前应启动的 App 分区编号。
- 校验 App 有效性:对目标 App 分区进行 CRC 或 SHA 校验,确认固件完整。
- 跳转执行:设置 MSP 与 PC 指针,跳转到 App 的复位向量。
若目标 App 校验失败,Bootloader 自动切换到另一分区启动,实现故障回退。以下为 Bootloader 跳转核心代码示例:
c
typedef void (*pFunction)(void);
void jump_to_app(uint32_t app_addr)
{
uint32_t msp = *(volatile uint32_t *)app_addr;
pFunction jump = (pFunction)(*(volatile uint32_t *)(app_addr + 4));
if ((msp & 0xFFF00000) != 0x20000000) {
return; /* MSP 非法,拒绝跳转 */
}
__set_MSP(msp);
jump();
}
5. App 端设计要点
App 端需要配合 Bootloader 完成升级流程,主要包括以下设计要点:
- 中断向量表重定位:App 编译时需将中断向量表偏移到自身分区起始地址,运行时通过 SCB->VTOR 设置偏移。
- 固件接收与写入:通过串口、Wi-Fi 或以太网接收升级包,按扇区擦写备用分区。
- 升级标志写入:固件写入并校验完成后,更新 Flag 区中的启动标志,通知 Bootloader 下次启动新分区。
- 版本上报:App 启动后向服务器上报当前版本号,便于后台管理升级状态。
中断向量表重定位示例:
c
SCB->VTOR = APP_A_ADDR; /* 或 APP_B_ADDR */
6. 升级流程设计
完整的双 App 升级流程如下:
- 设备运行在 App A,服务器下发新固件包。
- App A 接收固件,写入 App B 分区,并计算校验值。
- 写入完成后,App A 将启动标志切换为 App B,并复位重启。
- Bootloader 校验 App B 固件有效,跳转执行 App B。
- App B 启动后上报新版本号,升级完成。
若步骤 2 或步骤 3 失败,启动标志未切换,设备继续运行 App A,升级失败不影响当前功能。
7. 升级失败回滚机制
双 App 方案天然支持失败回滚,具体机制如下:
- 写入失败:新固件写入备用分区过程中出错,备用分区标记为无效,Bootloader 继续启动原分区。
- 校验失败:新固件写入完成但 CRC 校验不通过,不切换启动标志。
- 运行失败:新固件启动后运行异常,可通过看门狗或心跳超时触发回滚标志,Bootloader 下次启动旧分区。
运行失败回滚需要 App 在启动早期设置「运行成功」标志,若超时未设置,Bootloader 判定启动失败并回退。
8. 注意事项与工程实践
在实际工程落地时,需要注意以下几点:
- Flash 扇区对齐:STM32F4 扇区大小不一,分区边界必须落在扇区起始地址,避免擦写越界。
- 编译链接脚本:App A 与 App B 使用不同的链接脚本,分别指定 Flash 起始地址与中断向量表偏移。
- 固件包格式:建议在固件包头加入魔数、版本号、长度与 CRC,便于 Bootloader 校验。
- 升级包加密:对固件进行加密传输,防止固件被逆向或篡改。
- 双备份策略:Flag 区可存储两份标志,防止标志写入过程中断电导致状态丢失。
9. 总结
双 App 方案为 STM32F4 OTA 升级提供了可靠的容错机制,通过分区隔离、启动标志切换与校验回滚,有效解决了升级失败、断电中断等常见问题。该方案适用于对可靠性要求较高的量产设备,是嵌入式 OTA 设计中值得优先考虑的架构之一。