资讯动态

STM32F103C8T6串口IAP固件升级实践:Bootloader+App双区方案

发布时间:2026/9/7 9:25:29 来源:尧图企业网站定制
简介面向 STM32F103C8T6 开发者的串口 IAP 在线升级源码包解决产品现场通过串口完成固件升级时 Bootloader 与 App 跳转的问题适合有基础嵌入式开发经验、正在规划量产升级方案的工程师学习参考。资源共 430 个文件压缩包约 2.23MB涵盖 h/c 核心源码、启动汇编与链接脚本、Keil/IAR 等多种工程文件以及 hex/bin 固件产物和说明文档便于直接对照工程结构进行二次移植。其中大量 h/c 为驱动与应用层实现s/asm 为启动与底层汇编文件uvproj/ewp/icf/ld 覆盖主流 IDE 与链接器配置bin/hex 可直接烧录验证txt/doc 等文本资料可用于查看说明。目前已有 5076 人学习下载。通过工程源码可理解 IAP 升级流程中的分区规划、串口通信协议、Flash 擦写与跳转实现利用现成工程和编译产物可快速搭建实验环境结合硬件板卡验证升级逻辑节省从零开发 Bootloader 的时间。项目标题: STM32F103C8T6实现串口IAP方式升级固件源码包项目正文: STM32F103C8T6最小系统板串口IAP升级BootloaderApp双区方案串口调试助手发送bin文件HAL库实现支持跳转与Flash擦写。关键词: STM32F103C8T6, 串口IAP, 固件升级, Bootloader, HAL库摘要描述: 基于STM32F103C8T6的串口IAP固件升级完整源码包含Bootloader、App工程与上位机操作流程。做嵌入式开发几年谁没被“改个BUG就要拆机烧录”折磨过板子封进外壳、设备挂在墙上、产线已经出货这时候想升级固件总不能拿把螺丝刀挨个拆吧。串口IAP就是干这个的——通过Bootloader在应用运行中接收新固件直接写进Flash省掉拆机、省掉烧录器一根串口线就能完成升级。这套基于STM32F103C8T6的串口IAP源码包就是为解决这个问题而生的完整方案适合正在做小批量产品、需要远程或本地升级固件的开发者也适合刚接触IAP想搞懂原理的初学者直接抄作业。源码包里Bootloader工程、Demo App工程、上位机发送脚本全都有串口助手能直接配合使用拿到手改个引脚就能跑。1. 整体设计与功能拆解1.1 为什么必须用IAP而不是ISP很多初学者会有个疑惑ST-Link烧录不也能写Flash吗为什么非要多此一举搞个Bootloader这里先说结论ISP适合开发调试阶段IAP适合产品量产后的维护阶段。ISPIn-System Programming需要烧录器ST-Link/J-Link参与走的是SWD/JTAG接口本质还是调试器把固件写进Flash设备不在手边就无能为力。而IAPIn-Application Programming是程序自己写自己的Flash只要有一个能接收数据的通道——串口、网口、USB、甚至无线模块——就能完成固件更新。对于已经出货的产品串口往往还在哪怕是预留的调试口一根USB转TTL线就能现场升级成本极低。C8T6这颗芯片做IAP还有个天然优势它属于中容量产品Flash按1KB/页组织擦除和写入都很灵活。64KB的Flash划分成16KB的Bootloader区 48KB的App区Bootloader负责收数据、验数据、写Flash、跳转App只管跑业务逻辑两区互不干扰这是最常见的双区方案。1.2 源码包的组成与分工这套源码包里包含三个独立的部分逻辑非常清晰Bootloader工程独立Keil工程烧录在0x08000000起始的Boot区域上电后先运行。它负责初始化串口、等待升级指令、接收固件数据并写入App区、校验无误后跳转执行App。App Demo工程一个最简单的LED闪烁程序编译时把ROM起始地址改成0x08004000通过特定方式响应跳转验证整个升级链路是否跑通。上位机发送端可以直接用XCOM、SSCOM这类串口助手发送bin文件源码包也附带了一个Python脚本自动完成握手、分包、发送的过程不用手点。三个部分各司其职你不需要在这套代码里同时看到Bootloader和App的main函数混在一起因为混在一起反而容易搞混跳转逻辑。分开工程编译、分别烧录排查问题也方便。2. 核心原理与关键技术点2.1 Flash分区地址是一切的起点STM32F103C8T6的Flash从0x08000000开始共64KB。分区方案直接决定了后面所有代码里的地址参数这一步千万别拍脑袋建议画一张表区域起始地址大小用途Bootloader0x0800000016KB (0x4000)IAP升级程序上电入口App0x0800400048KB (0xC000)业务应用跳转目标预留对齐0x08010000-Flash末尾边界选择16KB给Bootloader是综合考虑后的结果。单纯串口接收、擦写、跳转的逻辑编译出来一般就3~5KB16KB留了充足余量以后加协议、加加密算法也不会拥挤。48KB给App对于大部分C8T6的项目足够用了毕竟这颗芯片定位就是中低复杂度应用。这里要特别提醒C8T6的Flash页大小是1KB不是大容量型号的2KB。所以擦除App区时起点0x08004000开始的第0页对应Bootloader区的第64页擦除循环要按页数算准确擦多擦少都会出问题。2.2 串口升级协议设计串口IAP的核心是可靠传输。Bootloader和上位机之间必须约定一套清晰的通信协议否则数据错位、丢包、校验失败会让人疯掉。这套源码包里的协议采用“帧头 命令字 长度 数据 校验”的结构帧结构: [0xAA 0x55] [CMD] [LEN_L] [LEN_H] [DATA...] [CRC8]帧头固定0xAA 0x55用于同步检测到这两个字节才开始解析帧。命令字CMD0x01表示握手请求0x02表示擦除App区0x03表示固件数据帧0x04表示跳转执行。长度LEN数据域长度最大设成10281024数据4地址一帧刚好一页Flash数据加4字节的写入地址。数据域对于0x03数据帧前4字节是32位目标地址后面紧跟1024字节固件内容。校验CRC8对整帧做校验收到后校验不符直接丢弃并回复错误帧上位机做超时重传。用过串口助手的都知道手动分包发送容易出错。所以源码包里的Python脚本做了两件事把bin文件拆成N个1024字节的包逐包发送并等待Bootloader的ACK收到NACK或超时则重发当前包。这套机制看着简单但实测下来在115200波特率下48KB固件大约20秒传完丢包率极低。2.3 跳转核心代码解读Bootloader收到0x04命令后执行跳转跳转不是简单的一条goto必须处理好三件事关闭外设和中断、重映射中断向量表、设置主栈指针。直接贴核心代码typedef void (*pFunction)(void); void JumpToApp(uint32_t app_addr) { uint32_t stack_addr *(volatile uint32_t *)app_addr; // 检查栈顶地址是否在RAM范围内防止跳到空Flash if ((stack_addr 0x2FFE0000) ! 0x20000000) { return; } // 跳转前必备清理关闭全局中断去初始化外设 __disable_irq(); HAL_RCC_DeInit(); HAL_DeInit(); SysTick-CTRL 0; SysTick-LOAD 0; SysTick-VAL 0; // 中断向量表重映射到App起始地址 SCB-VTOR app_addr; // 取复位向量设置MSP跳转 pFunction jump (pFunction)(*(volatile uint32_t *)(app_addr 4)); __set_MSP(stack_addr); jump(); }这段代码里那个“检查栈顶地址是否在RAM范围内”的if判断非常关键新手最容易漏。如果App区根本没烧录程序Flash里全是0xFF此时读取栈顶地址就是0xFFFFFFFF直接跳转必然HardFault。加上合法性校验Bootloader就能稳稳待在原地不会变成砖头。3. 实操记录从CubeMX配置到升级跑通3.1 硬件连接与准备工作实际操作时我用的是最常见的STM32F103C8T6最小系统板一块CH340 USB转TTL模块几条杜邦线。连接方式很简单CH340的TXD接板子的PA10USART1_RXRXD接PA9USART1_TXGND共地。如果你用的是带3.3V输出的CH340模块也可以给板子供电但千万别用5V输出给C8T6供电虽然芯片的VDD是3.3V但AM1117稳压后一般没事可万一模块不是线性稳压方案很容易烧板。所以最稳的做法是板子USB供电CH340只连TXD、RXD、GND三根线。软件方面需要准备Keil MDK我用的是5.x版本、STM32CubeMX可选直接改工程也行、XCOM串口助手或源码包内的Python脚本、CH340驱动。说到CH340驱动Windows 10以上系统通常免驱但老系统偶尔会识别成未知设备装上官方驱动就解决了。3.2 Bootloader工程配置要点Bootloader工程我用HAL库写的CubeMX里配置很直观几个关键参数时钟外部8MHz晶振PLL倍频到72MHz系统时钟。C8T6默认内部HSI也能跑但串口波特率精度会受影响115200下长期跑下来会有偶发乱码所以外部晶振是首选。USART1异步模式115200-8-N-1使能全局中断。收发都用中断方式不用DMA因为在Bootloader里简化逻辑比追求速度更重要。GPIOPA9、PA10复用为USART1功能另外拉一个LED指示引脚我用的PC13最小系统板上的LEDBootloader运行时LED慢闪升级成功跳转后LED快闪直观区分Bootloader和App哪个在跑。中断优先级USART1全局中断分到2组优先级抢占优先级0子优先级0。这里不要用默认值因为Bootloader里没有其他中断源但高优先级能防止和Flash擦写时的延时冲突。生成工程后在main.c里补全IAP的完整状态机上电先初始化串口和LED然后发送一个“Bootloader Ready”提示串等待上位机发0x01握手命令。如果没有收到握手等一个超时时间我设了2秒就自动跳转App如果收到握手则进入升级流程擦除App区、逐包接收数据、CRC校验、最后跳转。3.3 App工程必须修改的两个地方App工程看起来和普通工程没区别但有两个致命点不改升级必失败。第一是ROM起始地址。在Keil的Options for Target - Target页里把IROM1的Start改成0x08004000Size改成0xC000。如果不改App还是从0x08000000开始Bootloader跳转过去执行的就是一堆错乱数据。第二是中断向量表重映射。Cortex-M3内核的中断向量表默认在0x08000000App启动后如果不搬到0x08004000一旦发生任何中断定时器、串口、外部中断CPU还是会去0x08000000查向量表拿到的却是Bootloader的中断服务函数逻辑彻底错乱。HAL库下写法很简单// 在main函数最开始SystemInit之后立即执行 SCB-VTOR 0x08004000;有人问为什么不放在SystemInit里因为SystemInit是启动文件调用的那时App的main还没运行放这里也完全可以。我习惯放在main第一行清晰直观。App工程编译生成bin文件时要在Keil的User页加一句用户命令fromelf.exe --bin -o ./App/App.bin ./App/App.axf这样每次编译自动生成App.bin省去手动用fromelf转换的麻烦。3.4 上位机操作与升级全流程我用源码包里的Python脚本做升级流程很顺畅。先启动Bootloader串口助手能看到“Bootloader Ready”提示然后运行脚本指定串口和bin文件路径脚本自动完成整个流程打开串口我这里用的是COM4发送0x01握手命令。Bootloader回复握手成功然后擦除0x08004000起48KB Flash擦除期间LED快速闪烁。脚本把bin文件按1024字节分成48个包逐包发送每包等待ACK超时1秒重发。最后一包发完脚本发送0x04跳转命令。Bootloader校验总计接收字节数一致则跳转App启动LED开始快闪升级完成。整个流程不用人工介入中途断了也不怕重新运行脚本就能从头再来。实测下来48KB固件在115200波特率下跑完大约18~22秒稳定性和速度都比较理想。4. 典型问题排查与避坑实录4.1 IAP跳转后卡死HAL_Delay无响应这是最经典的问题网上搜“iap跳转后卡死hal_delay”能出来一大堆。原因在于HAL库里有个隐蔽的坑跳转前调用了HAL_DeInit()重置所有外设但SysTick的中断使能会被清掉。App启动后HAL_Init又会重新配置SysTick可如果Bootloader里用了HAL_Delay跳转前没有把SysTick的计数器和中断彻底关干净App里的HAL_Delay直接死循环。我排查时用调试器看App运行位置发现卡在HAL_Delay的while (uwTick - tickstart Delay)里。解决方法是跳转前强制清零SysTick并关闭其中断代码里那三行SysTick-CTRL 0; SysTick-LOAD 0; SysTick-VAL 0;就是这个作用。另外在App的main函数里优先执行HAL_Init()再设置VTOR。4.2 App能跑但串口中断不触发这个问题和4.1同源都是中断向量表没改对。症状是现象诡异LED在闪、主循环正常但一开串口中断就死机或者中断服务函数不进。我遇到过一次是App里用USART2做通信但没在App工程里重映射VTOR结果串口中断一进来就跳到Bootloader的USART1中断处理函数里去了整个逻辑彻底乱套。解决方式很简单App main里务必加上SCB-VTOR 0x08004000;一行且必须在任何中断使能之前。4.3 串口升级过程中偶发丢包、校验失败这个问题我排查了很久最终定位到两个原因。第一是串口线质量用的杜邦线太长在1米以上时高速传输容易出现反射115200波特率下偶尔就是会错几个字节。换成20公分的短线后问题消失。第二是Bootloader接收缓冲区的处理我在中断里只把数据存入环形缓冲区主循环里再解析帧这样即使某帧解析失败也不会丢下一个帧头。如果图省事在中断里直接解析极容易在擦写Flash时Flash擦写会暂停内核运行把后续数据弄丢。4.4 生成的bin文件无法升级bin文件本身没问题但升级后App起不来这种情况最常见的原因是生成bin时起始地址不对。fromelf命令默认生成的是从0x08000000开始的地址映射而我们App是从0x08004000开始的所以必须在App工程配置里让fromelf知道起始地址。Keil的fromelf命令本身不带地址参数它从axf文件里读取而axf的地址又是由IROM1的Start决定的所以只要Start改对了bin文件就是对的。还有一种可能是把普通hex文件当成bin发送了hex有地址偏移结构完全不同代码里根本不认。所以务必确认发送的是纯二进制bin不是hex。4.5 擦除Flash后串口无响应偶尔会遇到Bootloader擦除App区后串口怎么发数据都没有反应。这种情况排查下来多是Flash擦写操作把USART的时钟配置或者GPIO配置给影响了。HAL库的Flash操作会触发PRECISERR或者总线错误如果中断不安全可能会导致程序跑飞。解决方法是擦除Flash前确保串口发送和接收的缓冲区已经准备好擦除期间关闭串口中断等擦除完成、USART重新初始化后再恢复通信。我在代码里特意把擦除操作放在了状态机里擦除前发一个“Erasing”状态提示擦完再发“Erase Done”这样上位机等回复时就知道什么时候可以开始发数据了。5. 源码包扩展思路与个人建议一套IAP框架跑通之后你会发现它的价值远不止“能用串口升级固件”这么简单。这个框架往深了扩展可以做很多事加密升级在Bootloader里加AES-128解密bin文件上传前先加密防止固件被抄板。C8T6主频72MHz解密48KB固件也就多花几秒完全可以接受。回滚机制预留两个App区A区升级前先把旧固件备份到B区新固件跑飞了还能自动切回旧版本。代价是App可用空间减半适合对可靠性要求高的设备。断点续传记录最后写入的页数和帧号掉电后重新握手时从断点继续传不算复杂但极大提升体验。多通道支持把串口接收改成类似接口就能很容易扩展到ESP8266的WiFi空中升级、4G模块的远程升级核心的Flash写入和跳转逻辑完全复用。我在实际项目里用的就是这套框架改出来的版本把串口通信层换成自定义协议后配合一个带4G模块的网关实现了现场设备的远程固件更新省下大量售后差旅成本。最后再分享一个小技巧调试Bootloader时别一上来就搞真升级。先在App工程里写个简单的LED翻转程序Bootloader也做好然后反复测试升级和跳转确认链路通畅后再把真实业务代码集成进来。这样把问题隔离在最小范围定位Bug会快很多。IAP本身的风险就在于“改错一步变砖”但一旦跑通它给你的产品带来的维护便利性绝对值得投入这点时间。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价