STM32 开发这几年,工具链的变化其实挺大的。早些年大家烧程序基本就是 Keil MDK 里点一下 Download 按钮,或者用 J-Link 的 J-Flash 单独操作,再老一点用 ST-Link Utility。后来 ST 官方把 ST-Link Utility 停更了,全面转向STM32CubeProgrammer,这个工具现在已经是 STM32 生态里绕不开的一环。不管你是用 ST-Link、UART 串口、USB DFU 还是 OTA,它基本都能覆盖。而且它跨平台,Windows、Linux、macOS 都有对应版本,命令行模式还能直接塞进 CI 流水线里做自动化烧录。
我平时接触最多的场景有两类:一类是产线或者实验室里用 ST-Link 给 C8T6 这类小板子烧程序,另一类是板子已经焊死在设备里、SWD 引脚没引出来,只能靠USB 下载或者系统存储器启动模式来救场。这两种场景用 STM32CubeProgrammer 的姿势完全不一样,踩的坑也不同。下面我就按自己实际操作的顺序,把整个流程、原理和那些文档里不会写的细节都捋一遍,不管你是刚上手 STM32 的新人,还是想把这套工具用进自动化流程的老手,应该都能捞到点东西。
1. 先搞清楚 STM32CubeProgrammer 到底解决什么问题
1.1 它和 Keil、J-Flash 的分工区别
很多人第一次接触这个工具会有点懵:我 Keil 里明明能下载,为什么还要单独装一个 STM32CubeProgrammer?这个问题得从工具定位说起。
Keil MDK 的下载功能,本质上是把编译产物通过调试器(ST-Link 或 J-Link)写进 Flash,它依赖的是调试会话。也就是说,你得先能连上芯片的调试接口,Keil 才能下载。而 STM32CubeProgrammer 是一个独立的烧录工具,它不关心你的代码是怎么编译出来的,只关心"把这份二进制数据写进目标存储区"这件事。它支持的连接方式比 Keil 广得多:
- ST-Link:SWD 或 JTAG 接口,最常用的方式
- UART:通过串口 + Bootloader 下载,不需要调试器
- USB DFU:芯片内置的 USB 设备固件升级模式
- OTA:通过无线方式更新
这就意味着,当你的板子 SWD 引脚被占用、被锁、或者根本没引出来的时候,Keil 就无能为力了,但 STM32CubeProgrammer 还能通过串口或 USB 把程序灌进去。这是它最大的价值。
另外还有一个容易被忽略的点:STM32CubeProgrammer 能直接操作选项字节(Option Bytes),比如读写保护、BOR 电平、复位行为这些。Keil 里做这些操作很别扭,而在这个工具里就是点几下的事。所以哪怕你平时用 Keil 开发,手边备一个 STM32CubeProgrammer 也是刚需。
1.2 三种下载方式的适用场景对比
在动手之前,先想清楚你手上这块板子适合走哪条路。我把三种主流方式列个表,方便你对号入座:
| 下载方式 | 依赖硬件 | 是否需要调试器 | 典型场景 | 速度 |
|---|---|---|---|---|
| ST-Link (SWD) | SWD 四线 | 需要 ST-Link | 开发调试、产线烧录 | 快 |
| UART 串口 | TX/RX/GND + BOOT0 | 不需要 | SWD 被占用、救砖 | 中等 |
| USB DFU | USB D+/D- | 不需要 | 板子有 USB 口、现场升级 | 较快 |
选哪种,核心看两个条件:SWD 引脚是否可用,以及BOOT0 是否可控。如果 SWD 好好的,直接用 ST-Link,别折腾。如果 SWD 连不上,但 BOOT0 能拉高,那就走串口或 USB DFU。如果 BOOT0 也被焊死了,那就只能上电复位时序那套骚操作了,后面会讲。
提示:C8T6 这类最小系统板,BOOT0 通常有个跳线帽或者 0 欧电阻,改起来很方便。但如果是已经集成到产品里的板子,BOOT0 可能直接接地,这时候就得看芯片手册里系统存储器的进入条件了。
1.3 系统存储器启动模式的原理
要理解 UART 和 USB DFU 下载,必须先搞懂系统存储器启动模式。STM32 的 Flash 里其实有两块区域:一块是你自己写的用户程序区,另一块是 ST 出厂时就烧好的系统存储器(System Memory),里面存着一段 Bootloader。
芯片上电时,会根据 BOOT0 和 BOOT1 两个引脚的电平决定从哪里开始执行:
- BOOT0 = 0:从主 Flash 启动,也就是跑你自己的程序
- BOOT0 = 1,BOOT1 = 0:从系统存储器启动,跑 ST 的 Bootloader
- BOOT0 = 1,BOOT1 = 1:从 SRAM 启动,一般调试用
当芯片从系统存储器启动时,ST 的 Bootloader 会去检测各个外设接口(USART1、USART3、CAN、USB 等),等待上位机发来的下载命令。这时候你用 STM32CubeProgrammer 通过串口或 USB 连上去,就能把新的固件写进主 Flash。写完之后把 BOOT0 拉回低电平,重新上电,芯片就从主 Flash 跑新程序了。
这个机制的好处是:它不依赖你原来的程序是否正常。哪怕你之前烧进去的程序把 SWD 引脚复用成了普通 IO,或者开了读保护,只要 Bootloader 还在,你就能通过系统存储器模式把芯片救回来。这是嵌入式开发里非常重要的一条后路。
2. 用 ST-Link 下载:最常规也最容易翻车的方式
2.1 硬件连接与驱动确认
ST-Link 下载是最标准的路径,但恰恰因为太常规,很多人连接上就出问题。先把硬件接对:
STM32 的 SWD 接口一共四根线:
- SWDIO:数据线
- SWCLK:时钟线
- GND:地
- 3.3V:供电(可选,如果板子自己供电就不用接)
注意,ST-Link 上的引脚标法可能和板子上的标法不一样。常见的 ST-Link V2 小U盘那种,引脚顺序是 3.3V、SWDIO、SWCLK、GND,而板子上的 SWD 排针顺序可能是 GND、SWCLK、SWDIO、3.3V。接反了不会烧,但连不上,这是新手最常犯的错。我的习惯是先用万用表确认一下板子的 GND 和 3.3V,再对照 ST-Link 的丝印一根根接。
驱动方面,Windows 下装了 STM32CubeProgrammer 之后,ST-Link 的驱动会自动装上。如果你之前装过旧版 ST-Link Utility,可能会有驱动冲突,建议先在设备管理器里把旧的 ST-Link 设备卸载干净,再装新工具。Linux 下需要把 udev 规则加上,否则普通用户没权限访问 USB 设备,这个后面命令行部分会细说。
2.2 在 CubeProgrammer 里建立连接
打开 STM32CubeProgrammer,界面左上角是连接配置区。操作顺序是这样的:
- 在ST-LINK下拉框里选择你的调试器。如果只插了一个,通常显示 "ST-LINK/V2" 或 "ST-LINK/V3"
- Port选 SWD(除非你用 JTAG)
- Frequency先保持默认的 4000 kHz,连不上再往下调
- 点击右上角的Connect按钮
连接成功后,右侧会显示芯片的详细信息:Device ID、Flash 大小、UID 等。如果这里能看到正确的芯片型号和 Flash 容量,说明连接没问题,可以进入下一步。
如果点 Connect 报错,最常见的几个原因:
- 接线错误:SWDIO 和 SWCLK 接反,或者 GND 没接
- 目标板没供电:ST-Link 的 3.3V 输出电流有限,大板子带不动,得单独供电
- 频率太高:长排线或者干扰大的环境下,4000 kHz 连不上,降到 1000 kHz 甚至 500 kHz 试试
- 芯片被读保护:如果之前开了 RDP Level 1,连接时会提示需要先解除保护
注意:如果连接时提示 "No STM32 target found",先别急着怀疑芯片坏了。把频率降到 500 kHz,重新上电,再试一次。我遇到过好几次都是频率问题,不是硬件问题。
2.3 下载配置与选项字节设置
连接成功后,切换到Erasing & Programming标签页。这里有几个关键选项:
- File path:选择你的固件文件,支持 .hex、.bin、.elf 等格式
- Start address:.bin 文件必须手动填起始地址,通常是 0x08000000;.hex 和 .elf 自带地址信息,不用填
- Verify programming:建议勾上,下载完自动校验
- Run after programming:下载完自动运行,调试时很方便
点击Start Programming就开始烧录了。进度条走完,如果勾了 Verify,会再校验一遍,全部通过就说明成功了。
这里有个细节值得说:.bin 和 .hex 的区别。.bin 是纯二进制,没有任何地址信息,所以你必须告诉工具从哪个地址开始写。.hex 是 Intel Hex 格式,每行数据都带了地址,工具自己会解析。如果你拿不准,优先用 .hex,省心。但有些产线为了省空间会用 .bin,这时候起始地址填错,程序就跑不起来,而且现象很诡异——芯片能连上,但程序没反应。我踩过一次,地址填成了 0x08000000 之外的偏移,查了半天才发现。
选项字节(Option Bytes)在另一个标签页里。这里能改的东西包括:
- RDP:读保护等级,Level 0 无保护,Level 1 禁止读出 Flash
- BOR:欠压复位电平
- nRST_STOP / nRST_STDBY:低功耗模式下是否保持复位引脚
- Boot configuration:BOOT0 相关的配置
改选项字节要格外小心,尤其是 RDP。一旦从 Level 0 升到 Level 1,再想降回来会触发全片擦除,Flash 里的程序全没了。所以量产阶段如果要用读保护,一定要在最后一步做,别在调试阶段就开。
3. UART 串口下载:SWD 被占用时的救命稻草
3.1 BOOT0 拉高与串口接线
当 SWD 引脚被占用、或者你手上根本没有 ST-Link 的时候,串口下载就是最实用的方案。前提是芯片的 BOOT0 引脚可控。
操作步骤:
- 把BOOT0 拉高(接 3.3V),BOOT1 保持低电平(接地)
- 用 USB-TTL 模块连接芯片的USART1:TX 接 PA10(RX),RX 接 PA9(TX),GND 共地
- 给板子重新上电,让芯片进入系统存储器启动模式
这里要注意,不同型号的 STM32,Bootloader 支持的串口不一样。F103 系列通常是 USART1(PA9/PA10),有些型号还支持 USART3、CAN 等。具体看参考手册的 Bootloader 章节。接错串口,工具是连不上的。
USB-TTL 模块的选择也有讲究。CH340、CP2102、FT232 都可以,但要注意电平匹配。STM32 是 3.3V 电平,如果你的 USB-TTL 模块输出 5V,长期用可能会损伤 IO。建议选带 3.3V/5V 跳线的模块,拨到 3.3V 档。
3.2 在 CubeProgrammer 中配置 UART 连接
回到 STM32CubeProgrammer,这次连接方式选UART,而不是 ST-LINK。
配置项:
- Port:选择 USB-TTL 对应的 COM 口
- Baudrate:默认 115200,可以往上调,但 Bootloader 对波特率的容忍度有限,建议先用 115200
- Parity:Even(偶校验),这是 ST Bootloader 的默认要求,选错了连不上
- Data bits:8
- Stop bits:1
点 Connect,如果一切正常,会读到芯片信息。然后和 ST-Link 一样,在 Erasing & Programming 里选文件、填地址、点下载。
串口下载的速度比 ST-Link 慢不少,一个几十 KB 的固件可能要十几秒。如果固件大,可以考虑把波特率提到 230400 甚至 460800,但前提是你的 USB-TTL 和线材质量跟得上,否则会丢包导致下载失败。
提示:串口下载完成后,一定要把 BOOT0 拉回低电平,再重新上电。否则芯片会一直停在 Bootloader 里,你的程序永远不跑。这个错误太常见了,我见过不止一个同事对着板子发呆半小时,最后发现是跳线帽没拔。
3.3 串口下载失败的排查思路
串口下载失败的原因比 ST-Link 多,因为多了一层 USB-TTL 和 Bootloader 的交互。按这个顺序排查:
- 确认 BOOT0 真的拉高了:用万用表量一下 BOOT0 引脚对地电压,应该是 3.3V
- 确认串口线序正确:TX 和 RX 是交叉连接的,不是直连
- 确认 COM 口没被占用:串口助手、其他工具如果开着同一个 COM 口,CubeProgrammer 会打不开
- 确认 Parity 设置:必须是 Even,这是最容易忽略的
- 降低波特率:从 115200 降到 57600 或 9600 试试
- 检查供电:USB-TTL 的 3.3V 输出电流很小,如果板子功耗大,得单独供电
如果以上都试过还不行,可能是芯片的 Bootloader 版本问题,或者芯片本身有故障。这时候可以换一块板子验证,排除是工具还是硬件的问题。
4. USB DFU 下载:现场升级的优雅方案
4.1 DFU 模式的进入条件
USB DFU(Device Firmware Upgrade)是 STM32 内置的另一种下载方式,走的是芯片的 USB 外设。相比串口,它的速度更快,而且很多产品板本身就带 USB 接口,现场升级时不用额外接线。
进入 DFU 模式的条件和串口类似:BOOT0 拉高,从系统存储器启动。但有个额外要求:芯片的 USB 外设(通常是 PA11/PA12,即 USB_DM/USB_DP)必须引出来,并且 Bootloader 支持 USB DFU。
不是所有 STM32 都支持 USB DFU。F103 系列里,中等容量和以上容量的型号支持,小容量的可能没有。具体查对应型号的参考手册。
进入 DFU 模式后,电脑上会枚举出一个 USB 设备,在设备管理器里显示为 "STM32 BOOTLOADER" 或者类似的 DFU 设备。如果没识别到,检查:
- USB 线是不是数据线(有些线只能充电)
- PA11/PA12 有没有接反
- 芯片是否真的进入了系统存储器模式
4.2 用 CubeProgrammer 走 USB 下载
连接方式选USB,如果设备被正确识别,Port 下拉框里会出现对应的 USB 设备。选中后点 Connect,后面的操作和前面一样。
USB DFU 的下载速度明显比串口快,一个 100KB 左右的固件几秒钟就完事。而且它不需要额外的 USB-TTL 模块,一根 USB 线搞定,现场升级时特别方便。
但 DFU 也有它的局限:
- 需要芯片支持:不是所有型号都有 USB DFU Bootloader
- 需要 USB 接口可用:如果产品的 USB 口被外壳挡住或者用作其他用途,就不方便
- BOOT0 要可控:和串口一样,得能拉高 BOOT0
所以实际产品里,如果要做现场升级,更常见的方案是在用户程序里实现 IAP(In-Application Programming),通过 USB 或串口接收新固件,自己写进 Flash。这样就不依赖 BOOT0 了。但 IAP 的实现复杂度高,而且一旦用户程序跑飞,就没法升级了。所以很多产品会保留 DFU 作为最后的救砖手段。
4.3 DFU 与 IAP 的取舍
这里多说一句 DFU 和 IAP 的选择。我个人的经验是:
- 开发阶段:用 ST-Link,最快最方便
- 产线烧录:用 ST-Link 或者脱机烧录器,效率高
- 现场升级:优先 IAP,用户体验好;DFU 作为备用
- 救砖:DFU 或串口,看哪个接口方便
DFU 的优势是不依赖用户程序,哪怕 Flash 里的程序全乱了,只要 Bootloader 在,就能救回来。IAP 的优势是不需要动 BOOT0,用户操作简单。两者不是替代关系,而是互补。
5. 命令行模式:把烧录塞进自动化流程
5.1 CLI 基本用法
STM32CubeProgrammer 除了图形界面,还提供了命令行工具,路径通常在安装目录的bin文件夹下,Windows 下是STM32_Programmer_CLI.exe,Linux 下是STM32_Programmer_CLI。
基本用法:
STM32_Programmer_CLI -c port=SWD -w firmware.hex -v -rst参数解释:
-c port=SWD:连接方式,SWD 或 UART 或 USB-w firmware.hex:写入文件-v:写入后校验-rst:写完后复位运行
如果是 UART:
STM32_Programmer_CLI -c port=COM3 br=115200 parity=even -w firmware.hex -v -rst命令行模式最大的价值是可以脚本化。产线上几百块板子要烧录,用图形界面一块块点太慢了,写个批处理脚本,插上 ST-Link 自动烧录、自动校验、自动记录,效率提升不是一点半点。
5.2 Linux 下的 udev 权限配置
Linux 下用命令行工具,最常见的问题是权限。普通用户默认没有权限访问 USB 设备,会报 "Unable to open device" 之类的错误。
解决办法是加一条 udev 规则。在/etc/udev/rules.d/下新建一个文件,比如49-stlinkv2.rules,内容:
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", MODE="0666"0483是 ST 的 Vendor ID,3748是 ST-Link V2 的 Product ID。不同版本的 ST-Link,Product ID 可能不同,可以用lsusb命令查。加完规则后执行:
sudo udevadm control --reload-rules sudo udevadm trigger然后重新插拔 ST-Link,普通用户就能访问了。这个配置在 CI 环境里尤其重要,因为 CI 通常以非 root 用户运行。
5.3 批量烧录脚本示例
给一个我实际用过的批量烧录脚本思路,Windows 批处理:
@echo off set CLI="C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\STM32_Programmer_CLI.exe" set FIRMWARE=firmware.hex :loop echo 请插入板子,按任意键开始烧录... pause >nul %CLI% -c port=SWD -w %FIRMWARE% -v -rst if %errorlevel%==0 ( echo 烧录成功 ) else ( echo 烧录失败,请检查连接 ) goto loop这个脚本的逻辑是:插一块板子,按一下键,烧录,拔下来换下一块。产线上配合夹具使用,效率很高。如果要做全自动,可以结合 USB 热插拔检测,但那个复杂度就上去了,一般用半自动就够了。
注意:批量烧录时,ST-Link 的固件版本要统一。不同批次的 ST-Link 固件版本不一样,有时候会出现某几台能烧、某几台不能烧的情况。建议产线用的 ST-Link 统一升级到最新固件。
6. 常见问题与排查速查表
6.1 连接类问题
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| No STM32 target found | 接线错误/频率过高/未供电 | 检查 SWD 四线,降频到 500kHz,确认供电 |
| 连接时提示读保护 | RDP Level 1 | 解除保护(会全片擦除) |
| UART 连不上 | Parity 设置错误/BOOT0 未拉高 | 改为 Even,确认 BOOT0=3.3V |
| USB 设备不识别 | 线材问题/PA11PA12 接反 | 换数据线,检查 USB 差分线 |
| 连接时断时续 | 排线过长/干扰大 | 缩短排线,降低频率 |
6.2 下载类问题
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 下载到一半失败 | 波特率过高/Flash 未擦除 | 降波特率,勾选全片擦除 |
| 下载成功但程序不跑 | BOOT0 未拉回/起始地址错误 | 拉低 BOOT0,检查 .bin 起始地址 |
| 校验失败 | Flash 坏块/供电不稳 | 换板子验证,检查电源 |
| 下载后芯片锁死 | 选项字节配置错误 | 用系统存储器模式救砖 |
6.3 几个容易忽略的细节
第一,ST-Link 的固件版本。STM32CubeProgrammer 连接时会检查 ST-Link 固件版本,如果太旧会提示升级。升级本身没问题,但升级过程中如果断电,ST-Link 可能变砖。所以升级时确保 USB 连接稳定,别中途拔线。
第二,目标板的复位引脚。有些板子的 NRST 引脚接了电容或者复位芯片,ST-Link 的复位信号可能驱动不了。这时候可以在 CubeProgrammer 的连接设置里把 "Reset mode" 改成 "Software reset" 或者 "Hardware reset",多试几种。
第三,低功耗模式下的连接。如果芯片跑进了 Stop 或 Standby 模式,SWD 可能连不上。这时候需要先唤醒芯片,或者在连接设置里勾选 "Connect under reset",让 ST-Link 在复位状态下建立连接。
第四,多块板子同时烧录。如果一台电脑插了多个 ST-Link,CubeProgrammer 会列出所有设备,需要手动选择对应的序列号。命令行模式下可以用sn=参数指定 ST-Link 序列号,实现多工位并行烧录。
7. 我踩过的几个坑和实操心得
说几个文档里不会写、但实际会遇到的坑。
坑一:C8T6 的 Flash 容量识别问题。有些便宜的 C8T6 板子,芯片实际容量和标称不符,CubeProgrammer 读出来的 Flash 大小可能是 128KB 而不是 64KB。这时候如果你按 64KB 写程序,没问题;但如果程序超过了实际容量,就会写失败。遇到这种情况,以实际读出的容量为准,别硬写。
坑二:Keil 和 CubeProgrammer 抢 ST-Link。如果你 Keil 开着调试会话,CubeProgrammer 是连不上 ST-Link 的。反过来也一样。所以切换工具时,先把另一个工具的连接断开。这个看似简单,但我见过有人折腾半天,最后发现是 Keil 还在后台占着调试器。
坑三:USB DFU 的驱动问题。Windows 10 以上通常能自动识别 DFU 设备,但有些精简版系统会缺驱动。如果设备管理器里显示黄色感叹号,需要手动装 ST 的 DFU 驱动,驱动文件在 CubeProgrammer 安装目录里能找到。
坑四:串口下载的波特率陷阱。ST 的 Bootloader 对波特率有容差要求,不是所有波特率都支持。115200 是最稳的,230400 在有些芯片上会失败。如果非要提速,先小批量验证,别一上来就全产线推。
坑五:选项字节改完没生效。改完选项字节后,需要断电重新上电才生效,光点复位按钮不够。这个细节很容易忽略,尤其是改读保护的时候,以为没生效,其实是没重新上电。
实操心得:我现在做新板子调试,第一步永远是先用 STM32CubeProgrammer 连一下,确认芯片能识别、Flash 容量对、选项字节正常。这一步花不了一分钟,但能提前排除很多硬件问题。如果连都连不上,那后面写代码也是白搭。另外,我习惯把常用的烧录命令写成脚本放在项目根目录,换电脑或者重装系统后,直接跑脚本就行,不用重新记参数。
最后再分享一个小技巧:STM32CubeProgrammer 的图形界面里,连接成功后可以点"Firmware upgrade"旁边的按钮,把当前芯片的完整 Flash 内容读出来存成 .bin 文件。这个功能在逆向分析或者备份出厂固件时特别有用。读出来的文件可以直接用 CubeProgrammer 再写回另一块同型号的芯片,相当于克隆。产线上如果要做小批量复制,这个功能能省不少事。