上周在车间盯产线的时候,碰到一个挺典型的场面:一批板子卡在烧录工位上,操作员来回插拔串口线,一条线一条线地等进度条爬。那批板子用的是512KB镜像,115200的波特率,单块烧下来四十多秒,一个班下来产能被烧录环节死死压住。当时我就把调试器接上,改用JTAG口的SWD模式重新跑了一遍,同样的镜像,七秒出头就完事,前后一对比,差出来6.8倍。这个数字不是拍脑袋来的,是我在固定平台上反复测了二十多次取的平均值。
固件烧录这件事,看起来只是把bin文件写进芯片,但选JTAG还是UART,直接决定了研发阶段一天能迭代多少次、量产线一天能出多少货、现场升级要等多久。这篇就把实测的过程、数据、踩过的坑完整摊开讲,涉及STM32、GD32这类常见MCU,也顺带说ESP32、DA14585这些典型烧录场景。不管你是刚上手的新人,还是天天跟烧录工具打交道的老手,应该都能从里面找到能直接抄的部分。
1. 为什么烧录速度值得较真:从产线的一次卡顿说起
很多人觉得烧录慢一点无所谓,反正就等几分钟。这种想法在实验室里成立,一旦放到批量场景就会变成真金白银的成本。我算过一笔账:单块板子烧录时间从47.6秒压到7秒,按一个工位一天8小时、良率95%算,单工位日产能从大约570块提到3900块以上。哪怕只上两个烧录工位,一天多出来的产出也够覆盖一台中端调试器的钱了。更关键的是研发阶段,改一行代码要重新烧一次,一天迭代二三十次的话,光等待就能吃掉一个多小时。
除了速度,还有稳定性这个隐性收益。UART下载依赖目标板的Bootloader,一旦Bootloader被误擦或者芯片被读保护锁住,串口这条路就直接断了,得靠调试器来救场。反过来,JTAG/SWD是从芯片的调试接口直接下指令,不依赖用户程序,只要调试口没被彻底关死,基本都能把板子救回来。所以我的习惯是:开发阶段一律走SWD,量产测试环节才考虑要不要切UART做降本。
1.1 6.8倍这个比值是在什么条件下测出来的
数字脱离条件就没意义,所以我先把边界条件说清楚。本次实测平台是一块STM32F407VET6核心板,主频168MHz,片内Flash 512KB,镜像是一个完整的功能固件,大小刚好512KB(524288字节),包含代码段、常量表和一段初始化参数区。JTAG侧用的是ST-Link V2,工作在SWD模式,SWCLK拉到4MHz;UART侧用的是CH340转串口模块,波特率115200,8N1,配合芯片内置的系统Bootloader下载。
两边的软件入口也不同。SWD走的是STM32CubeProgrammer的SWD烧录通道,直接操作Flash控制器;UART走的是系统Bootloader,通过同步握手后分包写入。两边的校验都开着,用的是最常用的CRC校验。测出来JTAG侧平均7.0秒,UART侧平均47.6秒,比值6.8。需要说明的是,这个6.8是特定条件下的结果,换芯片、换调试器、换波特率,比值都会变,后面第3节我会把变量一个个拆开讲。
1.2 JTAG与UART到底差在哪一层
要理解这个差距,得从物理层往上捋。UART是异步串行,收发双方没有共享时钟,靠约定的波特率各自采样,为了采准,每个字节要额外加起始位和停止位,8N1下每传1个有效字节实际要发10个bit。115200波特率下,理论有效速率就是115200除以10,等于11520字节每秒。这还只是理论值,实际还要被握手、分包、等待Flash编程等环节吃掉一部分。
JTAG/SWD是同步串行,调试器提供独立的时钟线,数据和时钟严格对齐,不需要起始位停止位这种开销,每拍就能传1个bit,甚至一拍传多个bit。更关键的是,SWD是专门为调试和烧录设计的协议,它把Flash的擦除、编程、校验都做成了一条条命令,调试器可以直接指挥Flash控制器干活,几乎不占用芯片的CPU资源。一个是被动地喂数据,一个是主动地下命令,本质差别就在这儿。你可以把UART想象成用勺子一勺一勺往桶里倒水,JTAG则像直接接管了水龙头。
2. 实测方案怎么搭:平台、接线与计时方法
测速这事儿最怕口径不一致,我见过有人把一键下载工具的界面刷新时间也算进去的,也见过只算擦除时间不算校验的。为了让结果能复现,我把平台、接线、工具链和计时方式都固定下来,下面逐项交代。
2.1 测试平台与器件清单
选型上我特意挑了市面上最常见的组合,避免用太冷门的器件导致结果没有参考价值。主控用STM32F407是因为它的Flash编程特性资料最全,社区踩坑记录也最多。调试器选ST-Link V2而不是J-Link,是因为前者几乎是每个新手盒子里的标配;UART侧同时准备了CH340、CP2102和FT232R三种转串口模块做交叉验证,因为不同芯片的实际吞吐差异其实挺明显。
| 项目 | 型号/参数 | 备注 |
|---|---|---|
| 主控 | STM32F407VET6 | 168MHz,Flash 512KB |
| 调试器 | ST-Link V2 | SWD模式,SWCLK 4MHz |
| 串口芯片 | CH340 / CP2102 / FT232R | 交叉对比 |
| 镜像 | 512KB bin | 含代码与参数区 |
| 上位机 | 常规商用笔记本 | USB 2.0直连,不经过HUB |
| 供电 | 独立5V/2A适配器 | 避免调试器供电不足 |
接线方面,SWD最少只需要四根:SWDIO、SWCLK、GND、3V3,再加一根NRST会更稳。UART需要TX、RX、GND三根,另外把BOOT0拉高、BOOT1拉低进入系统Bootloader。这里有个新手常踩的坑:UART的TX/RX必须交叉接,也就是模块的TX接板子的RX,模块的RX接板子的TX,直连是收不到数据的,很多人对着接线图看半天以为接对了,其实就是没交叉。
2.2 镜像与工具链选择
镜像固定成512KB是为了方便换算,实际项目里镜像大小可以从几十KB到几MB不等,倍数关系会随镜像变大而略微收窄,因为擦除时间是按整片或按扇区计的,属于固定开销,镜像越大这块开销占比越低。工具链这边,SWD侧我用了STM32CubeProgrammer的命令行版本,方便脚本化重复跑;UART侧也用它自带的UART通道,保证两边是同一套软件逻辑,减少变量。
我特意没有用那种一键下载的图形工具,因为它们的进度条和内部重试机制会干扰计时。命令行方式的好处是每个阶段都有明确的时间戳,擦除多久、写入多久、校验多久都能单独抠出来。如果你手头只有图形工具,问题也不大,但建议至少把日志打开,很多工具在后台会默认做一次全片校验,这一项在小镜像时看不出来,镜像一大就非常明显。
2.3 计时口径与误差控制
计时口径我定的是从点击开始烧录到软件提示校验通过为止,中间包含连接、擦除、写入、校验四个阶段。为了降低误差,每一边都跑25次,去掉最快和最慢的各3次,取中间19次的平均值。控制变量方面,全程用同一个USB口、同一根线材、同一个电源,环境温度控制在25度左右。为什么要这么讲究?因为Flash编程速度对温度其实有轻微影响,芯片温度高的时候编程会略快,但这个差异通常在个位数百分比,不至于影响结论,只是让数据更干净。
另外提醒一句,别在虚拟机里测速。我试过一次在虚拟机里跑,USB透传带来的延迟和抖动让UART侧的耗时凭空多了三四秒,SWD侧也不稳定。要测就老老实实在物理机上测,或者至少确认USB控制器是直通的。
3. 数据拆解:6.8倍差距到底出在哪个环节
光看总时间只能得出快慢,看不出原因。所以我把每次烧录的三个主要阶段分别记了时间,这样就能定位到差距到底来自哪一段,也能判断优化该往哪个方向使劲。
3.1 原始测试数据
下面这张表是三种转串口芯片在115200下的表现,以及SWD的对照数据。可以看到CH340和CP2102的差异在1秒以内,FT232R略快一点点,但整体量级一致,说明瓶颈不在串口芯片本身,而在协议和波特率。SWD那边的数据则明显低一个数量级。
| 烧录方式 | 平均耗时 | 最快 | 最慢 | 校验方式 |
|---|---|---|---|---|
| JTAG/SWD(4MHz) | 7.0s | 6.8s | 7.4s | CRC |
| UART + CH340(115200) | 47.6s | 46.9s | 48.5s | CRC |
| UART + CP2102(115200) | 47.2s | 46.5s | 48.1s | CRC |
| UART + FT232R(115200) | 46.4s | 45.8s | 47.3s | CRC |
把7.0和47.6一除,得到6.8。这个倍数在小镜像下会更大,比如128KB镜像时,固定开销占比上升,UART侧约13秒,SWD侧约3.2秒,倍数能到4左右反而变小——注意这里方向是反的。真正让倍数拉开的是大镜像,因为UART的传输时间随镜像线性增长,而SWD的写入时间增长斜率要平缓得多。
3.2 擦除、写入、校验三段耗时占比
把三个阶段拆开看,结论就清楚了。SWD的7秒里,擦除占1.2秒,写入占4.5秒,校验占1.3秒;UART的47.6秒里,握手和擦除占3.5秒,数据传输加写入占40.2秒,校验占3.9秒。可以看到UART侧有超过八成的时间花在传输本身,这正是波特率限制的直接体现。
| 阶段 | JTAG/SWD | UART(115200) | 主要瓶颈 |
|---|---|---|---|
| 连接与握手 | 0.3s | 1.8s | 同步字节与波特率协商 |
| 擦除 | 1.2s | 1.7s | 芯片内部Flash控制器 |
| 写入/传输 | 4.5s | 40.2s | 串口速率与分包ACK |
| 校验 | 1.3s | 3.9s | 回读带宽 |
写入阶段是差距的主战场,40.2秒对比4.5秒。原因前面说过,UART每传一字节要额外花掉两个bit的开销,而且Bootloader为了保证可靠,通常每写一包还要等一个ACK,一来一回又是一段时间。SWD则把命令和数据打包进SWCLK的节拍里,几乎不存在等待。
3.3 为什么提高波特率也追不上
有人会问,那把波特率提到921600甚至2M不就完了?理论上确实可以。按115200算,纯传输512KB需要45.5秒,如果提到921600,传输时间应该压到5.7秒左右,加上擦除校验,理论总时间约11秒,看起来能追到只差一点。但现实里有两个坎:一是很多芯片的出厂Bootloader只支持固定几档波特率,115200是最保险的;二是USB转串口芯片在高波特率下的稳定性会下降,实测CH340在921600下每跑十次就有一两次丢包重传,反而拖慢整体。
我实测过把CP2102提到460800,平均耗时降到14.8秒,已经能打,但前提是目标Bootloader支持,且线材质量过关。如果镜像不大、又必须走串口,提高波特率确实是性价比最高的优化。可一旦面对多MB的大镜像,或者Bootloader锁了固定波特率,JTAG/SWD的优势就没法被追平。顺便说一句,用J-Link把SWCLK提到12MHz时,我测到过3.9秒的成绩,那时候倍数直接奔着12倍去了。
4. 两条路线的完整实操
数据看完,接下来是能直接照着做的部分。我把两种方式的完整流程拆成可执行的步骤,每一步都说明为什么这么做,避免你只会点按钮不懂原理。
4.1 JTAG/SWD 烧录全流程
先接线。ST-Link V2的SWDIO接板子的SWDIO(STM32上是PA13),SWCLK接SWCLK(PA14),GND接GND,3V3接板子供电(如果板子外部供电,这一根可以不接,但两地必须共地),NRST建议接上,方便用复位模式连接。接线长度尽量短,超过15厘米建议加屏蔽,否则高时钟下容易误码。
接着配置并执行。用STM32CubeProgrammer的命令行版本,一条命令就能跑完:
STM32_Programmer_CLI -c port=SWD mode=UR freq=4000 \ -e all \ -w firmware.bin 0x08000000 \ -v \ -rst这里的mode=UR表示用复位模式连接,也就是先拉住NRST再建立连接,能在程序跑飞或者调试口被占用时救回板子。freq=4000是SWCLK频率4MHz,如果连接不稳就往下调,比如2000或1000,稳定性比速度重要。-e all是全片擦除,量产时如果只是更新应用区,可以改成按扇区擦除,能省下不少时间。-v是烧完校验,-rst是烧完复位运行。
如果你用的是J-Link,J-Flash的命令行类似,核心参数是接口速度-speed和地址范围。OpenOCD用户则写好cfg文件后用program命令。要注意的是,量产环境我建议把日志输出到文件,出现坏板时能回溯到底是哪一步失败的。
4.2 UART Bootloader 烧录全流程
UART这条路的前置条件是芯片内置系统Bootloader没被破坏。以STM32为例,进入方式是上电时把BOOT0拉高、BOOT1拉低,然后复位。接线是模块TX接板子RX(通常是PA10),模块RX接板子TX(PA9),GND共地。连接前先确认转串口芯片的驱动装好了,CH340、CP2102、FT232R的驱动各不相同,装错会出现端口能识别但一收发就报错的情况。
进入Bootloader后,上位机会先发一个0x7F同步字节,芯片回0x79表示就绪。用命令行方式:
STM32_Programmer_CLI -c port=COM3 br=115200 \ -e all \ -w firmware.bin 0x08000000 \ -v跑起来后你会看到进度条一格一格走,40多秒是正常节奏。这里有两个实操要点:第一,握手阶段如果反复失败,先把波特率降到9600试一次,能通说明是速率不匹配;第二,写入完成后别急着断电,等校验通过再操作,否则可能出现半写状态,板子直接不启动。
4.3 关键参数计算与验证
动手之前先算一遍理论值,心里有底。公式很简单:有效速率等于波特率除以每字节位数,8N1下每字节10位。115200除以10等于11520字节每秒。512KB即524288字节,除以11520约等于45.5秒。实测47.6秒,效率约95.6%,说明Bootloader的分包和ACK开销控制得还不错,这个数字可以当作同类方案的参考基线。
再看SWD侧。STM32F407的Flash编程是按字或按行写入的,一行256字节,512KB对应2048行,每行的编程时间由芯片内部电荷泵决定,典型值在2毫秒上下,光编程就要4秒左右,加上擦除和校验,7秒完全对得上。想验证自己板子的实际编程速度,可以只烧一个固定大小的测试区块,用秒表掐一下,多测几次取平均,比看文档更准。
5. 常见故障与排查实录
这部分是我这些年踩坑攒下来的,很多问题在官方文档里找不到,但现场一撞一个准。按故障类型分类整理成速查表,遇到问题可以对照着排查。
5.1 JTAG链识别失败类
最经典的报错就是error (209040): can't access jtag chain和error (209053): unexpected error in,还有OpenOCD下的swd/jtag communication failure。这几个报错出现时,先别怀疑芯片坏了,九成是连接或时序问题。
| 报错现象 | 可能原因 | 处理办法 |
|---|---|---|
| can't access jtag chain | 接线松、时钟太快、目标未供电 | 检查SWDIO/SWCLK,降频到1MHz |
| swd/jtag communication failure | NRST未接、程序抢占调试口 | 用connect under reset模式 |
| 识别到ID但读不到Flash | 读保护开启 | 解除RDP前先备份,会全片擦除 |
| 偶发连接失败 | 线材过长、电源纹波大 | 缩短线材,加去耦电容 |
还有一个特别隐蔽的坑:代码里如果调用库函数把JTAG功能禁用了,只保留SWD,那么标准JTAG模式就连不上,必须切SWD。有些项目甚至把SWD引脚也复用成普通GPIO,结果烧完程序板子就再也连不上,只能靠复位模式或者擦除引脚配置来救。解决思路是:在固件启动早期留一小段延时,别一上电就把调试口改掉;或者干脆保留SWD不放,只关JTAG的几条多余引脚。
5.2 UART进不去Bootloader类
串口这条路最常见的三个问题:进不了Bootloader、握手失败、中途断连。进不了先查BOOT0电平,用万用表量一下是不是真的拉高了,跳线帽接触不良很常见。握手失败多数是波特率或TX/RX接反,交叉接线这个再强调一次。中途断连通常是USB转串口芯片的驱动或供电问题,FT232R和CP2102在长时间大数据量传输时表现会更稳一些。
另外,部分芯片的系统Bootloader会检查某个选项字节,如果之前被改过,UART通道可能直接不可用,这种时候只能切回SWD救。还有一类是镜像本身的问题,比如bin文件地址偏移没对,烧进去后校验能过但跑不起来,这就不属于通信故障了,得回头查链接脚本。
5.3 速度不达预期的隐藏因素
明明改成了SWD,为什么还是慢?我遇到过几种情况。一是调试器时钟没调,默认可能只有几百KHz,手动提到4MHz甚至更高就有明显提升。二是USB走了HUB,尤其是那种不带独立供电的HUB,带宽和供电都打折扣。三是软件默认做了全片擦除加全片校验,镜像只有100KB也擦整个512KB,白白浪费时间。
还有一个容易忽略的点:某些工具在烧录前会自动做一次芯片读回校验,读回是通过调试口慢慢读的,512KB读回来也要一两秒。如果你追求极限速度,可以在确认镜像没问题后关掉这一项,但量产烧录不建议关,校验是拦截坏片的最后一道闸。
6. 选型建议与场景匹配
测了这么多,最后落到怎么选。我的原则是不搞一刀切,按场景分。研发调试、量产烧录、现场升级这三种场景的诉求完全不同,盲目追快或者一味省成本都不对。
6.1 研发、量产、现场升级三种场景
研发阶段无条件选SWD。改一行烧一次,7秒和47秒的体验天差地别,而且SWD能救砖,遇到程序跑飞随时能连回来。这个阶段别省调试器那点钱,J-Link或者好一点的ST-Link都值得。
量产阶段要算总账。如果产线已经买了带SWD的工装,直接用;如果产线空间有限、只留了串口排针,那就把波特率往上提,能到460800就用460800。量产还有一个技巧是并行烧录,一台上位机带多个调试器同时烧,比单条线提速更划算,前提是每个通道独立供电、独立USB控制器。
现场升级则要看设备条件。很多设备装在现场,只留了串口或者根本不留接口,这时候就得靠OTA或者串口升级。现场升级的镜像通常不大,波特率提到230400一般够用,重点反而是升级过程的掉电保护,别升到一半断电变砖。
6.2 固件加密与安全烧录的额外考量
现在提固件安全的越来越多,烧录环节也得考虑进去。常见做法有两类:一类是烧录时开启读保护,防止别人通过调试口把固件读走;另一类是固件本身做签名校验,Bootloader在启动前验证镜像签名。这两类都会影响速度。开启读保护后,调试口的某些操作会受限,校验阶段可能变慢;签名校验则是在烧录后或启动时额外算一次哈希,镜像越大耗时越明显。
我一般这么处理:研发阶段不开读保护,方便反复调试;量产时在最后一道工序统一开启读保护,并且把开启动作放到烧录脚本的最后一步,避免中途锁死。签名校验的密钥管理是另一门学问,这里不展开,只提醒一句:密钥一旦泄露,加密就形同虚设,别把私钥和烧录工具放在同一台联网机器上。
顺带说下固件加密对速度的实际影响。我做过的对比是,开启读保护后SWD烧录变化不大,因为写操作本身不受影响;但校验阶段因为不能随意回读,工具会改用另一种比对方式,时间可能增加10%到20%。所以如果你的烧录节拍卡得很死,这块也要预留时间。
回头看这套实测,最大的收获不是记住了6.8这个数,而是搞明白快在哪里、慢在哪里。我现在做新项目,第一步就是把SWD接口引出来,哪怕最后量产不用,留个后路总没错。另外提醒一句,不同批次芯片的Flash编程速度会有一点浮动,别拿一次测试结果当铁律,量产的节拍还是要留20%余量。后面如果大家感兴趣,我还可以把并行烧录和OTA升级的实测也整理出来,那又是另一套坑和另一些省时间的技巧。