Rust RTIC嵌入式并发工程 - 第1节

1. 课程导入

各位同学好,欢迎来到这门实战课。我先说说这门课的来由。

做了十几年嵌入式开发,我用过C、C++,也踩过无数坑。内存越界、野指针、中断竞争——这些老问题,几乎每个项目都躲不掉。直到我接触了Rust,说实话,有种「终于等到你」的感觉。

但Rust不是银弹。裸机编程本身就有它的硬骨头要啃。RTIC框架的出现,算是把Rust在嵌入式领域的潜力真正释放了出来。我个人习惯把RTIC叫做「中断驱动的协程调度器」,它让并发安全变得可管理。

核心观点: Rust解决内存安全,RTIC解决并发安全。两者结合,裸机编程的体验被彻底改变了。

1.1 为什么是Rust?

你可能会问:C语言用了三十年,不也挺好?

嗯,C确实好。但C的问题在于——它把所有的安全责任都丢给了程序员。你忘了检查边界?段错误。你忘了关中断?死锁。你用了野指针?恭喜,你获得了一个随机崩溃的嵌入式设备。

Rust的所有权模型、借用检查器、生命周期标注——这些机制在编译期就把内存错误堵死了。我在一个电机控制项目中,用Rust重写了原来的C代码,结果发现了一个隐藏了三年的缓冲区溢出bug。那个bug在C代码里一直没触发,但理论上随时可能炸。

对比维度 C语言 Rust
内存安全 全靠程序员自觉 编译期强制保证
并发安全 手动加锁,容易遗漏 Send/Sync trait + 类型系统
零成本抽象 基本没有抽象 泛型、trait、闭包全零成本
裸机支持 成熟但原始 cortex-m-rt + PAC + HAL
生态工具 Makefile + gcc rustup + cargo + probe-rs

说白了,Rust让你在写底层代码时,也能享受到高级语言的安全感。而且它是零成本的——你不用担心性能损失。

1.2 为什么是RTIC?

RTIC全称是Real-Time Interrupt-driven Concurrency。它不是一个操作系统,而是一个框架。它帮你管理中断优先级、任务调度、资源共享。

我记得第一次用RTIC时,最让我惊讶的是它的「无锁数据共享」机制。在传统裸机编程中,中断和主循环共享数据,你得小心翼翼地用关中断或原子操作来保护。RTIC通过优先级 ceiling 协议,在编译期就保证了不会出现优先级反转和数据竞争。

我的经验: 如果你在写一个多中断、多任务的嵌入式项目,RTIC能帮你省掉至少一半的调试时间。我曾经在一个六中断的传感器融合项目中,用RTIC三天就完成了原本预计两周的并发逻辑。

1.3 裸机编程的挑战与机遇

裸机编程,说白了就是没有操作系统。你直接操作寄存器、管理中断、分配内存。挑战很明显:

  • 没有内存保护: 一个越界写就能干掉整个系统
  • 没有任务调度: 你得自己决定什么时候该做什么
  • 没有标准库: 连println!都要自己实现
  • 调试困难: 没有gdb?那就靠LED闪烁吧

但机遇同样存在。裸机编程让你对硬件有完全的控制权。延迟可预测、资源占用极低、没有OS的开销。在IoT设备、电机控制、传感器采集这些场景中,裸机依然是首选。

Rust的出现,让裸机编程的挑战被大幅降低了。你想想看,以前最头疼的内存问题,现在编译器帮你盯着。以前最麻烦的中断竞争,现在RTIC帮你排好队。

注意: Rust裸机编程的学习曲线确实比C要陡。但一旦跨过所有权和生命周期的门槛,你会发现写出来的代码几乎不需要调试内存问题。这个投入是值得的。

搭建基于Cortex-M的Rust开发环境

好,理论说完了,咱们动手。搭建环境这件事,我建议你跟着我的步骤来,一步都不要跳。我曾经帮一个学员排查环境问题,结果发现他跳过了target安装步骤——整整浪费了两天。

2.1 安装rustup

rustup是Rust的工具链管理器。如果你还没装,打开终端执行:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

