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();
}
}
这段代码做了三件事:
- 获取外设所有权(Rust的所有权系统在这里发挥作用)
- 把PB0配置为推挽输出(对应板子上的DS0 LED)
- 用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」这个动机出发,到工具链安装、开发板认识,最后用LED闪烁程序把整个流程跑通。后面的课程都会基于这个环境展开。
我的建议: 别急着往后学。先把环境搭好,LED点起来,感受一下Rust嵌入式的开发流程。这一步走稳了,后面DMA、中断那些内容才能学得扎实。
好,第一章就到这儿。工具链装好了,LED亮了,咱们算是正式入门了。后面每一章都会比这一章更有挑战,但也更有意思。准备好了吗?