1. 课程导学与开发环境搭建

各位同学,欢迎来到《Rust嵌入式DMA传输与中断协同优化性能调优实战》的第一章。我是你们这门课的主讲,做了十几年嵌入式开发,最近几年一头扎进Rust的世界里,发现这东西在嵌入式领域是真的香。

这一章咱们不急着讲什么高深的理论,先把家伙事儿备齐了。你想想看,工欲善其事必先利其器,环境搭不好,后面全是坑。我见过太多人卡在第一步就放弃了,挺可惜的。

1.1 Rust嵌入式开发:为什么是Rust?

先聊聊为什么我们要用Rust做嵌入式。说白了,C语言统治了嵌入式世界几十年,确实够强,但有些问题它解决不了——内存泄漏、野指针、数据竞争。这些问题在PC上可能只是蓝屏,在嵌入式设备上可能就是炸飞了。

Rust呢?它从语言层面就把这些问题堵死了。所有权系统、借用检查器、生命周期标注——这些概念听起来玄乎,但用起来你会发现,编译器就像个严格的教官,你代码写得不规范,它直接不让你编译通过。

核心优势对比

特性 C语言 Rust
内存安全 全靠程序员自觉 编译器强制保证
数据竞争 运行时可能发生 编译期杜绝
零成本抽象 有限 强(无运行时开销)
生态成熟度 极高 快速成长中
学习曲线 平缓 陡峭但值得

我个人习惯把Rust比作「带安全带的跑车」。你开起来可能觉得束缚多,但真出了事,安全带能救你的命。我在一个工业控制项目里用过Rust,当时有个同事用C写了个DMA缓冲区溢出,查了三天才找到。换成Rust,编译器当场就报错了。

1.2 工具链安装:把家伙事儿备齐

好,咱们开始动手。安装工具链这事儿,我建议你按顺序来,别跳步。

1.2.1 安装Rust主工具链

打开终端,执行这条命令:

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

安装完成后,重启终端或者执行 source $HOME/.cargo/env。验证一下:

rustc --version
cargo --version

嗯,看到版本号就对了。我建议你安装稳定版,别追nightly,除非你特别需要某些实验特性。

1.2.2 添加ARM目标

咱们用的是STM32F4,ARM Cortex-M4内核。需要添加交叉编译目标:

rustup target add thumbv7em-none-eabihf

这个目标名有点长,但别嫌麻烦。它告诉编译器:生成ARMv7E-M架构的代码,硬件浮点,无操作系统。

小提示: 如果你用的是STM32F4的某些变体(比如F401),也是这个目标。Cortex-M4F都带硬件FPU,用 eabihf 后缀能充分发挥硬件浮点性能。

1.2.3 安装调试工具

调试是嵌入式开发的命根子。我推荐用 probe-rs,它比OpenOCD好用多了:

cargo install probe-rs-tools

安装完成后,插上你的调试器(ST-Link或者J-Link),执行:

probe-rs list

能看到设备信息,说明调试链路通了。我曾经有一次怎么都连不上,最后发现是USB线的问题——换了根带屏蔽的线就好了。嗯,这种坑你早晚也会遇到。

1.3 STM32F4开发板介绍

咱们课程用的是STM32F407VGT6,也就是常见的「探索者」开发板。为什么选它?

  • 性能够用: 168MHz主频,1MB Flash,192KB SRAM
  • 外设丰富: 3个SPI、3个I2C、6个USART、2个DMA控制器
  • 生态成熟: 资料多,遇到问题好查

来看看板子上的关键资源:

资源 数量/规格 课程用途
LED 2个(DS0、DS1) 基础输出实验
按键 4个(KEY0~KEY3) 中断输入实验
DMA控制器 2个,共16个流 DMA传输核心实验
定时器 14个 PWM、输入捕获
USART 6个 串口DMA通信

我个人建议你买一块一样的板子,这样跟着课程做,代码直接跑。如果你用其他型号,注意引脚和外设的差异,但原理是一样的。

1.4 第一个LED闪烁程序

终于到了动手环节。咱们用Rust让板子上的LED闪起来——这是嵌入式世界的「Hello World」。

1.4.1 创建项目

cargo new --bin stm32f4-led-blink
cd stm32f4-led-blink

1.4.2 配置依赖

编辑 Cargo.toml:

[dependencies]
cortex-m-rt = "0.7"
cortex-m-semihosting = "0.5"
panic-halt = "0.2"
stm32f4xx-hal = { version = "0.15", features = ["stm32f407"] }

[[bin]]
name = "led-blink"
path = "src/main.rs"

这里我用了 stm32f4xx-hal 这个硬件抽象层,它封装了寄存器操作,让我们能用Rust的风格写代码。你想想看,要是直接操作寄存器,代码会变成一坨 unsafe,那还不如用C呢。

1.4.3 编写代码

打开 src/main.rs,写入:

#![no_std]
#![no_main]

use panic_halt as _;
use cortex_m_rt::entry;
use stm32f4xx_hal::{
    prelude::*,
    pac,
    gpio::GpioExt,
    timer::Timer,
};

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

    // 初始化GPIO
    let gpiob = dp.GPIOB.split();
    let mut led = gpiob.pb0.into_push_pull_output();

    // 配置定时器
    let mut timer = Timer::syst(cp.SYST, 1.hz(), clock);

    loop {
        led.set_high().unwrap();
        timer.wait();
        led.set_low().unwrap();
        timer.wait();
    }
}

这段代码做了三件事:

  1. 获取外设所有权(Rust的所有权系统在这里发挥作用)
  2. 把PB0配置为推挽输出(对应板子上的DS0 LED)
  3. 用SysTick定时器实现1Hz闪烁

注意: 这里用了 unwrap(),在正式产品代码中应该做错误处理。但教学阶段,先让代码跑起来再说。

1.4.4 编译与烧录

编译:

cargo build --release

烧录:

probe-rs run --chip STM32F407VGTx target/thumbv7em-none-eabihf/release/led-blink

看到板子上的LED开始闪烁了吗?如果没亮,检查这几个地方:

  • LED是不是接在PB0上(不同板子可能不同)
  • 调试器连接是否牢固
  • 电源是否正常(3.3V)

我第一次用Rust点灯的时候,折腾了半小时没亮,最后发现是忘记接GND了。嗯,这种低级错误,谁还没犯过呢?

1.5 本章知识体系

咱们把这一章的内容串起来,画个图看看整体结构:

第一章 知识体系结构 Rust嵌入式开发环境 为什么用Rust? 工具链安装 STM32F4开发板 第一个LED闪烁 rustup ARM目标 probe-rs cargo 💡 核心要点 所有权系统保证内存安全 零成本抽象无运行时开销 编译器严格但可靠

这张图把咱们这一章的内容串起来了。从「为什么用Rust」这个动机出发,到工具链安装、开发板认识,最后用LED闪烁程序把整个流程跑通。后面的课程都会基于这个环境展开。

我的建议: 别急着往后学。先把环境搭好,LED点起来,感受一下Rust嵌入式的开发流程。这一步走稳了,后面DMA、中断那些内容才能学得扎实。

好,第一章就到这儿。工具链装好了,LED亮了,咱们算是正式入门了。后面每一章都会比这一章更有挑战,但也更有意思。准备好了吗?