装完之后,确认一下版本:

rustup --version
rustc --version
cargo --version

我个人习惯用稳定版工具链,但嵌入式开发有时需要nightly特性。别担心,rustup可以同时管理多个工具链。

2.2 添加Cortex-M目标

我们要为Cortex-M系列芯片添加交叉编译目标。以最常见的Cortex-M4为例:

rustup target add thumbv7em-none-eabihf

其他常见目标:

芯片系列 目标三元组
Cortex-M0/M0+ thumbv6m-none-eabi
Cortex-M3 thumbv7m-none-eabi
Cortex-M4 (无FPU) thumbv7em-none-eabi
Cortex-M4 (有FPU) thumbv7em-none-eabihf
Cortex-M7 thumbv7em-none-eabihf

小技巧: 不确定你的芯片用哪个目标?查芯片手册的Core部分,或者直接看cortex-m-rt crate的文档。我一般先查芯片型号,再对表选目标。

2.3 安装probe-rs

probe-rs是一个烧录和调试工具,支持ST-Link、J-Link、DAP-Link等常见调试器。它比openocd好用多了——我个人的真实感受。

cargo install probe-rs --features cli

装完后测试一下:

probe-rs list

如果能看到你的调试器,说明安装成功。我曾经遇到一个坑——Windows下需要安装WinUSB驱动,否则probe-rs认不出设备。如果你也遇到这个问题,用Zadig工具给调试器装WinUSB驱动就行。

2.4 安装cargo-generate

cargo-generate可以帮我们从模板快速生成项目。RTIC官方提供了项目模板,用起来非常方便。

cargo install cargo-generate

然后生成一个RTIC项目:

cargo generate --git https://github.com/rtic-rs/app-template

跟着提示输入项目名称、芯片型号、调试器类型。几分钟后,一个完整的RTIC项目就准备好了。

2.5 验证环境:点亮LED

环境搭好了,咱们跑个经典的LED闪烁程序验证一下。这是RTIC项目的main.rs核心部分:

#![no_std]
#![no_main]

use panic_halt as _;
use rtic::app;

#[app(device = stm32f4xx_hal::pac, peripherals = true)]
const APP: () = {
    #[init]
    fn init(cx: init::Context) {
        let dp = cx.device;
        let rcc = dp.RCC.constrain();
        let clocks = rcc.cfgr.sysclk(48.MHz()).freeze();
        let gpioc = dp.GPIOC.split();
        let mut led = gpioc.pc13.into_push_pull_output();

        led.set_high();
    }

    #[idle]
    fn idle(_cx: idle::Context) -> ! {
        loop {
            continue;
        }
    }
};

编译:

cargo build --release

烧录:

cargo run --release

如果LED亮了,恭喜你——环境搭建成功。

避坑指南: 我曾经在烧录时遇到「芯片锁定」的问题——调试器连不上芯片。原因是之前用其他工具烧录时设置了读保护。解决办法:用probe-rs的unlock命令,或者用ST-Link Utility解除保护。别慌,芯片一般不会真的锁死。

本章知识体系

下面这张图总结了本章的核心脉络,你可以把它当作学习地图:

第一章 知识体系结构图 嵌入式Rust裸机编程 为什么是Rust? 为什么是RTIC? 裸机编程挑战与机遇 内存安全:所有权 + 借用检查 零成本抽象:泛型 + trait 裸机支持:cortex-m-rt + PAC 中断驱动并发模型 无锁数据共享:Ceiling协议 编译期优先级分析 rustup + target添加 probe-rs烧录调试 cargo-generate模板 核心目标:搭建可用的Cortex-M Rust开发环境 理解Rust+RTIC为何适合裸机编程

这张图把本章内容分成了三大块:Rust的优势、RTIC的价值、裸机编程的挑战与机遇。底部是环境搭建的四个核心工具。你可以用它来检查自己是否遗漏了某个知识点。

本章小结: Rust + RTIC的组合,让嵌入式裸机编程从「刀耕火种」进入了「工业化生产」阶段。环境搭建是第一步,也是最关键的一步。工具链装好了,后面的事情就顺了。