1、课程导学与环境准备:为什么选择Rust做嵌入式开发?

各位同学,欢迎来到《STM32移植Rust嵌入式HAL实战》的第一章。

我是你们这门课的老朋友。做了十几年嵌入式开发,从C到C++,再到这几年一头扎进Rust的世界,感触挺深的。今天咱们不急着敲代码,先聊聊为什么选Rust,再把吃饭的家伙——工具链和环境——给搭好。

1.1 为什么是Rust?

你可能会问:C语言用了这么多年,生态成熟,资料遍地,为什么要折腾Rust?

我个人的体会是:Rust解决了C语言里最让人头疼的两个问题——内存安全和数据竞争。

在嵌入式领域,这两个问题尤其致命。一个野指针,一个全局变量被中断和主循环同时修改,轻则跑飞,重则烧板子。我在项目中遇到过不止一次,排查起来真是欲哭无泪。

Rust的核心承诺:

  • 内存安全:所有权和借用机制,编译期就帮你检查指针合法性,没有空悬指针、野指针。
  • 无数据竞争:借用规则保证同一时刻只有一个写者或多个读者,不会出现并发修改。
  • 零成本抽象:高级抽象(如迭代器、闭包)在编译后和手写C一样高效,不增加运行时开销。
  • 现代工具链:包管理器Cargo、内置测试、格式化工具,开箱即用。

说白了,Rust把很多运行时才能暴露的问题,提前到编译期就给你堵死了。你想想看,这能省多少调试时间?

特性 C语言 Rust
内存安全 依赖程序员经验,易出错 编译期强制检查
数据竞争 需手动加锁,易遗漏 借用规则自动保证
抽象开销 低,但抽象能力弱 零成本抽象,功能强大
包管理 手动管理,混乱 Cargo,自动依赖解析
生态成熟度 极高 快速增长中

当然,Rust也有学习曲线。但一旦跨过那个坎,你会发现写嵌入式代码变成了一件很安心的事。

1.2 安装Rust工具链

好,理论说完了,咱们动手。第一步,装Rust。

  1. 安装rustup:Rust的版本管理器和工具链安装器。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

运行后按提示选择默认安装即可。装完后重启终端,或者执行 source $HOME/.cargo/env 让环境变量生效。

  1. 验证安装:
rustc --version
cargo --version

看到版本号输出,说明装好了。

  1. 添加ARM交叉编译目标:STM32是ARM Cortex-M内核,我们需要交叉编译。
rustup target add thumbv7m-none-eabi   # 适用于STM32F1系列(Cortex-M3)
# 如果你用STM32F4(Cortex-M4),用这个:
rustup target add thumbv7em-none-eabi
# 如果你用STM32F0(Cortex-M0),用这个:
rustup target add thumbv6m-none-eabi

小提示: 不确定你的芯片用哪个target?查一下芯片手册里的内核型号,或者直接看STM32CubeMX里的Device页面。我一般先装全了,省得后面换芯片时再折腾。

1.3 配置STM32开发环境:OpenOCD和GDB

有了Rust,我们还需要一个调试器来烧录和调试。我习惯用OpenOCD + GDB的组合,免费、强大、跨平台。

1.3.1 安装OpenOCD

OpenOCD负责和调试器(如ST-Link、J-Link)通信,把程序烧进芯片,并提供GDB服务。

  • Ubuntu/Debian:
sudo apt install openocd
  • macOS:
brew install openocd

装完后验证:

openocd --version

1.3.2 安装GDB(ARM版本)

GDB是调试器,我们需要ARM交叉编译版本的GDB。

  • Ubuntu/Debian:
sudo apt install gdb-multiarch
  • macOS:
brew install arm-none-eabi-gdb
  • Windows:可以从ARM官网下载GNU Arm Embedded Toolchain,里面包含arm-none-eabi-gdb。

注意: 我曾经在Windows上踩过一个坑——OpenOCD和GDB的路径里有空格,导致启动脚本报错。建议把安装路径改成纯英文、无空格的目录,比如 C:\tools\openocd。

1.4 创建第一个Rust嵌入式项目

环境搭好了,我们来写个经典的“点灯”程序。这是嵌入式界的Hello World。

1.4.1 使用cargo-generate创建项目

我推荐用现成的模板,省去手动配置的麻烦。这里用 cortex-m-quickstart 模板。

# 安装cargo-generate
cargo install cargo-generate

# 生成项目
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart

按提示输入项目名称,比如 stm32-blink。

1.4.2 配置芯片型号

进入项目目录,打开 .cargo/config.toml,修改编译目标:

[target.thumbv7m-none-eabi]
# 替换为你的芯片型号
rustflags = ["-C", "link-arg=-Tlink.x", "-C", "link-arg=-Tdefmt.x"]

[build]
target = "thumbv7m-none-eabi"

然后打开 memory.x 文件,根据你的芯片修改Flash和RAM大小。以STM32F103C8为例:

MEMORY
{
  FLASH : ORIGIN = 0x08000000, LENGTH = 64K
  RAM   : ORIGIN = 0x20000000, LENGTH = 20K
}

1.4.3 编写点灯代码

打开 src/main.rs,替换为以下内容:

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use panic_halt as _;
use stm32f1xx_hal::{
    pac,
    prelude::*,
};

#[entry]
fn main() -> ! {
    // 获取外设访问权限
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    // 初始化GPIO
    let mut gpioc = dp.GPIOC.split();
    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);

    // 初始化延时
    let mut delay = cp.SYST.delay(&dp.RCC.clocks);

    loop {
        led.set_high().unwrap();
        delay.delay_ms(1000u32);
        led.set_low().unwrap();
        delay.delay_ms(1000u32);
    }
}

代码说明:

  • #![no_std]:告诉Rust不使用标准库,因为嵌入式环境没有操作系统支持。
  • #![no_main]:不使用标准main函数入口,改用cortex_m_rt提供的入口。
  • stm32f1xx_hal:STM32F1系列的硬件抽象层库,封装了寄存器操作。
  • pc13:STM32F103C8板载LED通常接在PC13引脚。

1.4.4 编译和烧录

编译:

cargo build --release

烧录前,先启动OpenOCD。连接好ST-Link和开发板,在项目目录下执行:

openocd -f interface/stlink.cfg -f target/stm32f1x.cfg

保持OpenOCD窗口开着,另开一个终端,用GDB烧录:

arm-none-eabi-gdb -q target/thumbv7m-none-eabi/release/stm32-blink
(gdb) target remote :3333
(gdb) load
(gdb) continue

看到板载LED开始闪烁了吗?恭喜,你的第一个Rust嵌入式程序跑起来了!

避坑指南: 如果烧录时报错 target not halted,试试在GDB里先执行 monitor reset halt 再 load。我曾经被这个问题卡了半小时,后来发现是调试器供电不稳,换了个USB口就好了。

1.5 本章知识体系

下面这张图帮你梳理本章的核心脉络:

第一章知识体系图 第一章:课程导学与环境准备 为什么选择Rust? 安装Rust工具链 配置STM32开发环境 内存安全 无数据竞争 零成本抽象 安装rustup 添加交叉编译目标 安装OpenOCD 安装GDB 产出:第一个Rust嵌入式项目(点灯) 💡 本章核心 理解Rust在嵌入式领域的优势 → 搭建完整工具链 → 跑通第一个点灯程序 这是后续所有章节的基础,务必亲手操作一遍

好了,第一章的内容就到这里。环境搭好了,项目跑起来了,后面我们就可以开始深入探索Rust嵌入式HAL的各个模块了。