STM32串口烧写实战:FlyMCU与Bootloader原理详解
1. 为什么还要折腾串口烧写这件事
手里攥着一块STM32最小系统板,J-Link和ST-Link都在抽屉里躺着,但偏偏遇到产线小批量试产、客户现场返修、或者板子已经把SWD接口封在结构件里拆不出来的情况——这时候能救场的,往往就是那根不起眼的USB转TTL线。串口烧写(ISP,In-System Programming)不是什么新鲜技术,但真正把它用稳、用透、出了问题能自己定位的人并不多。FlyMCU这个工具在圈子里流传很广,界面朴素得像上个时代的产物,可它背后调用的是芯片出厂固化的Bootloader,走的是STM32系统存储器里的那段引导程序,这条路子跟调试器烧写完全是两套逻辑。
这篇文章想解决的问题很具体:让你在没有调试器、或者调试器接口不可用的情况下,仅凭一个USB转串口模块和FlyMCU,把程序可靠地写进STM32。适合谁看?刚入门的嵌入式学习者、需要做小批量烧录的产线技术员、以及那些手头项目已经定型、不想再为烧写专门留调试口的开发者。我会把Bootloader的启动条件、BOOT引脚的电平组合、FlyMCU的配置项含义、以及我实际踩过的坑都摊开讲,重点放在“为什么这么配”和“出问题怎么查”上,而不是简单罗列步骤。
需要提前说明的是,串口烧写依赖芯片内部的系统Bootloader,不同系列(F1、F4、L4等)的Bootloader版本和协议细节有差异,本文以最常见的STM32F103系列为主线展开,其他系列在关键差异处会单独标注。另外,FlyMCU本质是一个串口ISP上位机,它做的事情就是按照芯片厂商定义的同步协议,把HEX文件分包发进去,理解了这个协议框架,你排查问题时就不会只盯着“点下载没反应”干着急。
2. 串口烧写的底层逻辑与方案选型
2.1 Bootloader是怎么被唤醒的
STM32上电或复位后,芯片会采样BOOT0和BOOT1两个引脚的电平,决定从哪块存储区域取指令。这个机制是硬件层面的,跟你在代码里写了什么完全无关。以F103为例,BOOT0接高、BOOT1接低时,芯片从系统存储器启动,那段区域是出厂时固化的Bootloader,用户改不了也擦不掉;BOOT0接低时从主闪存启动,也就是你正常跑程序的状态。
这里有个容易被忽略的细节:BOOT引脚的电平是在复位信号的上升沿被锁存的,不是持续采样。也就是说,你先把BOOT0拨到高电平,再按一下复位键,芯片才会进入Bootloader;如果只是拨了开关没复位,芯片还停留在原来的运行状态。我见过不少人拨完开关就直接点下载,然后抱怨“连不上”,其实芯片压根没进ISP模式。
系统Bootloader启动后,会初始化USART1(F103上是PA9为TX、PA10为RX),然后等待上位机发来同步字节0x7F。收到同步字节后,Bootloader回一个ACK(0x79),握手就算建立。之后上位机发命令、Bootloader回响应,整个协议是命令-应答式的,每一步都有校验。FlyMCU做的就是把这套流程封装成图形界面,你点“下载”时它在背后跑的就是这一串交互。
2.2 为什么选FlyMCU而不是官方工具
官方有STM32CubeProgrammer,功能更全,支持串口、USB DFU、SWD多种通道。但在纯串口ISP这个场景下,FlyMCU有几个实际优势:体积小、免安装、启动快,在一台老旧的工控机上双击就能跑;界面上的“读芯片”“擦除”“写Flash”“校验”按钮一目了然,产线工人培训五分钟就能上手;最关键的是它对HEX文件的解析比较宽容,一些由老版本编译器生成的、格式不太规范的HEX文件,CubeProgrammer可能会报错,FlyMCU往往能正常处理。
当然FlyMCU也有短板:它不支持命令行批量操作,做自动化烧录得另想办法;对新型号芯片的器件ID识别依赖内置数据库,太新的片子可能认不出来。所以我的建议是,日常开发调试用CubeProgrammer或调试器,产线小批量、现场应急、教学演示这类场景用FlyMCU,两者不冲突。
2.3 硬件链路的几个硬性条件
USB转TTL模块的选择有讲究。市面上常见的CH340、CP2102、FT232都能用,但要注意电平匹配:STM32的USART是3.3V电平,如果你的模块输出是5V TTL,长期直连可能损伤芯片的IO。稳妥做法是选带3.3V/5V电平选择跳线的模块,拨到3.3V档;或者模块本身是3.3V输出的。我实测过5V模块直连F103的情况,短时间能用,但RX引脚长期承受5V,属于超规格使用,不建议这么干。
接线只有三根是必须的:模块的TX接芯片的PA10(RX),模块的RX接芯片的PA9(TX),GND对接GND。注意是交叉连接,TX对RX。很多人第一次接会接成TX对TX,然后死活通不了。另外,如果目标板是独立供电的,USB转TTL的VCC不要接,避免两个电源打架;如果目标板没电,可以用模块的3.3V输出给板子供电,但要确认模块的3.3V输出电流够不够(CH340的3.3V输出通常只有几十毫安,带不动整板,只适合给最小系统供电)。
BOOT0的处理方式取决于你的板子设计。有拨码开关或跳线帽的最省事;没有的话,可以用杜邦线把BOOT0接到3.3V,烧写完再拔掉接回GND。我个人的习惯是在自己的测试板上焊一个两位拨码开关,BOOT0和BOOT1各一位,省得每次插拔杜邦线。
3. FlyMCU配置项逐个拆解与实操要点
3.1 端口与波特率的取舍
打开FlyMCU,第一件事是选端口。这里有个坑:如果你插上USB转TTL后,设备管理器里能看到COM号,但FlyMCU的下拉列表里没有,点一下旁边的刷新按钮通常就能出来。如果还是没有,检查驱动是否装好,CH340在Win10以上系统有时需要手动装驱动。
波特率的选择是个权衡。FlyMCU默认115200,这个速率在大多数情况下够用,烧一个几十KB的工程大概几秒钟。想更快可以上到230400甚至460800,但前提是你的USB转TTL模块和线材质量过关。我用过一根劣质USB线,115200下正常,提到230400就频繁校验失败,换线后问题消失。所以波特率不是越高越好,稳定优先。产线环境如果线缆较长、干扰较大,反而建议降到57600,牺牲一点速度换可靠性。
还有一个隐藏参数是“校验方式”,FlyMCU界面上通常有“偶校验”和“无校验”的选项。STM32的Bootloader协议默认使用偶校验(8位数据+1位偶校验),这个必须跟芯片侧一致,选错了握手就过不去。大部分情况下保持默认即可,但如果你用的是某些特殊的串口转接方案(比如经过隔离器),校验位可能被改动,这时候要回来检查这一项。
3.2 HEX文件与“编程到文件末尾”的含义
加载HEX文件后,FlyMCU会显示文件大小和起始地址。这里要理解一个概念:HEX文件里记录的地址是芯片Flash的绝对地址,FlyMCU会按照这些地址去写,而不是从0x08000000无脑顺序写。所以如果你的工程链接脚本里把程序起始地址设成了0x08004000(比如做了IAP升级,前面留了Bootloader区),FlyMCU会正确地写到0x08004000,不会覆盖前面的引导程序。这一点比某些只会从固定地址开始写的工具要灵活。
“编程到文件末尾”这个选项,字面意思是只写到HEX文件记录的最后地址,后面的Flash区域不动。如果不勾选,有些版本的FlyMCU会按芯片全容量擦除后再写。对于做IAP升级的场景,这个选项很关键:你要保留前面的Bootloader区,就必须确保擦除范围不越界。我的做法是烧写前先用“读芯片”功能确认一下目标区域的内容,烧完再读回来比对,虽然多花几十秒,但能避免把引导程序擦掉的惨剧。
3.3 擦除、写入、校验的时序关系
FlyMCU的“下载”按钮背后其实是一串动作:握手、发擦除命令、等待擦除完成、逐页写入、最后整体校验。擦除是以页为单位的,F103小容量型号页大小1KB,中容量2KB。擦除命令发出后,芯片内部要花时间执行,这段时间上位机在等应答,界面上可能看起来“卡住”了,其实是正常的。如果板子上Flash容量大、擦除范围广,等个几秒甚至十几秒都正常,不要急着拔线。
写入阶段是按数据包发的,每包最多256字节,Bootloader每收一包回一个ACK。如果某一包没收到ACK,FlyMCU会重试,重试几次还不行就报错。校验阶段是把刚写进去的内容读回来逐字节比对,这一步能抓出写入过程中的位翻转。我遇到过校验失败但重新烧一次就好的情况,多半是接触不良或电源波动导致的偶发错误;如果每次都失败在同一地址,那就要怀疑Flash该区域是不是坏了,或者HEX文件本身有问题。
注意:擦除操作不可逆,烧写前务必确认HEX文件的地址范围没有覆盖到不该动的区域,尤其是做了IAP分区设计的板子。
3.4 读芯片与器件ID识别
FlyMCU的“读芯片”功能会发命令获取芯片的器件ID和Flash容量。这个ID是芯片出厂时写死的,可以用来确认你连上的是不是预期的型号。比如F103C8的ID对应64KB Flash,如果读出来显示128KB,那可能是C8T6的“扩容版”(实际是C8的die但标了更大容量),也可能是你连错了板子。产线批量烧录时,先读一颗确认型号,再批量操作,能避免把A型号的程序烧进B型号的板子。
器件ID读不出来,通常意味着握手阶段就失败了,问题出在硬件链路或BOOT模式上,跟HEX文件无关。这时候别在软件配置里打转,回头查接线和BOOT引脚。
4. 完整烧写流程与现场操作记录
4.1 从零开始的一次烧写实录
拿一块F103最小系统板,目标是把一个点亮LED的工程烧进去。第一步,把USB转TTL的3.3V、GND、TX、RX分别接到板子的3.3V、GND、PA10、PA9,注意TX-RX交叉。第二步,BOOT0用杜邦线接到3.3V,BOOT1接GND(F103的Bootloader不依赖BOOT1,但接低是稳妥的)。第三步,插上USB,设备管理器确认COM号,比如COM5。
打开FlyMCU,端口选COM5,波特率115200,校验位偶校验。点“读芯片”,如果一切正常,下方信息栏会显示器件ID和Flash容量。这一步成功,说明握手通了,链路没问题。然后点“打开文件”加载HEX,确认文件大小和起始地址合理。勾选“编程到文件末尾”和“校验”。点“下载”,观察进度条和信息栏:先擦除,再写入,最后校验,全程大概几秒到十几秒。
烧完后,把BOOT0拨回GND,按一下复位键,程序就跑起来了。如果LED没亮,先别怀疑烧写失败,用调试器读一下Flash内容确认程序确实写进去了,再查程序本身的逻辑。我见过烧写成功但LED不亮的情况,最后发现是工程里LED的引脚定义跟板子实际接线对不上,跟烧写过程无关。
4.2 批量烧录的节奏控制
小批量烧录时,如果每烧一颗都要手动点一次“下载”,效率低还容易漏。FlyMCU没有内置的批量队列,但可以利用它的“自动下载”选项(部分版本有),配合产线夹具实现半自动:夹具压针接触测试点,操作员按一下启动按钮,上位机自动执行握手-擦除-写入-校验。如果没有自动选项,就老老实实按流程走,但可以把BOOT0的切换做成夹具上的联动开关,减少手动插拔。
批量场景下我建议每烧10颗抽检一颗,用“读芯片”加校验的方式确认。另外,烧录工位的电源要稳,USB口供电能力参差不齐,最好用带外部供电的USB Hub,避免多设备同时插导致电压跌落。
4.3 关键参数速查表
| 参数项 | 推荐值 | 说明 |
|---|---|---|
| 波特率 | 115200 | 稳定优先,线材好可上230400 |
| 校验位 | 偶校验 | 与Bootloader协议一致,不可随意改 |
| 数据位 | 8 | 固定 |
| 停止位 | 1 | 固定 |
| BOOT0 | 高电平(烧写时) | 复位上升沿锁存,拨完要复位 |
| BOOT1 | 低电平 | F103上非必需,接低稳妥 |
| 接线 | TX-RX交叉,GND共地 | 3.3V电平匹配 |
| 擦除范围 | 按HEX地址 | 做IAP时务必确认不越界 |
5. 故障排查实录与避坑经验
5.1 握手失败的三层排查法
“读芯片”没反应,是最常见的故障。我的排查顺序是:先查物理层,再查配置层,最后查芯片层。物理层看接线是否交叉、GND是否共地、USB转TTL的TX/RX指示灯在发数据时是否闪烁(不闪说明上位机根本没发出去,可能是端口选错)。配置层看波特率和校验位是否匹配,端口号是否选对。芯片层看BOOT0是否真的在高电平、复位是否执行过、芯片是否供电正常。
有一次我折腾了半小时,最后发现是杜邦线内部断了,外表看不出来,万用表一量才露馅。所以线材要备几根好的,出问题先换线,这是成本最低的排除法。
5.2 校验失败的几种典型原因
校验失败说明写进去的内容跟HEX文件对不上。偶发失败重烧一次就好,多半是电源波动或接触不良。固定地址失败,先确认HEX文件本身是否完整(用文本编辑器打开看末尾有没有异常),再考虑Flash该区域是否损坏。还有一种情况是芯片被读保护了,读保护状态下Flash内容读不出来,校验自然失败,这时候需要先解除读保护(会全片擦除),再重新烧写。
读保护的判断方法是:能握手、能擦除、能写入,但一校验就报错,且读芯片时Flash内容全是0xFF或乱码。解除读保护的操作在FlyMCU里通常没有直接按钮,需要用官方工具或调试器执行,这也是为什么我建议手边常备一个ST-Link,专门处理这类“疑难杂症”。
5.3 烧写成功但不运行的排查思路
程序烧进去了,复位后不跑,先确认BOOT0是否已经拨回低电平。这是最容易被遗忘的一步,芯片还在Bootloader里等着,当然不跑用户程序。BOOT0没问题的话,用调试器连上去看PC指针停在哪,是HardFault还是卡在启动文件的某个循环里。还有一种情况是程序本身依赖的外部晶振没起振,而Bootloader阶段不涉及晶振,所以烧写一切正常,一跑用户程序就卡住。
5.4 常见问题速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 读芯片无响应 | 接线错误/BOOT0未拉高/未复位 | 查交叉接线、拨BOOT0后复位 |
| 握手成功但擦除失败 | 芯片读保护/Flash损坏 | 解除读保护后重试 |
| 写入中途报错 | 电源不稳/线材质量差 | 换线、降波特率、外部供电 |
| 校验失败(偶发) | 接触不良/干扰 | 重烧、检查GND |
| 校验失败(固定地址) | Flash坏块/HEX异常 | 换芯片验证、检查HEX |
| 烧写成功不运行 | BOOT0未拨回/程序问题 | 拨回BOOT0、调试器查PC |
6. 几个容易被忽略的细节
串口烧写的速度瓶颈不在波特率,而在擦除和校验。擦除是芯片内部操作,快不了;校验要把整个文件读回来比对,文件越大越慢。所以优化烧录时间,优先考虑减小HEX文件体积(比如开优化、去掉调试信息),而不是一味拉高波特率。
USB转TTL模块的TX/RX指示灯是个好东西,发数据时TX灯闪、收数据时RX灯闪,能直观判断数据有没有出去、有没有回来。握手阶段如果TX闪了RX不闪,说明芯片没应答,问题在芯片侧;如果TX都不闪,问题在上位机或端口。
最后说一个我自己的习惯:每次烧写前,先用“读芯片”确认器件ID和容量,再加载HEX,烧完再读一次目标区域的前几十个字节跟HEX比对。多花十几秒,但能避免“以为烧进去了其实没烧进去”的尴尬,尤其是在产线环境里,返工的成本远高于这十几秒。