资讯动态

WSL下STM32烧录全指南:USB转发与OpenOCD配置实战

发布时间:2026/10/13 8:55:43 来源:尧图企业网站定制
兄弟们有没有这种经历想做点嵌入式的东西兴致勃勃翻出积灰的开发板结果第一句话是“我板子呢”好不容易在某个抽屉角落找到了插上ST-Link打开终端却发现自己在WSL里然后脑子里冒出第二个问题——“WSL里面到底能不能烧录STM32”答案是能而且折腾明白之后特别顺手。这篇文章我打算把WSL烧录STM32开发板的完整链路拆开揉碎讲一遍从USB设备怎么“穿透”到WSL里到OpenOCD怎么配置、烧录命令怎么写再到我踩过的那些坑一次说清楚。适合已经装了WSL、平时用VSCode写嵌入式C、又不想为了烧录切回Windows命令行的朋友。1. 先把一个核心问题说透WSL凭什么能操作USB硬件很多人对WSL有个误解以为它就是Windows里开了一个Linux窗口是个“玩具环境”干不了跟硬件打交道的事。其实WSL2本质上是一个轻量级虚拟机跑的可是正儿八经的Linux内核。既然是Linux内核那理论上它就能访问USB设备、加载驱动、跟开发板通信。但“理论上能”和“实际能用”之间还差了关键的一步——怎么把物理USB设备从Windows主机“转交”给WSL这个虚拟机。这一步不是WSL自带的得靠一个叫usbip的协议去实现。1.1 为什么放着原生工具不用非要在WSL里烧录先解释一下动机不然很多人觉得这是脱裤子放屁。我做嵌入式C开发的工作流是这样的代码全部放在WSL的Linux文件系统里用CMake组织工程编译靠arm-none-eabi-gcc调试用VSCode连WSL的Remote Development。整个流程在Linux侧非常顺畅Makefile、CMake脚本、编译参数、链接脚本都是纯Linux风格。问题是到了烧录这一步以前我得切回Windows打开STM32CubeProgrammer或者STM32CubeMX里的烧录功能把编译好的bin文件再拖进去烧一次。来回切换非常打断节奏尤其当你在持续改代码、持续烧录验证的时候每烧一次就要切一次窗口时间一长真的会烦。所以最理想的状态是编完代码在WSL的终端里敲一条命令烧录、校验、复位一气呵成。这是整个事情的出发点。1.2 USB设备转发这层原理用生活类比讲清楚usbip的原理我一般用一个快递柜的类比来解释。Windows这边相当于一个“快递站”USB设备就是快递包裹。usbipd-win这个工具是快递站的管理员它负责登记有哪些包裹USB设备、放在哪个柜子里端口号。WSL则是“快递接收方”它需要申请把某一个柜子里的包裹“寄”过来。申请的动作就是usbipd attach寄过来之后WSL里的Linux内核就能看到这个USB设备像本地设备一样使用。这个方案有几个特点设备始终物理连接在Windows主机上数据通过虚拟总线转发给WSL。WSL2使用的是轻量级虚拟化转发路径很短延迟可忽略。转发是会话级的WSL重启或设备拔出后需要重新attach。不需要关闭Windows侧对设备的占用但要避免两边抢设备。注意要区分WSL1和WSL2。WSL1走的是Windows内核兼容层USB转发的方式和要求有细微差别。我建议直接用WSL2各方面兼容性更好。2. 烧录前的环境准备把USB设备“送”进WSL你要烧录STM32至少需要两个物理层面的东西一块STM32开发板我这边主要是STM32F103C8T6也就是俗称的蓝丸一个调试烧录器ST-Link V2或者V3。ST-Link通过USB连Windows主机通过SWD四根线连开发板。这个环节的目标只有一个让WSL里的Linux系统能看到这个ST-Link设备。2.1 确定你的WSL版本和Linux发行版首先确认WSL版本打开PowerShellwsl --status wsl --version如果显示的是WSL2且内核版本在5.10以上基本就没问题。如果你的WSL还是1.x的老版本建议先升级wsl --updateLinux发行版我用的Ubuntu 22.04里面自带的工具链和驱动支持都全不用额外折腾源。2.2 安装usbipd-win这步是Windows侧的“快递管理员”usbipd-win是一个开源工具需要以Windows管理员权限安装装完以后在服务列表里会自动运行。安装方式很简单从官方GitHub仓库下载最新msi安装包双击安装就行。装好之后打开PowerShell管理员执行usbipd list这时会列出所有USB设备找到ST-Link那一行。它的BusId通常是类似3-2这样的格式记住它。然后绑定这个设备绑定之后设备就允许被转发到WSL了usbipd bind --busid 3-2最后一步是附加到WSLusbipd attach --wsl --busid 3-2这一步成功后你在WSL的终端里执行lsusb应该能看到类似STMicroelectronics ST-LINK的条目说明设备已经被“递”进来了。实操心得usbipd attach这个命令每次WSL重启后都要重新执行。如果不想每次都手动敲可以建一个Windows任务计划或者写一个简单的.bat脚本放桌面。后面排查章节我会给一个自动化的脚本方案。2.3 在WSL里配置权限udev规则和用户组设备虽然能被lsusb看到但不代表你有权限操作它。Linux下访问USB设备默认需要root权限或者设备节点的权限被正确设置。如果不处理OpenOCD启动时会报Permission denied甚至更直白的no device found。解决办法有两种一种是临时提权每次都用sudo运行OpenOCD简单粗暴但不优雅。另一种是一劳永逸的udev规则。在WSL的Ubuntu里创建udev规则文件sudo nano /etc/udev/rules.d/99-stlink.rules写入SUBSYSTEMusb, ATTR{idVendor}0483, ATTR{idProduct}3748, MODE0666, GROUPdialoutST-Link V2的Vendor ID是0483Product ID是3748。如果是ST-Link V3Product ID会不一样可以用lsusb看到的实际值来填。然后让你的用户加入dialout组sudo usermod -aG dialout $USER接下去两条命令让规则生效sudo udevadm control --reload-rules sudo udevadm trigger注意udev在WSL2里不是完全功能正常的有时候reload不会立即生效。我实际测试下来重启一次WSL后规则肯定能生效。wsl --shutdown然后重新打开终端即可。3. 烧录工具选型OpenOCD还是st-flash设备通了权限没问题了接下来选烧录工具。市面上的开源工具里最主流就是OpenOCD和st-flash。我的项目全部都基于OpenOCD下面说说为什么。3.1 OpenOCD稳在哪st-flash适合什么场景先看一张对比表维度OpenOCDst-flash支持的调试器ST-Link、J-Link、CMSIS-DAP等一大堆主要是ST-Link支持的芯片非常多不限于ST主要是STM32烧录 调试都支持能和GDB配合只烧录没法调试脚本化程度极高可以自定义命令序列比较简单够用但不够灵活配置复杂度配置多上手有门槛几乎没有配置适合场景工程化、自动化、C项目长期迭代临时烧一个hex到某块板子st-flash更像一个轻量级的烧录器拿来即用命令就一条st-flash write firmware.bin 0x08000000但这玩意儿遇到批量生产、Flash加密、自定义校验、脚本集成这些场景就力不从心了。OpenOCD像一个瑞士军刀烧录只是它的一个功能它还能做Flash擦除、读取、比对、复位运行、GDB调试。对于我这种用CMake管理项目的嵌入式C选手OpenOCD可以直接嵌进CMake的custom target里一条make flash就搞定烧录这个体验是st-flash给不了的。3.2 OpenOCD安装两种方式推荐直接源码编译Ubuntu的APT仓库里有OpenOCD安装很方便sudo apt update sudo apt install openocd但要注意版本问题。Ubuntu 22.04仓库里的是0.11.0这个版本对ST-Link V3的支持不完整对一些新板子也识别不佳。我吃了这个亏之后改成源码编译。其实编译不复杂依赖就几样sudo apt install libtool autoconf automake pkg-config libusb-1.0-0-dev libhidapi-dev texinfo然后从官方仓库拉源码git clone https://github.com/openocd-org/openocd.git cd openocd ./bootstrap ./configure --enable-stlink make -j$(nproc) sudo make install编译完之后验证一下openocd --version看到0.12.0以上的版本号说明你用的是最新代码对新一代调试器和芯片支持明显更稳。3.3 没有ST-Link时的替代方案串口ISP烧录聊到工具我顺手提一句如果你的ST-Link还没到手头只有一块板子和一根USB转TTL线一样能烧录。STM32的ROM里固化了一段Bootloader可以通过USART1接收数据。做法是把板子上的BOOT0引脚拉高然后通过串口工具发送固件。工具用stm32flashsudo apt install stm32flash命令大概是stm32flash -w firmware.bin -v -g 0x0 -b 115200 /dev/ttyUSB0这个方案的缺点是速度慢、没有调试功能而且每烧一次都要手动切BOOT0跳线。但对于应急场景确实是保底方案。我建议你在板子旁边常备一根USB转TTL线不占地方关键时刻真能救命。4. 烧录全流程实操从接线到芯片复位跑起来讲完了原理和工具下面是我实际上手操作的完整流程。这部分我尽量写得详细因为很多问题不是出在命令上而是出在细节上。4.1 接线检查连线错了后面全白搭先检查物理接线。ST-Link V2和STM32F103C8T6通过SWD接口连接只需要四根线ST-Link引脚开发板引脚SWDIOPA13SWCLKPA14GNDGND3.3V3.3V这块板的SWD接口一般有专门的排针位置如果没有直接从ST-Link飞线到PA13和PA14也可以。要注意的是电源如果开发板是独立供电那ST-Link的3.3V可以悬空不接如果开发板没有独立供电那3.3V一定要接上。我遇到的不少“烧录失败”问题根源就是3.3V没接芯片根本没上电。实操心得ST-Link的3.3V输出能力有限如果用蓝丸驱动LED矩阵或者小电机建议开发板单独供5VST-Link只做信号传输。别把负载都压在ST-Link上容易掉电复位烧录失败率会飙升。4.2 环境自检烧录前花30秒确认链路每次准备烧录前我习惯按顺序跑一遍自检不浪费时间能快速定位是哪个环节出问题。Windows侧PowerShell管理员模式usbipd list确认ST-Link设备还处于Attached状态。如果显示Detached说明需要重新执行usbipd attach --wsl --busid 3-2WSL侧lsusb确认能看到ST-Link设备。然后验证权限ls -l /dev/bus/usb/ 2/dev/null如果看不到usb目录或者没有权限检查udev规则是否生效必要时重启WSL。也可以顺便确认一下内核日志dmesg | tail -20如果看到USB相关的报错大概率是驱动或权限问题这一步能提前暴露问题避免后面OpenOCD报错时一头雾水。4.3 OpenOCD烧录命令逐段拆解OpenOCD烧录的核心命令是这样的openocd \ -f interface/stlink.cfg \ -f target/stm32f1x.cfg \ -c program firmware.bin 0x08000000 verify reset exit看起来不长但每个参数都有讲究。-f interface/stlink.cfg指定调试器接口配置文件。ST-Link V2用这个ST-Link V3要改成stlink-v3.cfg。选错配置文件是我早期经常犯的错症状是OpenOCD启动时报unable to open device但实际上硬件在Windows下是好的。-f target/stm32f1x.cfg指定目标芯片配置文件。STM32F103系列用stm32f1x.cfg其它芯片要对应换。比如STM32F4系列就用stm32f4x.cfg选错会直接导致Flash烧录失败因为erase算法和Flash地址完全不对。-c参数后面跟的是OpenOCD命令program firmware.bin 0x08000000把bin文件烧到Flash起始地址0x08000000。verify烧录完成后自动校验强烈建议加上。烧录成功不代表数据正确校验一下多一层保险。reset烧录完自动复位芯片程序立刻开始跑。exit跑完自动退出OpenOCD不进入交互调试模式。所以完整的语义是初始化ST-Link连接STM32F103烧录bin到Flash校验一遍然后复位运行芯片。4.4 烧录过程中读懂OpenOCD的输出再判断成功很多人烧录时看到一堆输出不知道怎么看其实OpenOCD的日志非常有规律。一次成功烧录的输出大概长这样Info : Listening on port 3333 for gdb connections Info : STLINK V2J29S7 (API v2) VID:PID 0483:3748 Info : Target voltage: 3.3V Info : clock speed 1000 kHz Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints Info : starting gdb server for stm32f1x.cpu Info : flash size 128k, block size 1k Info : Padding image section 0 with 8 bytes Info : wrote 24576 bytes from file firmware.bin in 2.279417s (10.531 KiB/s) Info : Verified OK Info : resetting target Info : target halted due to debug-request, current mode: Thread关键信息逐行解析STLINK V2J29S7ST-Link固件版本这一步说明调试器识别成功。Target voltage: 3.3V芯片供电正常如果这里显示0V检查3.3V接线。flash size 128k芯片识别的Flash容量如果识别成64k而你的芯片是128k说明芯片型号识别有误。wrote 24576 bytes实际写入的字节数跟bin文件大小对得上就说明没毛病。Verified OK校验通过这是烧录成功最直接的信号。resetting target复位完成程序应该已经跑起来了。如果看到Error: target not halted或者Error: flash write failed就根据后面的报错内容查下面这个章节我把自己踩过的几个高频坑列全。5. 烧录失败排查实录那些让人抓狂的问题这段就是我说的“踩坑精华”每个问题都是我实际遇到过、花时间排查过的不写空话直接上干货。5.1 高频问题速查表症状根因解决办法no device foundST-Link没有attach到WSL重新执行usbipd attach --wsl --busid 3-2Permission deniedudev规则没生效检查99-stlink.rules重启WSLunable to open deviceST-Link被Windows一侧的驱动占用用Zadig把驱动换成libusb-win32然后重新attachtarget voltage: 0V3.3V没接或者板子没上电检查供电线路flash write failed芯片配置文件选错确认用stm32f1x.cfg而不是f4x.cfgError: connect failedopenocd没退出就重新执行先杀掉残留的openocd进程pkill openocdError: init mode failedSWD接线不对或接触不良检查SWDIO/SWCLK/GND三根线重新插拔5.2 案例一WSL重启后设备“消失”了这是所有问题里出现频率最高的。WSL每次重启之后之前attach的USB设备会自动全部解脱你要是直接跑OpenOCD就会看到no device found。我想过各种办法去解决这个麻烦最后的方案是写一个一键脚本。在Windows桌面放个attach_usb.batecho off usbipd attach --wsl --busid 3-2另外我建议把usbipd bind设为开机自动执行。在Windows的任务计划程序里建一个计划任务触发器选“启动时”操作用usbipd bind --busid 3-2。这样即使WSL重启了双击一下bat就能恢复环境。5.3 案例二Windows驱动抢占导致OpenOCD连不上有一次我折腾了半天设备也能attachlsusb也能看到但OpenOCD就是报unable to open device。查了一圈发现Windows侧的ST-Link驱动默认被系统识别成了HID设备usbipd转发出去之后WSL访问不到底层的USB接口。解决办法是安装Zadig打开后在Options里选择List All Devices找到ST-Link把驱动换成libusb-win32。换完驱动后Windows自带的STM32CubeProgrammer可能会认不到ST-Link这是正常的因为驱动变了。烧录全走WSL侧OpenOCD这段风险是可接受的。5.4 案例三芯片Flash校验不通过Verified failed这个报错区别于flash write failed它是写可能写进去了但读出来比对不一致。最常见的原因是供电不稳。遇到这种情况不要急着重刷先检查供电。蓝丸板子如果用USB给开发板供电同时ST-Link又接了3.3V两个电源之间可能有电压差导致Flash写入时序异常。我的做法是开发板只用一个电源要么USB供电不接ST-Link的3.3V要么ST-Link供电就不接外部USB电源。电源域干净了校验不通过的概率会大幅下降。5.5 案例四老版本的OpenOCD不识别新芯片有一段时间我拿到一块新的板子芯片是STM32F401CCU6但用0.11.0版本的OpenOCD去烧录直接报unknown device。后来排查发现是OpenOCD版本太老对新芯片的支持还没合入主分支。这个问题的解法在前面已经说过了——源码编译新版OpenOCD。我建议做嵌入式长期开发的朋友从一开始就直接源码安装最新版省得一两个月后遇到新芯片又要重编一次。6. 把烧录集成进CMake嵌入式C项目的正确玩法既然是嵌入式C编程之旅那最后这部分必须讲讲怎么把烧录动作跟CMake工程整合真正实现一条命令编译加烧录。6.1 在CMakeLists里加一个自定义烧录目标我习惯在CMakeLists.txt的末尾加上这样一段# 获取bin文件路径 set(APP_BIN ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bin) # 定义flash目标 add_custom_target(flash COMMAND ${CMAKE_COMMAND} -E echo Flashing ${APP_BIN} ... COMMAND openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c program ${APP_BIN} 0x08000000 verify reset exit DEPENDS ${PROJECT_NAME}.bin COMMENT Flash firmware to STM32 via OpenOCD )然后再加一个生成bin的规则因为OpenOCD烧bin更方便链接生成的默认是elfadd_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_OBJCOPY} -O binary $TARGET_FILE:${PROJECT_NAME} ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.bin )这样每次编译完成后自动生成bin然后再编译一遍工程cmake --build build -j$(nproc)接下来只需要一个命令cmake --build build --target flash编译、生成bin、调OpenOCD烧录、校验、复位全链路自动跑完。整个过程没有任何切换操作就是改代码、编译、烧录、看结果这才是嵌入式开发该有的效率。6.2 双目标板场景下的配置复用如果你手里有多块不同的板子比如一块F103、一块F401那可以把芯片配置抽出来用CMake的cache变量控制set(OPENOCD_TARGET stm32f1x.cfg CACHE STRING OpenOCD target config) set(APP_FLASH_ADDR 0x08000000 CACHE STRING Flash address)在flash目标里引用这两个变量COMMAND openocd -f interface/stlink.cfg -f target/${OPENOCD_TARGET} -c program ${APP_BIN} ${APP_FLASH_ADDR} verify reset exit这样切换板子时只需cmake -DOPENOCD_TARGETstm32f4x.cfg ..不用改任何代码逻辑。7. 最后分享一点我自己的体会和踩坑心得整套WSL烧录方案跑通之后我最大的感触是嵌入式开发环境真正回归了Linux侧的统一。过去那种“代码在Linux里写、烧录切到Windows”的割裂感消失了整个流程从头到尾都在一个终端里完成这对我写C工程来说体验提升非常明显。如果你问我有没有必要一开始就搞OpenOCD加CMake集成我的回答是分阶段来。刚上手时用st-flash先跑通烧录链路感受到“板子能烧进去”的正反馈很重要。当你开始做有一定规模的项目编译产物要迭代几十次、上百次的时候再花半小时把它集成进CMake绝对不亏。最后一个实用小技巧OpenOCD烧录失败时第一步永远不是去改命令而是先跑一遍第4.2节里的自检三条命令。很多问题出在USB attach状态、驱动抢占这些环境因素上而不是命令本身写错了。把环境确认清楚烧录问题基本解决了一大半。

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

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

免费获取报价 →
↑