1. 嵌入式Rust开发环境搭建
说实话,嵌入式Rust开发的门槛,有一半都在环境搭建上。我刚开始接触那会儿,光是搞清楚工具链怎么配、目标平台怎么选,就折腾了两三天。但一旦把这条路走通,后面就顺畅多了。
这一章,我们就来一步步把环境搭好。你跟着我走,应该不会踩太多坑。
1.1 安装Rust工具链
首先,你得有Rust。这个很简单,打开终端,敲一行命令:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
安装完成后,重启终端,验证一下:
rustc --version
cargo --version
看到版本号,就说明装好了。我个人习惯用稳定版,因为嵌入式开发对稳定性要求高,nightly 虽然有些新特性,但偶尔会出幺蛾子。
1.2 目标平台配置
嵌入式开发跟桌面开发最大的区别,就是交叉编译。你的电脑是 x86_64 架构,但目标芯片可能是 ARM Cortex-M、RISC-V 或者其他。所以,你得告诉 Rust:我要编译成什么架构的代码。
常用的目标平台有这些:
| 目标平台 | 架构 | 常见芯片 | 添加命令 |
|---|---|---|---|
thumbv6m-none-eabi |
Cortex-M0, M0+ | STM32F0, nRF51822 | rustup target add thumbv6m-none-eabi |
thumbv7m-none-eabi |
Cortex-M3 | STM32F1, LPC1768 | rustup target add thumbv7m-none-eabi |
thumbv7em-none-eabi |
Cortex-M4, M7 | STM32F4, nRF52840 | rustup target add thumbv7em-none-eabi |
riscv32imac-unknown-none-elf |
RISC-V 32位 | GD32VF103, CH32V307 | rustup target add riscv32imac-unknown-none-elf |
riscv64imac-unknown-none-elf |
RISC-V 64位 | K210, D1 | rustup target add riscv64imac-unknown-none-elf |
举个例子,如果你用的是 STM32F103(Cortex-M3),就执行:
rustup target add thumbv7m-none-eabi
然后,在项目根目录创建 .cargo/config.toml 文件,写入:
[build]
target = "thumbv7m-none-eabi"
这样,每次 cargo build 就会自动交叉编译到目标平台。嗯,这里要注意:别写错 target 名字,我刚开始就手抖写成了 thumbv7em,结果编译出来的程序在 M3 上跑不起来,查了半天才发现。
1.3 嵌入式标准库与 no_std 环境
桌面 Rust 程序默认依赖 std 标准库,它提供了文件系统、网络、线程等功能。但嵌入式芯片通常没有操作系统,也没有堆内存管理,所以标准库的大部分功能都用不了。
这时候,我们就需要 #![no_std] 属性。它告诉编译器:我不要标准库,只用核心库 core。
一个典型的嵌入式 Rust 项目入口是这样的:
#![no_std]
#![no_main]
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[entry]
fn main() -> ! {
loop {}
}
这里有几个关键点:
#![no_std]:禁用标准库。#![no_main]:禁用默认的 main 函数入口,因为嵌入式环境没有操作系统来调用 main。#[panic_handler]:定义 panic 时的行为。我一般会在里面加个 LED 闪烁或者串口打印,方便调试。#[entry]:来自cortex-m-rt或riscv-rt库,标记程序入口点。
println!、Vec、String 等标准库类型。如果需要打印调试信息,得用 cortex-m-semihosting 或 rtt 这类库。我曾经在项目里忘了这一点,写了一大堆 println!,编译直接报错,尴尬得很。
1.4 调试工具:probe-rs 与 OpenOCD
写嵌入式程序,调试是逃不掉的。我推荐两个工具:probe-rs 和 OpenOCD。
1.4.1 probe-rs
probe-rs 是 Rust 社区开发的调试工具,原生支持 Rust,用起来非常顺手。安装很简单:
cargo install probe-rs --features cli
然后,用 probe-rs list 查看连接的调试器:
$ probe-rs list
The following devices were found:
[0]: J-Link (VID: 1366, PID: 0101, Serial: 123456789)
烧录和调试可以一条命令搞定:
probe-rs run --chip STM32F103C8 target/thumbv7m-none-eabi/debug/my-app
我个人特别喜欢 probe-rs 的 probe-rs debug 命令,它启动一个 GDB 服务器,然后你可以用 VSCode 或者命令行 GDB 进行断点调试。我在项目中用它调试过一个诡异的定时器中断问题,效率比用串口打印高太多了。
1.4.2 OpenOCD
OpenOCD 是老牌工具,支持各种调试器和芯片。安装方式因系统而异:
- Ubuntu/Debian:
sudo apt install openocd - macOS:
brew install openocd - Windows: 从官网下载安装包,或者用
scoop install openocd
启动 OpenOCD 连接调试器:
openocd -f interface/jlink.cfg -f target/stm32f1x.cfg
然后,在另一个终端用 GDB 连接:
gdb-multiarch target/thumbv7m-none-eabi/debug/my-app
(gdb) target remote :3333
(gdb) load
(gdb) continue
1.5 知识体系总览
下面这张图,把整个环境搭建的流程和关系梳理了一下。你跟着这个结构走,就不会乱。
1.6 验证环境
环境搭好之后,最好跑一个简单的例子验证一下。我一般用 cortex-m-quickstart 模板:
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
然后编译:
cargo build
如果编译通过,没有报错,说明环境基本没问题。接下来,你可以用 probe-rs 或者 OpenOCD 烧录到开发板上,看到 LED 闪烁或者串口输出,就大功告成了。
LINKER 相关的错误,原因是没安装 arm-none-eabi-gcc 或者链接器脚本没配置对。解决方案是:sudo apt install gcc-arm-none-eabi,然后在 .cargo/config.toml 里指定链接器。
好了,环境搭建就到这里。记住,这一步虽然繁琐,但值得花时间把它搞扎实。后面写驱动、调外设的时候,你会发现今天花的时间都是值得的。