STM32 IAP方案实战:从寄存器到上位机,实现双重CRC校验,可实现多方式通信升级
2026/8/16 2:03:39 网站建设 项目流程

从零实现 STM32F103 串口 IAP 固件升级系统(Boot + APP + 上位机全流程)

最近做了一个 STM32 在线升级(IAP)项目,从零实现了完整方案:Boot 引导程序、App 应用程序、Python 上位机三个部分,全链路实测可用。本文把整个系统的设计思路、通信协议、可靠性和踩过的坑都整理出来,希望能给想学 IAP 的朋友一些参考。


一、项目能做什么

STM32 程序写进 Flash 后,要更新怎么办?传统做法是用 ST-Link/J-Link 重新下载。但如果产品已经装到现场,总不能每台都拆开接下载器吧?

IAP(In-Application Programming,在应用中编程)就是解决这个问题的:程序自己给自己升级。只需要第一次用下载器烧 Boot,之后 App 的所有更新都通过串口完成——“第一次程序靠下载器,之后全靠串口”

本项目的三个部分:

  • Boot 引导程序:上电判断是否进入升级模式,决定收新固件还是跳转现有 App
  • App 应用程序:正常产品程序,可通过串口收升级指令,主动请求进 Boot 升级
  • Python 上位机:图形界面,选固件文件一键升级,实时显示 App 运行状态

二、系统架构

┌─────────────┐ 0xAF 升级请求 ┌──────────────────┐ │ Python 上位机 │ ───────────────────▶ │ App 应用程序 │ │ (GUI) │ │ (0x08004000) │ │ │ │ 收指令→写标志→复位 │ └──────┬───────┘ └─────────┬────────┘ │ IAP 协议帧 (115200) │ 软复位 │ ▼ │ ┌─────────────────────────┐ └────────────────────▶│ Boot 引导程序 │ │ (0x08000000) │ │ 查 Config 标志决定 │ │ 进升级 or 跳 App │ └────────────┬────────────┘ │ 升级完成跳转 ▼ 新 App 运行

Flash 内存布局(STM32F103C8, 64KB)

0x08000000 ┌──────────────────┐ │ Boot (16KB) │ 引导程序 0x08004000 ├──────────────────┤ │ App (47KB) │ 应用程序(可被升级) 0x0800F800 ├──────────────────┤ │ Config (1KB) │ 升级标志 + 固件信息(防变砖) 0x08010000 └──────────────────┘

三、通信协议

帧格式(发送)

┌──────┬──────┬──────────┬──────┬──────────────┬────────┬────────┐ │ 0xAA │ 命令 │ 数据长度 │ 序号 │ 数据... │ CRC16低│ CRC16高│ └──────┴──────┴──────────┴──────┴──────────────┴────────┴────────┘
  • 帧头0xAA
  • CRC16:MODBUS 算法,初值0xFFFF,多项式0xA001,对"命令~数据"计算,小端存放
  • 波特率:115200,8N1

命令表

命令命令码数据说明
握手 READY0x0A进入升级模式
擦除 ERASE0x0B擦除整个 App 区
写数据 WRITE0x0C数据(≤128B)seq 从 0 递增,地址 = APP_BASE + seq×128
校验 VERIFY0x0D总长度(4B LE) + CRC32(4B LE)对写入固件整包校验
跳转 JUMP0x0E校验通过后跳转 App

回复帧格式

┌──────┬──────────┬──────┬────────┬────────┐ │ 0xBB │ 状态码 │ 序号 │ CRC16低│ CRC16高│ └──────┴──────────┴──────┴────────┴────────┘

状态码

状态码含义
0xA0成功
0xB0重复包(序号相同)
0x01CRC 校验错误
0x03序号不连续(丢包)
0x04状态错误(当前状态不允许此操作)

升级时序

上位机 Boot │ 0x0A 握手 │ ├─────────────────────▶│ 状态→READY │ BB A0 成功 │ │◀─────────────────────┤ │ 0x0B 擦除 │ ├─────────────────────▶│ 擦除 App 区,状态→ERASED │ BB A0 成功 │ │◀─────────────────────┤ │ 0x0C 数据帧(seq=0) │ ├─────────────────────▶│ 写 Flash 偏移 0 │ BB A0 成功 │ │◀─────────────────────┤ │ 0x0C 数据帧(seq=1) │ ├─────────────────────▶│ 写 Flash 偏移 128 │ BB A0 成功 │ │◀─────────────────────┤ │ ... │ │ 0x0D 校验 │ ├─────────────────────▶│ 硬件 CRC32 比对,状态→VERIFIED │ BB A0 成功 │ │◀─────────────────────┤ │ 0x0E 跳转 │ ├─────────────────────▶│ 清标志 → 跳转 App │ ▼ │ App 开始运行

