IAP实验原理
194.2.2 IAP实验原理
本课讲解固件升级概念、Flash 布局和流程图,没有现场新增业务代码;具体源码从第 195 课开始逐段分析。
一、固件升级方式
1.1 ICP、ISP 与 IAP
原笔记中的“RCP、RSP、IP”是语音识别错误。本课实际比较的是 ICP、ISP、IAP:
| 方式 | 全称 | 执行升级的是谁 | 常用入口 | 典型用途 |
|---|---|---|---|---|
| ICP | In-Circuit Programming,在电路编程 | PC 上位机 + 调试/烧录器 | SWD/JTAG | 开发、生产首次烧录、故障恢复 |
| ISP | In-System Programming,在系统编程 | 芯片厂商固化在 System Memory 中的系统 Bootloader | BOOT 引脚选择 System Memory,再用其支持的串口等接口 | 没有用户 Bootloader 时现场烧录 |
| IAP | In-Application Programming,在应用编程 | 用户自己放在主 Flash 中的程序 | 由应用命令、按键、网络消息等触发 | 产品安装后更新用户固件 |
1.1.1 ICP
课程使用 ST-Link 通过 SWD 烧写,属于 ICP。它的特点是:
- 不依赖目标板上已有的 APP 是否能运行;
- 可以整片擦除、写入 Bootloader、参数和初始 APP;
- 通常需要接触调试接口和专用工具,不适合终端用户远程更新。
1.1.2 ISP
STM32F103 的 System Memory 中有 ST 出厂固化的系统 Bootloader。通过 BOOT 配置让 MCU 从 System Memory 启动后,可以使用芯片支持的通信接口下载代码。以本系列常见的 USART ISP 为例,基本过程是:
- 选择 System Memory 启动模式;
- 复位,让 ROM/System Memory Bootloader 运行;
- 上位机按 ST Bootloader 协议擦写主 Flash;
- 恢复从 Main Flash 启动并再次复位。
它和本课程自定义 Bootloader 的区别是:出厂 Bootloader 位于受保护的 System Memory,协议和可用接口由具体芯片型号决定;课程 Bootloader 位于用户主 Flash,功能由开发者决定。
1.1.3 IAP
IAP 指 MCU 在现场通过自身运行的软件改写内部 Flash。通信接口可以是 USART、CAN、USB、以太网或无线链路;接口只负责把新固件送到设备,最终仍要由 MCU 程序执行校验、擦除和编程。
“In-Application”不等于升级期间业务永不停机。本课程的 APP 收到升级命令后会复位,Bootloader 接管并覆盖 APP,完成后再跳转新 APP,因此正常业务在升级期间会中断。IAP 的真正优势是无需外接调试器,也无需用户手动切换到芯片的系统 Bootloader。
ST 的 AN2557说明了 STM32F10x IAP 的基本原则:IAP 程序可通过 USART 等协议重编程内部 Flash,用户 APP 必须使用独立的加载地址并重定位中断向量表。
1.2 IAP 编程
1.2.1 IAP 的组成
一个最小 IAP 系统至少包含:
- Bootloader:复位后最先运行,决定升级还是启动 APP,并负责接收、校验和写入固件。
- APP:完成产品业务,同时提供升级触发入口。
- 持久化状态:在复位后仍能保留的升级标志、版本号等信息,本课程放在内部 Flash 参数页。
- 传输协议:本课程用 USART2 + YMODEM。
- Flash 分区约定:Bootloader、参数和 APP 不能相互覆盖。
- 跳转约定:APP 的链接地址、向量表地址与 Bootloader 的跳转地址必须完全一致。
1.2.2 OTA 技术
OTA 是 Over-The-Air,即通过无线链路分发更新。它描述的是固件如何从远端到达设备,不是 IAP 的同义词:
- Wi-Fi、蜂窝网络、蓝牙等负责下载,属于 OTA 传输环节;
- 设备本地校验镜像并改写 Flash,属于 IAP/Bootloader 环节;
- 有些系统先把新镜像存入外部 Flash,重启后再由 Bootloader 安装;有些双槽系统直接写入非活动分区。
手机升级是更复杂的 OTA 系统,通常还包含签名验证、增量包、恢复分区、回滚和防降级,不能简化成“把文件写入 Flash 再重启”全部细节都相同。
1.2.3 两类 Bootloader
| 类型 | 存储位置 | 谁提供 | 本课是否使用 |
|---|---|---|---|
| STM32 系统 Bootloader | 芯片 System Memory | ST 在出厂时固化 | ISP 时可能使用,本实验的 IAP 不靠它运行 |
| 用户 Bootloader | 主 Flash 的 0x08000000 起始区域 | 开发者编译、烧写 | 是,本课程升级核心 |
自定义 Bootloader 本质也是普通 MCU 程序,只是职责特殊、地址固定且应尽量稳定。课程说“Bootloader 永远不变”应理解为产品策略上通常不随普通 APP 一起更新,并非技术上绝对不能升级。量产系统若需要更新 Bootloader,必须设计更严格的保护和恢复链路。
1.2.4 固件分布
配套 .ioc 确认芯片是 STM32F103ZET6,ST 资料确认该型号具有 512 KiB Flash。当前工程的实际分区为:
0x08000000 ┌──────────────────────────────┐
│ Bootloader │ 0x6000 = 24 KiB
0x08006000 ├──────────────────────────────┤
│ 参数保留区 │ 0x2000 = 8 KiB
│ 当前结构位于起始处,仅 12 B │
0x08008000 ├──────────────────────────────┤
│ APP │ 0x78000 = 480 KiB
0x08080000 └──────────────────────────────┘ 上边界,不属于 Flash
地址计算如下:
[ 0x08006000-0x08000000=0x6000=24576\ B=24\ KiB ]
[ 0x08008000-0x08006000=0x2000=8192\ B=8\ KiB ]
[ 0x08080000-0x08008000=0x78000=491520\ B=480\ KiB ]
原笔记“中间 2 KiB”的说法不够准确:STM32F103ZE 的单个 Flash 页是 2 KiB,当前参数写回确实只擦写起始的一页;但从参数起始地址到 APP 起始地址之间实际保留了 8 KiB。
各工程的链接设置也与分区对应:
- Bootloader scatter 文件:
IROM1 = 0x08000000,最大0x6000; - APP scatter 文件:
IROM1 = 0x08008000,最大0x78000; - APP 的
SCB->VTOR = 0x08008000,让中断向量指向 APP 自己的向量表。
1.2.5 升级流程
1.2.5.1 Bootloader 工作流程
复位后 Bootloader 执行:
硬件初始化
↓
读取并校验参数区(magic + checksum)
↓
读取 ota_flag
├─ 0:检查 APP 栈顶 → 设置 MSP → 跳到 APP Reset_Handler
├─ 1:USART2/YMODEM 接收固件 → 擦写 APP 区 → 清 ota_flag → 跳 APP
└─ 错误:进入课程源码的错误处理/重启路径
升级标志不能在开始接收前就清零。若传输失败或中途掉电,保留 ota_flag=1 可以让下次复位继续停留在 Bootloader 等待恢复。只有接收函数返回成功后,课程代码才清零标志。
1.2.5.2 APP 工作流程
APP 的主要职责仍是实际业务。升级入口只是附加功能:
- USART1 中断逐字节收取控制帧;
- 解析到合法升级命令后,把 RAM 标志置 1;
- 主循环把 Flash 参数区的
ota_flag改为 1; - 等待日志发送完成后触发系统复位;
- 复位后 Bootloader 读到标志并进入升级。
APP 不直接覆盖自身代码,是因为正在同一 Flash 区域执行的程序不适合一边运行一边擦除自己;把更新工作交给独立 Bootloader,边界更清晰,也保留了恢复入口。
1.3 Flash 读写
STM32 Flash 不能像 RAM 那样任意覆盖:
- 擦除把一整页恢复为
1,即通常读到0xFF; - 编程只能按器件支持的粒度把位从
1改为0; - 若要把已为
0的位恢复为1,必须先擦除所在页; - 写入前要解锁 Flash,操作后重新上锁;
- 所有地址和长度都必须落在允许的 APP/参数区域内。
课程参数更新函数的实际策略是:先把 12 字节配置读入 RAM,修改 ota_flag 和 checksum,再擦除 0x08006000 起始的 2 KiB 页,最后按 32 位字写回 12 字节。
固件接收端则按 2 KiB 页缓冲、擦除、写入并回读比较。回读验证很重要,但它只能验证“本次读回的字节是否和 RAM 缓冲一致”,还应结合完整镜像长度、整包哈希或签名判断镜像是否完整可信。
1.4 YMODEM 协议
YMODEM 建立在 XMODEM/CRC 的分包、序号、确认和重传机制之上。与裸串口连续发送整个 BIN 相比,它提供:
- 0 号包中的文件名和文件大小;
- 数据包序号及其反码,用于发现包号错误;
- 每包 CRC-16,用于检测传输错误;
- ACK/NAK 确认与超时重传;
- EOT 结束过程。
接收端持续发送字符 C(0x43)是在请求 CRC-16 模式。课程源码同时定义了 128 字节 SOH 包和 1024 字节 STX 包,但其 Flash 缓冲逻辑是课程基于 ST 示例修改的实现,应以实际互操作测试为准。
YMODEM CRC 解决的是链路传输出错问题,不提供身份认证。攻击者若能替换固件并重新计算 CRC,协议本身无法识别,因此远程产品还要验证数字签名或消息认证码。
1.5 可靠升级还缺什么
本课单槽覆盖流程适合学习,但量产设计至少还应考虑:
- 镜像合法性:检查首地址 MSP、Reset_Handler 范围、固件长度和目标型号;
- 完整性:对完整镜像计算 CRC32 或哈希,而不仅是逐包 CRC;
- 真实性:用公钥签名验证固件来自可信发布者;
- 掉电恢复:A/B 双槽、外部 Flash 暂存,或明确的可重试恢复模式;
- 版本策略:升级、防回滚、兼容性和参数迁移;
- 边界保护:永远不允许接收长度覆盖 Bootloader、参数区或 Flash 末端;
- 看门狗与超时:不能因为 PC 未发送文件而永久卡死,也不能在擦写关键页时误复位;
- 日志与结果确认:区分接收成功、Flash 写成功、整包校验成功和 APP 启动成功。
可进一步参考 ST 的 AN4657 IAP 应用笔记及 STM32F103ZE 产品页。
二、学习检查
完成本课后,应能清楚回答:
- ICP、ISP、IAP 的执行主体和启动入口分别是什么?
- OTA 为什么不是 IAP 的同义词?
- 出厂 System Memory Bootloader 与课程自定义 Bootloader 有何区别?
- 为什么 APP 既要链接到
0x08008000,又要重定位 VTOR? - 参数区为什么不能只用 RAM 变量保存升级标志?
- 为什么 YMODEM CRC 不能代替固件数字签名?