☰
IAP串口升级[2] - 设计思路
2026/10/10 5:22:01 网站建设 项目流程

IAP 串口升级 [2] — 设计思路

本系列共三篇:[1] 思考过程·[2] 设计思路(本文)· [3] 升级流程(含代码)。
上一篇讲了「怎么一步步想到的」,这一篇把每个设计决策的「为什么」整理成体系,方便查阅。


一、这套 Bootloader 解决什么问题

现场固件更新有四条传统路径:复位按键、断电重上电、DTR/RTS 拉复位、软件命令。前三条被硬件约束堵死(无按键、不断电、不改接线),只剩「软件触发 + 引导程序」。

所以整套设计围绕一件事展开:如何在这种最坏约束下,安全、可靠、不砖地完成升级。


二、总体架构:为什么要分层

Bootloader 拆成五个职责单一的模块:

模块职责依赖
iap.c核心流程:入口判断、‘C’ 循环、头校验、校验跳转逻辑层,不碰寄存器
iap_port.c硬件抽象:串口、Flash、跳转、复位唯一的硬件层
xmodem.cXmodem 接收协议只依赖 iap_port
iap_aes.cAES-128-CBC 解密纯算法,无依赖
iap_flash.cFlash 写状态机 + 解密回调连接 xmodem 与 flash

为什么分层?

  • 可移植:所有「碰寄存器」的代码收在iap_port.c一个文件。换 MCU 只重写它,上层iap.c/xmodem.c/iap_aes.c零改动。
  • 可测试:协议逻辑(xmodem/AES/CRC)与硬件解耦,可在 PC 上独立验证。
  • 易维护:改协议不动硬件,改硬件不动协议。

三、分区规划:为什么这么切 Flash

3.1 Bootloader 占低地址、APP 占高地址

0x08000000 ┌──────────────┐ │ Bootloader │ 20KB (0x5000) 0x08005000 ├──────────────┤ │ APP │ 44KB (0xB000) 0x08010000 └──────────────┘ (64KB 结束)

为什么 Bootloader 必须在0x08000000?Cortex-M 上电后硬件自动从0x08000000读栈顶 SP 和复位向量 PC,第一个执行的程序必须在最低地址——这是 CPU 架构决定的,没得选。

为什么留 20KB?Bootloader 要装串口+DMA、AES 全套 S 盒(约 512 字节)、Xmodem、Flash 驱动,编译后可能不到 10KB,留 20KB 是为后续加功能留余量。剩下 44KB 给 APP,FLASH_APP1_SIZE = 0x10000 - 0x5000 = 0xB000自动算出来。

3.2 APP 有效标志为什么放0x08004500(Bootloader 区末尾)

为什么放这里、而不是 APP 区内部?

  • 清标志不会误伤 APP:擦除单位是页(2KB)。标志放 APP 区,擦它会把 APP 代码一起擦;放 Bootloader 保留页,擦标志只动 Bootloader 自己的空闲页。
  • APP 被擦写时不动标志:升级擦写的是0x08005000往后的 APP 区,标志在它前面,物理隔离。

标志是 Bootloader 管理的元数据,必须和 APP 数据分区隔离。

3.3 页大小 2KB

Flash 擦除最小单位是 2048 字节,所有擦除都要按页对齐,这是 Flash 控制器决定的。


四、启动决策流:每一步为什么

4.1 上电先喂狗、初始化串口

main.c启动顺序:关中断 → NVIC 优先级 → 时钟 → GPIO →TIM6(喂狗)→ UART → 开中断 →iap_run。

为什么 Bootloader 也要喂看门狗?IWDG 一旦在 APP 里被使能,就不受系统复位影响——软复位后它还在倒计时。APP 触发软复位进升级后,看门狗仍在跑,Bootloader 升级期间不喂狗就会超时复位、打断升级。所以 Bootloader 必须带定时器中断喂狗,这决定了「不能纯轮询」。

4.2 用 RAM 标志判断「是否强制升级」

为什么用 RAM 而不是 Flash?软复位后 RAM 保留(不掉电),可传「意图」;Flash 写标志要擦页、慢且耗寿命。

为什么用 magic 而非布尔?冷启动 RAM 是随机值,恰好等于一个 32 位特定值的概率约 1/2³²;用单字节0xFF则断电残留为0xFF的概率极高,会误触发。

为什么地址选0x20003C00(SRAM 顶部)?16KB SRAM 是0x20000000~0x20004000,0x20003C00在最后 1KB,让 APP 的栈/堆/全局都够不着,不会被覆盖。

4.3 上电窗口监听命令

为什么有窗口?给「零改动 APP」留入口——APP 只改VECT_TAB_OFFSET,一行升级代码都不加,只能靠「上电瞬间 Bootloader 主动监听」捕获升级指令。

为什么 50ms?太短抓不到,太长每次开机傻等拖慢启动。50ms 在 115200 下够收完 9 字节(约 0.8ms),又是基本无感的延迟。这是「可靠 vs 速度」的折中。

为什么命令是0x55 0xAA 0x01 + "IAP" + 0x21?0x55 0xAA是经典同步前缀(交替 0101/1010 抗干扰);0x49 0x41 0x50是 ASCII「IAP」好记;尾部 CRC16 复用 Modbus 的算法(poly 0xA001),不必另写。

4.4 跳转 APP 的条件:标志 + 栈双重校验

两条都要过才跳:

  1. 0x08004500标志 ==0x78563412—— 回答「是不是一次完整成功烧录的产物」。
  2. APP 首字(栈顶 SP)高 16 位 ==0x2000—— 回答「前 4 字节是不是合法栈指针」,防止跳进空白 flash 的0xFFFFFFFF。