四、可靠性设计

4.1 Config 标志页(防变砖)

Flash 最后一页(0x0800F800)保存升级标志:

typedefstruct{u32 magic;// CONFIG_MAGIC,判断配置有效性u32 upgrade_flag;// UPGRADE_FLAG,是否请求升级u32 reserve[3];// 预留}Config_t;
  • 只有升级成功、跳转前才清标志
  • 升级中断电/失败 → 标志还在 → 下次上电 Boot停在升级模式等你重发,不会变砖
  • 已实测:升级中断电 2 次,重新上电均可恢复重来

4.2 关键设计点

  1. 中断 + 环形缓冲接收:STM32 USART 无 FIFO,慢轮询会丢字节。中断每收一字节入环,主循环取走处理
  2. 跳转前善后:关全局中断、清 NVIC、停 SysTick、复位时钟,再设 MSP 跳转
  3. FLASH_ProgramWord强制写魔数:避免config_read首次读到擦除态0xFFFFFFFF时把 magic 清 0,导致标志永远不生效
  4. CRC16 帧校验 + CRC32 整包校验双保险

4.3 通道抽象(transport 接口)

协议层不直接操作串口,而是通过统一的transport接口收发包。好处:以后接 WiFi/4G 远程升级时,只需新增一个 transport 实现,协议层零改动

typedefstruct{void(*init)(void);/* 初始化通道 */void(*send)(uint8_t*buf,uint16_tlen);/* 发送一帧 */uint8_t(*recv)(uint8_t*buf,uint32_ttimeout);/* 收一字节, 超时返回0 */}transport_t;voidtransport_set(transport_t*t);/* 注册当前通道 */transport_t*transport_get(void);/* 获取当前通道 */

recv必须带超时参数:让上层能感知"通道暂时无数据"。当前实现transport_uart.c(USART1),未来加transport_wifi.c即可切换。

五、疑难问题排查(踩坑记录)

现象原因解决
握手只收到 1~2 字节USART 无 FIFO + 慢轮询 + 阻塞 printf改用中断+环形缓冲区
写数据后多回0xB0重复包三个if未用else ifseq自增后误判重复改成else if
擦除后写数据被拒(0x04擦除后未设STATE_ERASED擦除成功设状态
空片/断电损坏后无法恢复App 无效时iap_load_app返回后死循环兜底进升级模式
config 标志写不进去config_read首次读到擦除态把 magic 清 0config_write强制写魔数
断电后上位机报"拒绝访问"串口句柄失效未重连上位机加断线自动重连
PC13 LED 不闪用了 CRL 而非 CRH、ODR 位号写错PC13 在 CRH,位 13

六、移植到其他芯片(如 RCT6)

C8T6RCT6
Flash64KB256KB
页大小1KB2KB
RAM20KB48KB
Flash 下载算法Med-densityHigh-density

只需改动 3 处,协议/上位机/App 逻辑零改动:

  1. flash.hFLASH_PAGE_SIZE1024 →2048
  2. flash.hCONFIG_ADDR0x800F800 →0x803F800(256KB 最后一页)
  3. Keil:APP 工程IROM1尺寸放大,Flash 下载算法选 High-density

APP_BASEVTOR仍是0x08004000,不用改——当初用"偏移量 + 宏"设计就是为了移植时收网。


总结

这套 IAP 方案麻雀虽小五脏俱全:Boot 跳转、Flash 擦写、自定义协议、状态机、上位机、可靠性设计、通道抽象都有。个人认为最有价值的是那几个坑——它们都是真实踩过、花时间排查出来的,能帮你少走很多弯路。

完整代码已开源,两个平台内容同步,欢迎 Star:

  • GitCode(国内访问快):👉 https://gitcode.com/2402_85030385/IAP
  • GitHub(国际访问):👉 https://github.com/wenroudezhizi/IAP

如果文章对你有帮助,欢迎点赞、评论、收藏!有问题也欢迎在评论区交流。

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

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

立即咨询