1. 课程导入

嵌入式开发的第一步,说白了就是搭好你的"兵器库"。我见过太多初学者卡在环境配置上,代码写好了却烧不进去,那种挫败感我太懂了。这一章咱们就把 Rust 嵌入式开发环境从头到尾捋一遍,确保你手里的工具链是趁手的。

1.1 安装Rust工具链

Rust 的安装其实很简单,但有几个细节我得提醒你。我个人习惯用 rustup 来管理工具链,它就像 Rust 版本的"瑞士军刀"。

核心步骤:

  1. 打开终端,执行 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  2. 安装完成后,运行 source $HOME/.cargo/env 刷新环境变量
  3. 验证安装:rustc --version 和 cargo --version

嗯,这里要注意:如果你在 Windows 上开发,建议用 WSL2 或者直接装 MSVC 工具链。我之前在 Windows 裸机上折腾过,踩了不少坑——链接器报错、路径问题层出不穷。后来切到 WSL2,世界清净了。

1.2 配置交叉编译目标

嵌入式开发跟桌面开发最大的区别是什么?说白了就是"交叉编译"。你的代码在 x86 电脑上写,但最终要跑到 ARM 或 RISC-V 芯片上。所以必须告诉编译器:"嘿,目标不是你自己!"

以最常见的 ARM Cortex-M 系列为例,我们需要添加对应的 target:

rustup target add thumbv7em-none-eabihf   # Cortex-M4/M7 带硬件浮点
rustup target add thumbv6m-none-eabi      # Cortex-M0/M0+
rustup target add thumbv7em-none-eabi     # Cortex-M4 不带硬件浮点

你可能会问:"我怎么知道该选哪个?" 看芯片手册的 Core 字段就行。比如 STM32F407 是 Cortex-M4F,那就用 thumbv7em-none-eabihf。我刚开始做 STM32 项目时,就因为这个选错了 target,编译出来的程序跑起来直接 HardFault,查了半天才发现是浮点单元没对上。

目标三元组 适用架构 典型芯片
thumbv6m-none-eabi Cortex-M0/M0+/M1 STM32F0, nRF51822
thumbv7m-none-eabi Cortex-M3 STM32F1, LPC1768
thumbv7em-none-eabi Cortex-M4 (无FPU) STM32F3
thumbv7em-none-eabihf Cortex-M4F/M7 (有FPU) STM32F4, STM32F7
riscv32imac-unknown-none-elf RISC-V 32位 GD32VF103, CH32V307

小技巧: 如果你不确定芯片的 FPU 情况,可以先选不带 hf 的版本。等程序跑通了,再切到 hf 版本做性能优化。这样能少走弯路。

1.3 安装probe-rs和openocd

工具链配好了,接下来得解决"怎么把程序烧进去"的问题。目前主流方案有两个:probe-rs 和 openocd。我个人的建议是:

  • probe-rs:纯 Rust 实现,安装简单,支持 CMSIS-DAP、ST-Link、J-Link。适合新手和纯 Rust 项目。
  • openocd:老牌工具,功能强大,但配置复杂。适合需要调试复杂场景的老手。

安装 probe-rs 只需要一行命令:

cargo install probe-rs --features cli

安装完成后,用 probe-rs list 检查是否能识别你的调试器。我记得第一次用 ST-Link 时,怎么都识别不到,后来发现是驱动没装好。Windows 上需要装 Zadig 把驱动换成 WinUSB,Linux 上则要配置 udev 规则。

如果你更习惯 openocd,可以这样安装:

# Ubuntu/Debian
sudo apt install openocd

# macOS
brew install openocd

# Windows
# 从 https://openocd.org/ 下载安装包

避坑指南: 我曾经在 Ubuntu 上直接用 apt 装 openocd,结果版本太旧(0.10.x),不支持新出的调试器。后来改成从源码编译,才解决了问题。建议你装之前先 openocd --version 看看版本,低于 0.11 的最好升级一下。

1.4 搭建第一个裸机项目

环境都齐了,咱们来写个"Hello World"——不过嵌入式里的 Hello World 不是打印字符串,而是让 LED 闪起来。这才是嵌入式工程师的浪漫。

先创建一个新项目:

cargo new --bin blinky
cd blinky

然后修改 Cargo.toml,添加依赖:

[package]
name = "blinky"
version = "0.1.0"
edition = "2021"

[dependencies]
cortex-m-rt = "0.7"
cortex-m-semihosting = "0.5"
panic-halt = "0.2"

[[bin]]
name = "blinky"
test = false
bench = false

接着创建 .cargo/config.toml,指定编译目标:

[target.thumbv7em-none-eabihf]
runner = "probe-rs run --chip STM32F407VGTx"

[build]
target = "thumbv7em-none-eabihf"

最后是 src/main.rs 的核心代码:

#![no_std]
#![no_main]

use panic_halt as _;
use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
    // 这里先留空,后续章节会填上GPIO控制逻辑
    loop {}
}

关键点解释:

  • #![no_std]:告诉 Rust 我们不用标准库,因为嵌入式环境没有操作系统支持。
  • #![no_main]:不用常规的 main 函数入口,改用 #[entry] 属性宏。
  • panic_halt:发生 panic 时让程序挂起,避免未定义行为。

编译试试:

cargo build

如果一切顺利,你会在 target/thumbv7em-none-eabihf/debug/ 下看到一个 blinky 文件。这就是你的第一个嵌入式程序——虽然它什么都不做,但至少证明工具链是通的。

个人经验: 我第一次编译裸机项目时,卡在链接阶段报错 "Linking with `cc` failed"。后来发现是没装 ARM 交叉编译器。Ubuntu 上装 gcc-arm-none-eabi 就能解决。如果你用 probe-rs,它自带链接器,一般不会出这个问题。

1.5 本章知识体系

下面这张图帮你理清整个环境搭建的脉络:

Rust嵌入式开发环境搭建知识体系 Rust嵌入式开发环境搭建知识体系 Rust工具链 交叉编译目标 烧录/调试工具 裸机项目模板 ARM Cortex-M RISC-V probe-rs openocd Cargo.toml .cargo/config 第一个可烧录的裸机程序 核心思路:从工具链 → 目标架构 → 烧录工具 → 项目模板,逐层搭建 每一步都验证通过,再进入下一步,避免问题堆积

这张图把整个环境搭建拆成了四个层次。你从最顶层的 Rust 工具链开始,一路往下走,每一步都验证通过再继续。我当年就是按这个顺序来的,虽然中间也踩过坑,但整体还算顺利。