五、升级数据流:每一步为什么

5.1 反复发 ‘C’ 等头

iap_receive_header不停发'C'(0x43)边发边等,直到收到合法头或超时。为什么反复发?'C'是 Xmodem 接收端的「开球」信号,反复发给主机时间——主机可能还没切到升级流程、串口缓冲还没清。双方不需要严格同步。

5.2 升级头:为什么带明文长度/明文 CRC32

为什么 Xmodem 之外还要加头?Xmodem 不携带文件长度和预期校验值,只会一包一包收。加头让 Bootloader 提前知道「多大、最终 CRC 多少」,才能做「长度校验」「回读校验」。

为什么记明文而非密文?校验对象是「写进 flash 的明文」,回读出来的也是明文,用明文 CRC 才对得上。搞反了回读校验就是白做。

5.3 写前清 APP 标志:防砖

收到合法头回 ACK 后,第一件事擦掉0x08004500标志,才开始收数据写 flash。

想象最坏情况:写到一半断电,flash 里是「半个新 APP」。若没清标志,下次上电 Bootloader 误以为 APP 是好的,直接跳进半个固件——变砖。若先清了标志,下次上电标志无效,不跳转,停在 ‘C’ 等重新升级——永远救得回来。

5.4 选 Xmodem + 重复块处理 + 就地解密

  • 为什么选 Xmodem?自带 ACK/NAK、CRC16、重传、EOT 一整套可靠传输,自己写容易出边界 bug。
  • 为什么重复块只 ACK 不写?Bootloader 回 ACK 但 ACK 丢了,主机会重发上一块。老实再写会在同一位置重复编程(flash 已写位不能直接覆盖)。所以收到seq == 当前序号-1就只 ACK 跳过。
  • 为什么就地解密?16KB SRAM 紧张,收进来 128 字节密文就地解密成明文,不额外开缓冲。

5.5 按页擦除 + 字编程

  • 为什么按 4 字节编程?N32G430 Flash 接口是 32 位字(FLASH_Word_Program),硬件只能按字写。
  • 为什么边写边擦、只擦「即将覆盖且未擦过的页」?数据流式到达,维护erased_until,只有新数据要写到未擦页时才擦。好处:只擦必要页、不会在升级开头卡「整片擦除」长阻塞、配合写前清标志中途断电也不误跳。

5.6 回读 CRC32 校验

为什么串口有 CRC16 还要回读?串口 CRC 只保证「这一包线上没传错」,管不了解密、写入、漏块、位翻转。回读是端到端兜底,从「主机选的 .bin」到「设备 flash 内容」最终比对,是防砖最后一道闸。

5.7 置标志 + 跳转的顺序

校验通过后:回 ACK → 置标志 → 跳转。为什么先置标志再跳?置标志=宣告「新固件已完整正确落盘」;跳转是单行道,回不来。顺序反了,跳转前被打断就置不上标志,下次又进不了 APP。

为什么跳转前校验栈?跳转=设 MSP 为 APP 首字、PC 为次字、重映射向量表。首字非法(空白 flash 的0xFFFFFFFF)会立刻崩,所以先查 SP 高 16 位 ==0x2000。


六、安全与健壮性专题

6.1 防砖三件套

手段防的是
写前清 APP 标志中途断电不跳「半个 APP」
回读 CRC32 校验写入内容错误
长度校验(≤ FLASH_APP1_SIZE)镜像越界覆盖

三者叠加,任何失败都不会把设备变成不可恢复的砖。

6.2 RAM 标志一次性

进升级前就清掉 RAM 标志。为什么?不清的话,升级结束复位后 Bootloader 又看到标志、再次强制进升级,陷入死循环。「强制升级」必须一次性。

6.3 失败后的降级恢复

iap_run外层死循环末尾有「若 APP 有效则跳转」。为什么?任何一步失败后,若 flash 里还有有效旧 APP 就跳回去恢复工作;连旧 APP 都没有就继续在 ‘C’ 循环等重新升级。永远给设备一条活路。

6.4 为什么加密用 AES-CBC

  • 目的是「防串口抓包/明文分析」,不是高安全——固件是核心资产,不想让人用逻辑分析仪直接读出 .bin。
  • AES-128-CBC 挡得住「随便抓包」,密钥硬编码,不追求抗逆向。
  • iap_aes.c纯 C 实现,microlib 下也能编译。
  • 选 CBC 而非 ECB:CBC 有链式混淆,相同明文块密文不重复,更难分析。

七、关键决策速查表(为什么 → 结论)

问题结论原因
Bootloader 放哪0x08000000Cortex-M 上电从这里取 SP/PC
进升级标志放哪RAM0x20003C00软复位保留、magic 防误判、顶部防覆盖
为什么 50ms 窗口零改动 APP 入口唯一无需 APP 配合的方案
为什么写前清标志防砖中途断电不跳半个 APP
为什么回读校验端到端兜底串口 CRC 管不到解密/写入错
为什么用 Xmodem成熟可靠自带重传+校验+流控
为什么加升级头补 Xmodem 缺的长度/CRC用于长度校验+回读校验
为什么就地解密省 RAM16KB SRAM 紧张
为什么 Bootloader 喂狗防升级中断IWDG 软复位后仍在跑
为什么分层可移植换芯片只改 iap_port.c

上一篇:[1] 思考过程 —— 从约束到方案的心路历程。
下一篇:[3] 升级流程 —— 完整时序 + 每一步的实际代码片段。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询