资讯动态

WSL 2下用OpenOCD烧录STM32:USB转发配置全攻略

发布时间:2026/10/13 11:12:05 来源:尧图企业网站定制
先说个真实场景某天我正坐在电脑前准备把一块 STM32 开发板通过 ST-LINK 调试器烧录新写的固件。Windows 下一切正常但为了统一整个团队的编译环境我一直习惯在 WSLWindows Subsystem for Linux里敲命令、跑脚本。那天我打开 WSL 终端执行了lsusb结果列表里空荡荡的完全没有 ST-LINK 的影子。那一刻我脑子里就一句话“我板子呢”其实板子就插在 USB 口上Windows 的设备管理器也识别得清清楚楚。问题出在我把 WSL 当成了一个普通的 Linux 终端却忘了 WSL 2 本质上是个轻量虚拟机它默认看不到 Windows 主机上的 USB 设备更别说直接操作开发板了。想直接在 WSL 里烧录 STM32核心要解决的就是“让 Linux 内核认识这块板子”的链路问题。这篇文章就记录一下我怎么把这根链路彻底打通从安装 USB 转发工具到配置 udev 规则再到用 OpenOCD 完成烧录中间踩过了哪些坑都会一并写清楚。无论你是刚入门嵌入式 C 开发的新手还是被 WSL 各种设备识别问题折腾过的老手这套流程应该都能给你一个靠谱的参考。1. 为什么要在 WSL 里搞嵌入式烧录一个懒人的解决方案你可能也会问明明 Windows 下有那么多现成的图形化烧录工具点几下鼠标就能把固件写进芯片为什么非要跑到 WSL 里折腾命令行我的理由很简单因为你可能根本不想打开那个图形化工具。1.1 工作流的统一性远大于工具界面做嵌入式 C 开发很多人会把代码放在 Windows 的某个目录里然后用 WSL 里的 GCC 工具链编译。这样做的最大好处是只要写一个 Makefile 或者 CMakeLists.txt无论谁把仓库克隆到哪台电脑上都能用几乎一模一样的命令编译出来。编译解决了烧录怎么办如果烧录还得回到 Windows 图形化界面那整个自动化链路就断了一截。更麻烦的是图形化工具往往没法轻易集成到你的 CI 流程、自定义脚本或一键部署方案里。让我彻底转向 WSL 烧录的催化剂是某次我在编译服务器上跑完测试想把生成好的固件直接烧到本地板子上验证。但编译服务器是 Linux本机开发板却插在 Windows 上两边根本连不通。后来我想明白一件事如果 WSL 能直接访问那块板子那它就相当于把我的 Windows 主机变成了一台“Linux 开发机”烧录、编译、调试、GDB 全都能在同一套命令行体系里完成。这种统一性带来的效率提升是任何图形界面都替代不了的。并且WSL 里的命令可以直接引用 Windows 文件系统下的路径比如/mnt/d/STM32Project/build/firmware.hex。这意味着你可以在 Windows 里用编辑器写代码在 WSL 里编译和烧录两边不需要任何文件拷贝天然无缝衔接。1.2 避开图形界面工具的“看不见的坑”还有一点比较现实很多图形化烧录工具虽然好用但当你需要自动化、批量生产、或者给测试团队配发烧录脚本时它们就显得非常笨重。命令行工具 OpenOCD 就没有这种困扰——它本来就是为脚本化和远程调试设计的。所以我决定彻底在 WSL 里打通烧录链路所有固件、脚本、配置文件都放进仓库版本控制里。以后新同事上任拉下代码、装好工具链直接执行make flash就能用根本不需要按钮在哪儿、协议怎么选这些琐碎经验。2. 打通 USB 设备的“任督二脉”WSL 2 下的 USB 转发配置先说清楚一个概念WSL 2 默认是不支持直接访问 USB 设备的。因为从架构上说WSL 2 跑在一个轻量级虚拟化平台上它有自己的 Linux 内核但硬件资源包括 USB 控制器仍然由 Windows 主系统管理。所以在 Linux 子系统里根本看不到 Windows 下挂载的那些 USB 设备。解决办法也很直接用一个开源的 USB/IP 转发工具把 Windows 上的物理 USB 设备“借给” WSL 2 内核使用。这个工具一般叫usbipd-win它通过 USB/IP 协议让 WSL 里的 Linux 驱动能够访问 Windows 侧真实接入的硬件。实际配置过程分三步走每一步都有关键细节漏一个就可能前功尽弃。2.1 检查你的 WSL 版本必须是 WSL 2USB 转发只支持 WSL 2WSL 1 没有完整的内核没法挂载这种虚拟 USB 驱动。因此第一步确认你的系统用的是 WSL 2。在 PowerShell 或 CMD 里输入wsl -l -v如果你看到版本那列写的是 1那需要先升级。可以在管理员 PowerShell 里执行wsl --set-version 发行版名称 2如果发行版是全新的也可以直接安装指定的 Ubuntu 发行版并默认使用 WSL 2。为了保险起见建议先执行一次wsl --update把 WSL 内核更新到最新版本因为老内核可能存在一些 USB 设备兼容性的 bug。配套表Windows 侧和 WSL 侧需要准备的组件组件作用安装方式WSL 内核提供 Linux 环境基础wsl --update管理员权限usbipd-winWindows 侧 USB 转发服务GitHub 发布页下载安装包Linux 侧usbip客户端工具WSL 内连接 USB 设备的驱动工具sudo apt install linux-tools-virtual hwdata注意usbipd-win安装后需要重启终端或 PowerShell 才能生效。安装过程中会注册一个 Windows 服务重启后这个服务会自动启动。2.2 绑定和连接把这块板子“递”给 WSL安装完usbipd-win后在 Windows PowerShell管理员模式下先看看当前有哪些 USB 设备usbipd list输出会类似这样BUSID VID:PID DEVICE STATE 1-2 0483:3748 STMicroelectronics ST-LINK Not attached其中VID:PID是设备的供应商 ID 和产品 ID。ST-LINK 调试器的 VID 一般是0483PID 可能是3748等具体看你的硬件版本。找到 BUSID 之后把它绑定给 WSL 使用usbipd bind --busid 1-2这一步意味着 Windows 会把该 USB 设备的所有权“共享”给 WSL。绑定之后设备状态应该变成Bound。然后在 WSL 终端里确认已经安装了 usbip 客户端工具。安装方式sudo apt update sudo apt install linux-tools-generic hwdata接着连接设备sudo usbip attach -r Windows主机IP -b 1-2这里的Windows主机IP一般可以填 localhost如果你是从另一台 Linux 机器连过来的就要填真实的主机地址。执行成功后再跑一次lsusb如果列表里出现了类似STMicroelectronics ST-LINK的行恭喜你这块板子现在在 WSL 的 Linux 内核里已经被“看到”了。2.3 一个容易被忽略的细节绑定 vs 分离usbipd bind只是授权把设备共享出来但没有真正连接到 WSL 内核。真正建立连接的是usbip attach。很多人刚接触时会困惑为什么我 bind 了之后lsusb还是看不到因为 bind 只是设置了权限attach 才是激活那根“虚拟数据线”。如果不想再让 WSL 占用设备断开连接用sudo usbip detach --port 端口号或者直接在 Windows 端用usbipd unbind --busid 1-2unbind 之后设备会回到正常 Windows 独占状态图形化烧录软件也能正常看到它。建议每次重新拔插开发板后都重新执行一次attach因为 USB 设备的热插拔事件不会自动同步到 WSL 内核。3. 烧录工具选型为什么 OpenOCD 是最优选设备在lsusb里能看到只代表“硬件链路通了”离真正烧录还差一个软件层面的工具。这一步我选择了 OpenOCD目前嵌入式圈子里最常用的开源调试和烧录工具。3.1 为什么不直接用原厂图形化工具很多开发板出厂时原厂会提供一个图形化烧录软件功能非常全填好型号、选择固件点个按钮就能烧。但我在自动化工作流中吃过它的亏命令行的自动化支持差、许可证限制多、日志输出不友好而且在不同机器上版本兼容性参差不齐。OpenOCD 则完全不同开源免费安装方便原生支持命令行接口便于集成到 Makefile 和 CI 流程对 ST-LINK 调试器、STM32 各系列芯片的支持非常成熟可以通过 GDB 远程调试烧录只是它的附带功能之一。3.2 安装 OpenOCD 与核心配置详解在 WSL 里安装 OpenOCDsudo apt install openocd安装完成后要写两段配置一段描述调试器interface一段描述目标芯片target。调试器配置以 ST-LINK 为例source [find interface/stlink.cfg] transport select hla_swd目标芯片配置以 STM32F103 为例source [find target/stm32f1x.cfg]把这两行写进一个脚本文件比如stlink_flash.cfg然后运行openocd -f stlink_flash.cfg看到类似下面的输出就说明调试器已经识别到复位信号和目标芯片了Info : STLINK V2J29S7 (API v2) VID:PID 0483:3748 Info : Target voltage: 3.30 V Info : stm32f1x.cpu: hardware has 4 breakpoints, 2 watchpoints如果 OpenOCD 能正常启动并识别到内核那就意味着烧录的准备工作全部完成。注意transport select hla_swd这行是 ST-LINK 专用配置。不同调试器如 J-Link、CMSIS-DAP需要各自对应的配置文件不要照搬。3.3 烧录命令从单次烧录到一键脚本OpenOCD 的烧录命令可以写在命令行里例如openocd -f stlink_flash.cfg -c program build/firmware.elf verify reset exit其中program是烧录指令verify表示烧录完成后自动校验 Flash 内容reset表示烧录结束后复位目标板exit表示断开与调试器的连接。这条命令一次执行板子就能自动运行新固件。每次都要敲这么长一串很烦所以我会在 Makefile 里封装一个flash目标flash: openocd -f stlink_flash.cfg -c program build/firmware.elf verify reset exit以后编译完只需执行make flash从编译到上板一条命令走完那个“板子呢”的焦虑彻底消失。4. 摸黑排查实录当lsusb能看到板子OpenOCD 却报找不到设备正常情况下走到第 3 章就结束了。但嵌入式开发不可能总是一帆风顺更何况是在 WSL 这个“半虚拟化”环境里折腾。我在一次重新安装环境时遇到了一个典型的案例lsusb能看到 ST-LINK但 OpenOCD 启动后却提示找不到目标设备错误信息大概是Error: open failed或者target not found。这个问题非常有代表性下面是我完整的排查链路希望能给你提供一条可复现的排错思路。4.1 检查设备节点你以为你敢用的Linux 不一定让你用WSL 内lsusb能看到设备说明 USB 转发链路是通的。但访问 USB 设备底层需要经过设备文件比如/dev/bus/usb/001/003或/dev/ttyUSB0。很多时候 OpenOCD 报错是因为这些设备文件的权限不够。Linux 默认的权限策略中普通用户访问 USB 设备往往受限。WSL 默认启动的用户虽然有sudo权限但 OpenOCD 不会自动用 sudo 运行所以它可能直接连不上设备节点。解决方案是添加 udev 规则让系统允许普通用户访问这类设备。创建一个规则文件sudo nano /etc/udev/rules.d/99-stlink.rules写入SUBSYSTEMusb, ATTR{idVendor}0483, ATTR{idProduct}3748, MODE0666保存后重载规则sudo udevadm control --reload-rules sudo udevadm trigger这里MODEv0666意思是权限设为所有用户可读可写。之后重新启动 OpenOCD大概率能解决。4.2 排查 ST-LINK 的供电和 SWD 接线如果权限解决了还是找不到下一步检查硬件链路。STM32 开发板通常有板载 ST-LINK但也有人用独立的 ST-LINK 配合杜邦线连接目标板。如果 SWDIO、SWCLK、GND、3.3V 四根线没接好OpenOCD 会一直报找不到目标。有个小技巧在 OpenOCD 启动时加上一堆调试参数它会打印出底层访问过程openocd -d3 -f stlink_flash.cfg日志里如果出现no device found或者unable to open STLink device多半还是 USB 转发或权限问题如果日志显示调试器已连接却始终无法识别目标芯片那基本可以判定是 SWD 接线或目标板供电问题。4.3 WSL 1 vs WSL 2 的内核行为差异另一个踩坑点是如果你用的发行版还停留在 WSL 1usbipd的 attach 命令虽然不报错但 USB 设备根本不会出现在 Linux 设备树里。这是因为 WSL 1 没有虚拟化硬件层USB/IP 驱动没法工作。务必通过wsl -l -v确认版本。有一次我废了半天劲最后发现是之前安装了 Ubuntu 的旧发行版默认是 WSL 1升级到 WSL 2 后整个世界都清净了。4.4 串口调试时的二次心态崩溃烧录成功后另一个常见的“板子呢”场景是程序跑起来了但串口终端里没有任何输出。这同样可能是权限问题。STM32 的虚拟串口通常被映射为/dev/ttyACM0或/dev/ttyUSB0。如果你在 WSL 里用screen /dev/ttyACM0 115200打开提示权限不足那就需要给对应设备节点也加上 udev 规则SUBSYSTEMtty, ATTRS{idVendor}0483, ATTRS{idProduct}5740, MODE0666这里的 PID 要看你的具体设备。设置完后拔出重插一次让 udev 重新触发。自此以后烧录和串口调试都可以在 WSL 里无缝进行。5. 让“找板子”变成一条命令工程化烧录的进阶玩法当你能在 WSL 里稳定烧录这块 STM32 板子之后会发现这种命令行工作流越用越顺手。为了彻底告别“板子呢”的困扰我还在项目里做了一些工程化封装让整个过程自动化起来。5.1 编写 Makefile 触发烧录从零到一一个常用的 Makefile 片段会长这样TARGET firmware.elf BUILD_DIR build # 默认烧录目标 flash: $(BUILD_DIR)/$(TARGET) echo Flashing target... openocd -f stlink_flash.cfg -c program $(BUILD_DIR)/$(TARGET) verify reset exit这样make flash就是直接烧录。如果还想修改一下烧录策略比如烧完板子不要自动复位而是停在调试状态flash-noreset: echo Flashing without reset... openocd -f stlink_flash.cfg -c program $(BUILD_DIR)/$(TARGET) verify exit这种灵活性图形化工具根本给不了。5.2 联动 usbipd让命令自己“找板子”有时候插拔多了WSL 可能会丢失 USB 连接。与其手动去 PowerShell 里敲 attach不如直接在 Makefile 里加上一句自动处理的钩子。在脚本开始前可以尝试执行usbipd list | grep -q ST-LINK如果查到 Bound 状态但未 Attached就执行 attach。但这需要 Windows 侧的 PowerShell 权限所以更稳妥的做法是在 Windows 侧定期执行绑定操作或者在 WSL 启动脚本中加入 attach 命令。我个人的习惯是日常开发时先手动 attach 一次然后在整个工作会话期间都不断开。如果中途换插其他 USB 设备就把之前的 detach 掉重新 attach保持每次开发都从干净状态开始。5.3 用 .elf、.hex 还是 .bin烧录文件的取舍OpenOCD 的program命令支持多种格式。我一般用.elf因为它包含调试信息、入口地址和各段信息OpenOCD 可以自动解析。对于量产场景.hex 或 .bin 更常见因为它们体积小、结构简单适合批量写入。如果固件比较大重新烧录前可以先执行擦除命令openocd -f stlink_flash.cfg -c init -c halt -c flash erase_sector 0 0 last -c exit这条命令会擦除整个 Flash确保写入时不会被旧数据干扰。5.4 我踩过的“板子呢”最后一坑环境变量污染在 WSL 里不同项目可能引用不同的编译器和工具链路径。有一次我明明在 A 项目里能烧录在 B 项目里执行同一套命令却报找不到 OpenOCD。检查发现是.bashrc里设置了一个过时的 PATH 变量把 OpenOCD 的路径指到了另一个不存在的目录。排查命令which openocd如果输出的是某个可疑的路径用printenv PATH检查一下环境变量。这个教训让我意识到在 WSL 下开发工具链的路径管理要跟 Windows 侧同样严谨最好统一通过安装系统包或 symlink 来管理。最后说两句体己话折腾完这一整套流程再回头看“我板子呢”这个问题其实没有那么玄妙。无非是设备转发没打通、权限没过、配置文件不对这三类原因。但恰恰是这些看似琐碎的小问题构成了嵌入式开发中真实的“时间黑洞”。我个人现在的习惯是所有嵌入式 C 项目的编译、烧录、调试流程都跑在 WSL 里Windows 只负责编辑器、文档和必要时的硬件管理。这让我在任何一台电脑上都能快速进入状态也让我愿意把“烧录成功”这份底气分享给团队里的每一个人。希望这篇记录也能帮你少走几段弯路。

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

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

免费获取报价 →
↑