4、probe-rs深度解析:安装、工具链与VS Code集成
probe-rs,说白了就是嵌入式Rust调试的瑞士军刀。它把调试探针、烧录、GDB通信这些杂事全包了。我刚开始用的时候也觉得这东西就是个烧录工具,后来才发现它远不止这么简单。
4.1 probe-rs是什么?为什么它这么重要?
传统嵌入式开发,你得装OpenOCD、装GDB、装各种厂商的驱动。probe-rs把这些全整合了。它直接通过USB跟调试探针通信,不需要中间人。你想想看,少了一层依赖,出问题的概率就少了一大半。
核心能力一览:
- 烧录:支持ELF/HEX/BIN格式,自动解析
- 调试:内置GDB stub,支持断点、单步、内存查看
- RTT:实时终端输出,比串口好用十倍
- 硬件抽象:统一API,换探针不用改代码
我个人习惯把probe-rs看作一个硬件抽象层。不管你是用J-Link、ST-Link还是CMSIS-DAP,probe-rs都给你一套统一的接口。我在项目中遇到过最头疼的事,就是团队里有人用J-Link有人用ST-Link,烧录脚本各写各的。换成probe-rs后,一个命令通吃。
4.2 安装probe-rs
安装其实很简单,但有几个坑我得提前说。
4.2.1 标准安装方式
# 安装probe-rs工具链
cargo install probe-rs --features cli
# 验证安装
probe-rs --version
# 列出连接的调试探针
probe-rs list
嗯,这里要注意。如果你在Linux上,记得把当前用户加到plugdev组,不然没权限访问USB设备。
sudo usermod -a -G plugdev $USER
# 然后注销重新登录
我曾经踩过的坑:Windows上如果装了ST-Link的官方驱动,probe-rs可能认不到探针。原因是WinUSB驱动冲突。解决办法是用Zadig工具把探针的驱动换成WinUSB。别问我怎么知道的,那次折腾了我一下午。
4.2.2 从源码编译(可选)
如果你想用最新特性,或者你的平台没有预编译包,可以自己编译:
git clone https://github.com/probe-rs/probe-rs
cd probe-rs
cargo build --release --features cli
# 编译产物在 target/release/probe-rs
编译时间有点长,大概5-10分钟。我建议除非必要,否则用cargo install就够了。
4.3 cargo-flash:一键烧录
cargo-flash是probe-rs提供的Cargo子命令。说白了,就是cargo flash代替了cargo build + 手动烧录的繁琐流程。
4.3.1 基本用法
# 烧录当前项目
cargo flash --chip STM32F103C8
# 指定芯片和探针
cargo flash --chip nRF52840_xxAA --probe 0
# 烧录并复位运行
cargo flash --chip STM32F407VG --reset
你可能会问:芯片型号怎么查?用probe-rs chip list可以列出所有支持的芯片。我数过,大概有3000多种。基本上你能想到的MCU都在里面。
4.3.2 配置文件
每次敲--chip太麻烦。我习惯在项目根目录建一个.cargo/config.toml:
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "probe-rs run --chip STM32F103C8"
[env]
DEFMT_LOG = "debug"
这样你只需要:
cargo run
就能自动编译+烧录+运行。省事多了。
小技巧:如果你用defmt做日志,记得在配置里加上DEFMT_LOG环境变量。不然你什么都看不到,还以为程序没跑起来。
4.4 cargo-embed:调试与RTT的瑞士军刀
cargo-embed比cargo-flash更进一步。它不仅能烧录,还能启动GDB调试、打开RTT终端、监控内存。我90%的调试工作都靠它完成。
4.4.1 安装与配置
cargo install cargo-embed
然后你需要一个Embed.toml配置文件,放在项目根目录:
[default.general]
chip = "STM32F103C8"
[default.flashing]
enabled = true
halt_afterwards = false
[default.rtt]
enabled = true
[default.gdb]
enabled = true
gdb_connection_string = "pipe:arm-none-eabi-gdb"
这个配置的意思是:烧录后直接运行,开启RTT输出,同时启动GDB服务。你可以在VS Code里连上这个GDB服务进行调试。
4.4.2 启动调试会话
cargo embed
就这么简单。你会看到终端里同时显示RTT日志和GDB端口信息。我个人习惯开两个终端:一个跑cargo embed,另一个用GDB连上去做断点调试。
RTT vs 串口:为什么我推荐RTT?
- 速度:RTT走SWD,速度是串口的几十倍
- 不占外设:串口需要UART引脚,RTT不需要
- 双向通信:RTT支持输入,你可以给MCU发命令
- 低延迟:RTT延迟在微秒级,串口在毫秒级
4.5 与VS Code集成
命令行虽然强大,但图形化调试确实更直观。VS Code加上probe-rs插件,体验不输IDE。
4.5.1 安装插件
在VS Code扩展市场搜索probe-rs,安装官方插件。装好后,你会在左侧看到一个新的调试面板。
4.5.2 配置launch.json
按F5,选择probe-rs调试器,VS Code会自动生成.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "probe-rs",
"request": "launch",
"name": "probe-rs Debug",
"cwd": "${workspaceFolder}",
"chip": "STM32F103C8",
"flashingConfig": {
"flashingEnabled": true,
"haltAfterReset": false
},
"coreConfigs": [
{
"coreIndex": 0,
"programBinary": "target/thumbv7m-none-eabi/debug/your-project"
}
]
}
]
}
这里有个坑:programBinary路径要跟你cargo build的输出一致。我建议用${workspaceFolder}/target/thumbv7m-none-eabi/debug/${workspaceFolderBasename}这种动态路径,省得每次改。
4.5.3 调试体验
配置好后,按F5就能开始调试。你能看到:
- 断点:点一下行号就能设断点,跟调试桌面程序一样
- 变量监视:鼠标悬停在变量上就能看到值
- 调用栈:能看到当前执行到哪个函数
- 外设寄存器:可以直接查看和修改MCU的寄存器值
我的调试习惯:我会在main函数入口设一个断点,然后单步执行。这样能确保MCU启动流程没问题。如果连main都进不去,那肯定是启动文件或者链接脚本的问题。
4.6 知识体系总览
下面这张图总结了probe-rs的完整工作流程:
4.7 常见问题与避坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| probe-rs list 找不到探针 | 驱动冲突或权限不足 | Linux加plugdev组;Windows用Zadig换WinUSB驱动 |
| 烧录后程序不运行 | halt_afterwards = true | 在Embed.toml里设置halt_afterwards = false |
| RTT没有输出 | 没启用defmt或RTT通道 | 检查Cargo.toml是否加了defmt依赖,Embed.toml是否开启rtt |
| VS Code连不上GDB | 端口被占用或路径不对 | 先关掉其他调试会话,检查launch.json里的端口号 |
我曾经踩过的坑:有一次我烧录nRF52840,怎么都连不上。折腾了两小时,最后发现是USB线的问题。那根线只能充电不能传数据。所以,如果你遇到莫名其妙的连接问题,先换根USB线试试。这听起来很蠢,但真的能解决80%的问题。
4.8 小结
probe-rs把嵌入式调试的门槛降了一大截。你不需要再折腾OpenOCD的配置文件,不需要记一堆GDB命令。一个cargo embed搞定烧录、调试、日志输出。配合VS Code,调试体验跟桌面开发没什么区别。
我个人建议:刚开始用的时候,先跑通cargo flash烧录一个LED闪烁程序。确认硬件连接没问题后,再上cargo embed和VS Code调试。一步一步来,别想一口吃成胖子。