资讯动态

WSL下STM32烧录指南:USB/IP透传与OpenOCD实战

发布时间:2026/10/13 3:18:40 来源:尧图企业网站定制
我板子呢——WSL如何烧录STM32开发板(基于STM32的嵌入式C编程之旅7)“我板子呢”这句话玩STM32的朋友大概率都喊过。但最近我在WSL里做嵌入式C开发时喊出这句不是因为板子丢了而是因为一个特别离谱的局面板子明明用ST-Link插在电脑上LED都亮了可我在WSL里跑lsusb愣是连个影子都看不到。编译好的固件躺在build/目录里烧录工具却告诉我“No device found”那一刻真的很想掀桌。这一篇就是专门来解决这个问题的。我会完整拆解一套在WSLWindows Subsystem for Linux环境下直接烧录STM32开发板的方案从USB设备透传的原理、环境搭建到OpenOCD和st-flash两种烧录方式的实操命令再到我踩过的各种坑和排查思路。如果你也是“编译在WSL、烧录跑Windows”的受害者或者正准备把嵌入式开发环境整体迁到WSL里这篇文章应该能帮你少走不少弯路。1. 为什么非要在WSL里折腾烧录先说清楚动机。嵌入式开发里编译和烧录原本是连着的一套动作写完代码make然后用下载器把固件写进芯片。可一旦开发环境切成WSL这套动作就被劈成了两半。WSL里跑的是Linux环境GCC交叉编译工具链、CMake、Makefile这些在Linux下用起来非常顺手。我用的是arm-none-eabi-gcc加 CMake 的组合在WSL里编译STM32工程体验确实比Windows下的命令行舒服得多脚本也更好写。但问题在于WSL2本质上是一台轻量虚拟机它没有直接访问USB硬件的能力。你Windows宿主机上插着的ST-Link、CH340串口线WSL2默认一概看不见。这就导致一个很割裂的工作流在WSL里编译把.elf或.bin拷到Windows目录再打开Windows下的STM32CubeProgrammer或者其它烧录软件手动选芯片型号、选固件、点烧录。来回折腾一次还好要是每天改个代码烧十次八次这个“编译→拷贝→切窗口→点鼠标”的循环会让人崩溃。更麻烦的是脚本化基本不可能。想在WSL里写一个make flash一步到位Windows侧的图形化软件根本没法从命令行干净地调用。所以那段时间我就在想有没有办法让WSL直接认到USB设备让OpenOCD在Linux侧直接跟ST-Link通信把烧录和调试全部拉回WSL里。结论是有的而且方案比我想象中成熟关键是搞定USB设备透传这一层。2. 整体方案选型USB/IP透传加Linux烧录工具2.1 三条路线的对比在确定方案之前我大致梳理了三种可行的思路各有各的适用场景。第一种是“跨平台调用Windows工具”。在WSL里通过cmd.exe或powershell.exe调用Windows下的命令行烧录工具比如STM32CubeProgrammer的CLI模式。这条路不需要任何额外内核模块做起来也快但坑在于路径转换Windows路径和Linux路径互相翻译、环境变量隔离、输出输出流的编码问题偶尔还会遇到权限弹窗。能用但用起来不够“原生”。第二种是“WSL1直通”。WSL1因为不是虚拟机架构对USB的支持方式不一样某些场景下能直接看到串口设备。但WSL1对很多Linux系统调用的兼容性不如WSL2而且现在微软的更新重心明显在WSL2上为了USB支持牺牲整体体验不划算。第三种就是我这篇要重点讲的“USB/IP透传加Linux原生烧录工具”。具体来说Windows侧装一个usbipd-win服务把物理USB设备通过USB/IP协议共享出来WSL2侧把这个远程设备挂载成自己的USB设备。挂载成功之后Linux侧的openocd、st-flash、lsusb就能直接操作ST-Link跟在真机Linux上完全一样。三条路的对比如下方案配置复杂度使用体验脚本化难度推荐度Windows CLI跨平台调用低一般有路径和编码问题中过渡方案WSL1 USB直通低受架构限制兼容性差低不推荐USB/IP透传 Linux工具中原生Linux体验高强烈推荐2.2 为什么USB/IP方案最靠谱USB/IP这个方案的核心价值是把“设备在Windows上”这个事实对WSL隐藏掉。挂载完成后WSL里看到的ST-Link就是一个普通的本地USB设备所有Linux生态下的嵌入式工具链都能无缝工作。原理其实不复杂。USB/IP协议做的事情是把远程机器这里就是Windows宿主机上的USB设备抽象成网络资源通过TCP/IP传输USB的URBUSB Request Block数据。usbipd-win在Windows侧扮演“服务端”把物理USB设备共享到网络上WSL2内核里内置了USB/IP客户端驱动usbipd attach命令会把远程设备“拉”过来挂载成/dev/bus/usb/xxx/yyy。这个方案的好处有几个。第一不需要重新编译内核WSL2自带支持。第二设备挂载和卸载非常灵活一条命令搞定不用重启任何服务。第三Windows侧只需要做一次bind之后每次开机attach一下就行日常使用几乎是透明的。我最终选定这个方案还有一个现实原因我的开发板调试接口是ST-Link SWDOpenOCD在Linux下的支持非常成熟stlink.cfg这类接口配置本身就是为Linux环境写的。把USB透传搞定就等于把整个调试环境搬进了WSL。3. 环境准备把USB设备“塞”进WSL3.1 Windows侧安装usbipd-win第一步是给Windows装usbipd-win。这个东西在GitHub上有发布也可以用winget一条命令搞定winget install usbipd装完之后需要以管理员身份打开PowerShell或终端。因为后面所有的usbipd管理操作都要求管理员权限。插上ST-Link先看设备能不能被识别到usbipd list输出会列出当前所有的USB设备每一行左边是BusID类似1-1、1-3右边是设备描述。你会在里面看到一个类似ST-Link/V2的设备。接下来要做一个关键操作bind。这个动作的意思是让usbipd服务接管这个设备把它变成可共享的状态usbipd bind --busid 1-1注意bind之后Windows侧对这个设备的访问会被“切走”。你可能会发现Windows下的STM32CubeProgrammer突然不识别ST-Link了这是正常现象因为设备已经被标记为共享状态。不需要它的时候可以unbind。然后是把设备attach到WSLusbipd attach --wsl --busid 1-1如果电脑里只装了一个WSL发行版不加--wsl参数也会自动选择默认发行版。attach成功之后设备就从Windows“消失”了Windows设备管理器里会看到它变成一个无法识别的设备甚至直接消失而在WSL里lsusb就能看到了。3.2 WSL侧安装工具链设备透传搞定之后WSL侧要装三样东西编译工具链、OpenOCD、stlink-tools。sudo apt update sudo apt install -y gcc-arm-none-eabi openocd stlink-tools装完确认版本arm-none-eabi-gcc --version openocd --version st-flash --version这里有一点要注意Ubuntu软件源里的gcc-arm-none-eabi版本可能不是最新的但对于大多数STM32工程来说够用。如果你需要新版本可以去ARM官方下载独立工具链解压后加到PATH里。OpenOCD和stlink-tools直接用apt装就行版本通常也比较新。装完之后先挂载设备再验证一下usbipd attach --wsl --busid 1-1 lsusb正常的话lsusb里会看到类似STMicroelectronics ST-LINK/V2的一行。到这一步USB链路已经打通了。3.3 权限和udev规则这是很多人会忽略但特别重要的一步。你会发现刚挂载完设备lsusb能看到但openocd或st-flash一运行就报权限错误比如libusb_open() failed或者Permission denied。原因很简单普通用户没有访问USB设备的权限。解决方式有两种。偷懒的方式是每次都用sudo跑烧录命令但这样在脚本和IDE调试里会很别扭。正规的方式是写udev规则让系统自动给ST-Link设备放权。创建一个规则文件sudo vim /etc/udev/rules.d/99-stlink.rules写入以下内容SUBSYSTEMusb, ATTR{idVendor}0483, ATTR{idProduct}3748, MODE0666 SUBSYSTEMusb, ATTR{idVendor}0483, ATTR{idProduct}374b, MODE0666ST-Link常见的VID是0483PID3748对应ST-Link/V2374b对应ST-Link/V3。如果你的设备是其它版本可以用lsusb查一下实际的VID和PID再改。写完规则后重载一下sudo udevadm control --reload-rules sudo udevadm trigger之后断开再attach一次设备普通用户就能直接烧录了。提示udev规则文件里的MODE0666是给设备节点开放读写权限在个人开发机上没问题。如果你在公司或实验室有安全要求可以只给当前用户组授权写GROUPplugdev加MODE0664然后把用户加进plugdev组。4. 烧录实操两种工具、三种用法4.1 先用OpenOCD把固件烧进去OpenOCD是目前嵌入式调试烧录事实上的标准工具支持ST-Link、J-Link、CMSIS-DAP等一大堆调试器。它对STM32的支持非常完善几乎所有系列都有对应的target配置文件。我的工程编译产物是build/firmware.elf芯片是STM32F103系列。烧录命令如下openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c program build/firmware.elf 0x08000000 verify reset exit拆开看这条命令-f interface/stlink.cfg指定调试器接口配置告诉OpenOCD我们用的是ST-Link。老版本里可能叫stlink-v2.cfg新版本统一叫stlink.cfg会自动适配V2和V3。-f target/stm32f1x.cfg指定目标芯片配置F103就用这个F4系列换成stm32f4x.cfgF0系列换成stm32f0x.cfg。-c program ...后面跟的是一个OpenOCD命令序列。program表示烧录文件第二参数是烧录的起始地址STM32内部Flash的起始地址就是0x08000000。verify烧录完成后回读校验强烈建议加上。reset烧完自动复位芯片让程序跑起来。exit完成后退出OpenOCD进程。跑起来之后正常的输出大概是这样Info : STLINK V2J29S7 (API v2) VID:PID 0483:3748 Info : Target voltage: 3.3V Info : clock speed 4000 kHz Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints Info : starting gdb server for stm32f1x.cpu on 3333 Info : Listening on port 3333 for gdb connections Info : device id 0x20036410 Info : flash size 64kbytes Info : flash protected: 0x00000000:0x00000000 Warn : Adding extra erase range, 0x08000000..0x08001fff Info : Erasing and writing flash... Info : gdb server monitoring target halted Info : verified 168 bytes in 0.004s Info : resetting target看到verified和resetting基本就是烧录成功、程序已经跑起来了。实际用的时候我为不同芯片准备了几条命令别名放在shell配置里。比如alias flash-f103openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c program build/firmware.elf 0x08000000 verify reset exit alias flash-f407openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c program build/firmware.elf 0x08000000 verify reset exit4.2 轻量场景用st-flash如果你只需要单纯地把.bin或.hex文件写进Flash不关心调试st-flash是更轻的选择。它是ST-Link工具集里的命令行烧录程序用起来比OpenOCD简洁不少。先把.elf转成.bin或者直接用编译产出的.binarm-none-eabi-objcopy -O binary build/firmware.elf build/firmware.bin st-flash write build/firmware.bin 0x08000000st-flash write的第二个参数是烧录地址STM32固定是0x08000000。如果想整片擦除再烧st-flash erase这个工具会自动识别连接的ST-Link输出烧录进度。它的优点就是命令短、依赖少适合写进Makefile里做一键烧录flash: build/firmware.bin st-flash write build/firmware.bin 0x08000000不过st-flash有个局限调试功能基本没有也不能做断点、单步这些操作。如果只是快速验证代码逻辑st-flash足够一旦涉及调试还是得回到OpenOCD加GDB。4.3 从烧录到在线调试OpenOCD加GDB这一步才是把开发环境整体搬进WSL的最大红利在线调试。以前在Windows下用IDE调试代码改一点就要重新编译、重新连接调试器来回点鼠标。在WSL里OpenOCD加GDB的调试流程非常流畅。先把OpenOCD以服务模式跑起来只启动不烧录openocd -f interface/stlink.cfg -f target/stm32f1x.cfg它会监听localhost:3333端口等待GDB连接。然后在另一个终端启动GDBgdb-multiarch build/firmware.elfgdb-multiarch是支持多架构的GDBWSL的apt源里直接有。如果不想装这个也可以用arm-none-eabi-gdb效果一样。进入GDB后连接OpenOCD(gdb) target remote localhost:3333这条命令会让GDB通过3333端口跟OpenOCD通信OpenOCD再通过ST-Link控制芯片。连接成功后先复位并停在入口(gdb) monitor reset halt注意这里的monitor reset halt是OpenOCD的命令通过GDB转发给OpenOCD。它的作用是复位芯片并暂停在所有核心的入口处。然后就可以像平时调试一样干活了(gdb) load (gdb) break main (gdb) continueload把程序加载进Flashbreak main在main函数入口下断点continue运行程序到达断点后可以用next、step、print这些命令单步调试和查看变量。这套组合拳打下来我基本抛弃了Windows下的IDE调试。改代码、编译、烧录、看断点全程在WSL终端里完成效率高了一大截。注意GDB里调试STM32load和program的区别要搞清楚。load是GDB自己的加载命令它会把符号表信息和代码一起处理适合调试带符号的.elfprogram是OpenOCD的命令烧完就完事不做符号关联。调试场景用GDB的load纯烧录场景用OpenOCD的program别混。5. 常见问题与排查技巧实录这条路我走了好几遍坑也踩了不少。下面这些问题是出现频率最高的我按现象、原因、解决办法整理出来。5.1 usbipd list里看不到ST-Link现象执行usbipd list输出里只有鼠标键盘找不到ST-Link。可能原因有好几个。第一ST-Link是板载的但板子没供电或者USB线是“只能充电不能传数据”的线。第二ST-Link驱动在Windows下没有正确安装导致系统不识别它。第三你的板子走的是USB HUB再接电脑某些劣质HUB会丢设备。排查思路先把板子直接插电脑原生USB口换一根确定能传数据的线试试。然后打开Windows设备管理器看通用串行总线设备里有没有ST-Link相关条目。如果显示“未知USB设备设备描述符请求失败”大概率是线或者板子供电问题。如果显示有黄色感叹号那就去ST-Link驱动上想办法装好驱动重启再试。5.2 bind或attach报错现象执行usbipd bind提示没有管理员权限或者usbipd attach提示device not found。usbipd的管理命令必须管理员权限这是硬性要求。attach报device not found通常是BusID写错了或者设备根本没被bind过。执行之前先usbipd list看一眼设备当前状态bind过的设备状态会变成Attached或Shared之类。还有一点每次重新插拔设备后BusID可能会变别想当然用旧的BusID。5.3 WSL里lsusb能看到设备但openocd报权限错误现象设备已经挂载lsusb能看到但一跑openocd或st-flash就报libusb_open() failed或者Could not open device。基本就是udev权限没配置好。回到3.3节把udev规则写对udevadm control --reload-rules之后一定要重新attach设备让新的udev规则生效。如果改了规则还是不行可以先sudo openocd跑一下确认是权限问题再回头折腾udev。5.4 OpenOCD报SWD连接失败现象OpenOCD启动后报Error: target not halted或者SWD error后面跟着一堆TARGET: stm32f1x.cpu - Not halted。屏幕上的target voltage如果显示0.0V说明ST-Link根本没有看到芯片多半是接线问题。SWD只需要四根线SWDIO、SWCLK、GND加上目标板供电。检查SWDIO和SWCLK是不是接反了GND有没有共地。如果板子是3.3V供电ST-Link上的3.3V输出可以给板子供电但注意电流别超。还有一种情况是目标芯片被读保护了Flash里的选项字节把调试端口锁死这种情况需要先解除读保护但解除保护会清空Flash。5.5 烧录到一半卡死现象烧录进度走到一半OpenOCD一直卡在擦除或写入阶段最后超时报错。最可能的两个原因供电不稳和时序不稳。板子用USB口供电的时候如果同时接了电机、大功率外设电压跌落会导致烧录失败。这时换成独立供电或者把外设断开再试。时序方面可以降低SWD时钟频率在OpenOCD配置里加一句openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c adapter speed 1000 -c program build/firmware.elf 0x08000000 verify reset exitadapter speed 1000表示把SWD时钟降到1MHz速度慢一点但稳定很多。特别是用杜邦线连接ST-Link和板子的时候线长了、接触不良了降速是最有效的土办法。5.6 WSL里操作一半设备掉线现象烧录过程中突然报USB错误然后在WSL里lsusb看不到设备了Windows里也找不到。USB/IP透传有一个特性设备状态异常或者Windows侧驱动干预时连接会断开。最常见的是ST-Link固件还在运行、芯片被复位导致USB枚举异常。解决办法很简单重新attach一下usbipd detach --busid 1-1 usbipd attach --wsl --busid 1-1如果频繁掉线检查一下Windows侧是否有程序在轮询这个设备比如某些厂家后台软件停掉它们再试。5.7 问题速查表现象优先排查项有效解法usbipd list无设备USB线、供电、驱动换线、换口、装驱动bind/attach报错权限、BusID管理员运行、重新list查IDopenocd权限报错udev规则配置99-stlink.rules并重载SWD连接失败接线、供电、读保护查四线接线、查电压、解除保护烧录卡死供电、时序独立供电、降adapter speed设备掉线Windows侧干扰detach后重新attach6. 一些实操心得与建议整套方案跑通之后我再也没回过Windows侧烧录。这里分享几个我觉得特别有用的习惯和技巧。第一强烈建议把常用命令写成脚本。我不太喜欢每次敲一长串openocd参数所以把烧录、擦除、调试启动都做成了Makefile目标。现在编译加烧录只需要make flash一条命令脚本里先检查USB设备在不在不在就提示先attach然后自动执行openocd烧录。如果脚本里需要自动attach设备可以用usbipd attach --wsl --busid $(usbipd list | grep ST-Link | awk {print $1})这种方式动态取BusID避免写死。第二注意ST-Link固件版本和OpenOCD版本的匹配关系。老版本OpenOCD对ST-Link V3的支持不完善新版本OpenOCD有时又会提示ST-Link固件过老需要升级。如果OpenOCD启动时提示ST-Link固件版本太老去ST官网找固件升级工具在Windows侧升级一下然后再attach回WSL。第三关于接线能用板载ST-Link就尽量用板载。很多开发板把ST-Link做在板子上通过USB直连就行省去了外部线材的烦恼。如果是外接ST-LinkSWD线越短越好杜邦线超过10厘米就有风险。我试过用20厘米的线在4MHz时钟下烧录失败率明显变高降到1MHz就稳定了。后来干脆把线剪短怎么折腾都稳。第四替大家把话说清楚WSL里做嵌入式开发烧录这块只要打通了USB/IP透传体验不输原生Linux甚至比Windows下的图形化工具更高效。如果你还在纠结要不要把环境迁到WSL这条链路值得先搭起来试试。我个人的体会是真正卡住人的往往不是工具链编译而是这种“设备看不见、工具连不上”的边角问题一旦打通整个开发流的顺畅度会上一个台阶。最后再分享一个小技巧OpenOCD启动的时候如果target voltage显示正常的3.3V但SWD还是连不上先别急着怀疑配置。拿万用表量一下SWDIO和SWCLK两个引脚对地的电压SWDIO正常应该在0到3.3V之间跳变SWCLK是固定时钟输出。如果SWCLK一直为0或者一直是高检查ST-Link和目标板是不是共地了这个问题我遇到过一次排查了半小时才发现是GND线松了。嵌入式开发就是这样九成的怪问题最后都是物理层的问题工具链和配置反而靠谱得很。

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

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

免费获取报价 →
↑