嵌入式 Rust 编程完全指南 2026

引言 嵌入式开发长期被 C/C++ 主导,但内存安全问题频发。Rust 凭借零成本抽象和编译期内存安全,正在成为嵌入式开发的新选择。STM32、ESP32、nRF 等主流 MCU 都已支持 Rust。 本文从零开始,带你掌握嵌入式 Rust 开发的核心技能。 为什么选择嵌入式 Rust? 1.1 内存安全 without GC // C 代码:可能的空指针解引用 int *ptr = get_sensor_data(); int value = *ptr; // ❌ 如果 ptr 为空,崩溃 // Rust 代码:编译期检查 let data = get_sensor_data(); let value = *data; // ✅ Option 类型强制处理 None 情况 1.2 所有权系统防止数据竞争 // 多任务访问共享资源 static mut SENSOR_DATA: u32 = 0; // ❌ C 的全局变量,不安全 // Rust 使用 Mutex 保护共享数据 static SENSOR_DATA: Mutex<u32> = Mutex::new(0); // ✅ 编译期保证线程安全 1.3 零成本抽象 // trait 在编译期展开,无运行时开销 trait Sensor { fn read(&self) -> u16; } struct TemperatureSensor { /* ... */ } impl Sensor for TemperatureSensor { fn read(&self) -> u16 { // 直接编译为高效机器码 } } 环境搭建 2.1 安装 Rust 工具链 # 安装 rustup curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # 添加嵌入式目标 rustup target add thumbv7em-none-eabihf # Cortex-M4/M7 rustup target add thumbv6m-none-eabi # Cortex-M0/M0+ # 安装必要工具 cargo install cargo-binutils rustup component add llvm-tools-preview 2.2 项目模板 # 使用 cargo-generate 创建项目 cargo install cargo-generate cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart 项目结构 my-embedded-app/ Cargo.toml # 依赖配置 build.rs # 构建脚本 memory.x # 内存布局 src/ main.rs # 入口 peripherals.rs # 外设驱动 嵌入式 Rust 项目结构 2.3 Cargo.toml 配置 [package] name = "my-embedded-app" version = "0.1.0" edition = "2021" [dependencies] cortex-m = "0.7" cortex-m-rt = "0.7" panic-halt = "0.2" embedded-hal = "0.2" # 芯片特定 PAC stm32f4xx-hal = { version = "0.21", features = ["rt", "stm32f407"] } [profile.release] lto = true debug = true 核心概念 3.1 外设访问 #![no_std] #![no_main] use panic_halt as _; use cortex_m_rt::entry; use stm32f4xx_hal::{ pac, prelude::*, }; #[entry] fn main() -> ! { // 获取外设寄存器 let dp = pac::Peripherals::take().unwrap(); let cp = cortex_m::Peripherals::take().unwrap(); // 配置时钟 let rcc = dp.RCC.constrain(); let clocks = rcc.cfgr.sysclk(168.MHz()).freeze(); // 配置 GPIO let gpioa = dp.GPIOA.split(); let mut led = gpioa.pa5.into_push_pull_output(); // 闪烁 LED loop { led.set_high(); cortex_m::asm::delay(8_000_000); led.set_low(); cortex_m::asm::delay(8_000_000); } } 3.2 中断处理 use cortex_m_rt::exception; use stm32f4xx_hal::pac::interrupt; // 系统异常 #[exception] fn HardFault(ef: &cortex_m_rt::ExceptionFrame) -> ! { // 记录错误信息 loop {} } // 外部中断 #[interrupt] fn EXTI0() { // 清除中断标志 let dp = unsafe { pac::Peripherals::steal() }; dp.EXTI.pr.write(|w| w.pr0().set_bit()); // 处理中断逻辑 } 3.3 使用 embedded-hal trait use embedded_hal::digital::v2::OutputPin; use embedded_hal::blocking::delay::DelayMs; // 编写通用驱动 pub struct LedDriver<PIN> { pin: PIN, } impl<PIN> LedDriver<PIN> where PIN: OutputPin, { pub fn blink<D>(&mut self, delay: &mut D, times: u8) where D: DelayMs<u8>, { for _ in 0..times { self.pin.set_high().unwrap(); delay.delay_ms(500); self.pin.set_low().unwrap(); delay.delay_ms(500); } } } 实战项目:温湿度传感器 4.1 硬件连接 引脚 STM32F4 AHT20 传感器 SCL PB6 SCL SDA PB7 SDA VCC 3.3V VDD GND GND GND 4.2 I2C 驱动代码 use stm32f4xx_hal::{ i2c::{I2c, DutyCycle, Mode}, pac::I2C1, }; pub struct AHT20<I2C> { i2c: I2C, } impl<I2C> AHT20<I2C> where I2C: embedded_hal::blocking::i2c::WriteRead + embedded_hal::blocking::i2c::Write, { pub fn new(i2c: I2C) -> Self { Self { i2c } } pub fn measure(&mut self) -> Result<(f32, f32), ()> { // 启动测量 self.i2c.write(0x38, &[0xAC, 0x33, 0x00]).map_err(|_| ())?; // 等待转换完成 cortex_m::asm::delay(80_000); // 读取数据 let mut buf = [0u8; 6]; self.i2c.write_read(0x38, &[0x71], &mut buf).map_err(|_| ())?; // 解析温湿度 let humidity = ((buf[1] as u32) << 12 | (buf[2] as u32) << 4 | (buf[3] >> 4) as u32) as f32; let temperature = (((buf[3] & 0x0F) as u32) << 16 | (buf[4] as u32) << 8 | buf[5] as u32) as f32; Ok(( humidity / 1048576.0 * 100.0, temperature / 1048576.0 * 200.0 - 50.0, )) } } 4.3 主程序 #[entry] fn main() -> ! { let dp = pac::Peripherals::take().unwrap(); // 配置时钟 let rcc = dp.RCC.constrain(); let clocks = rcc.cfgr.sysclk(168.MHz()).freeze(); // 配置 I2C let gpiob = dp.GPIOB.split(); let scl = gpiob.pb6.into_alternate::<4>(); let sda = gpiob.pb7.into_alternate::<4>(); let i2c = I2c::new( dp.I2C1, (scl, sda), Mode::Fast { frequency: 400.kHz(), duty_cycle: DutyCycle::Ratio2to1, }, clocks, ); // 创建传感器 let mut sensor = AHT20::new(i2c); loop { match sensor.measure() { Ok((humidity, temperature)) => { // 通过串口打印 println!("温度:{:.2}°C, 湿度:{:.2}%", temperature, humidity); } Err(_) => { println!("传感器读取失败"); } } cortex_m::asm::delay(80_000_000); // 1 秒 } } 调试技巧 5.1 使用 defmt 日志 # Cargo.toml [dependencies] defmt = "0.3" defmt-rtt = "0.4" [profile.dev] debug = 2 [profile.release] debug = 2 #[defmt::global_logger] struct Logger; unsafe impl defmt::Logger for Logger { fn acquire() {} unsafe fn flush() {} unsafe fn release() {} unsafe fn write(_bytes: &[u8]) {} } #[entry] fn main() -> ! { defmt::info!("程序启动!"); let (temp, hum) = sensor.measure().unwrap(); defmt::info!("温度 = {:.2}°C, 湿度 = {:.2}%", temp, hum); loop {} } 5.2 使用 probe-rs 调试 # 安装 probe-rs cargo install probe-rs --features cli # 烧录程序 probe-rs run --chip STM32F407VGTx target/thumbv7em-none-eabihf/debug/my-app # GDB 调试 probe-rs gdb --chip STM32F407VGTx 性能优化 6.1 启用 LTO 和代码优化 [profile.release] lto = true # 链接时优化 codegen-units = 1 # 单个编译单元 opt-level = "s" # 优化体积(或"z"极致优化) 6.2 使用 heapless 容器 use heapless::{Vec, String}; // 无需 alloc 的动态容器 let mut data: Vec<u8, 32> = Vec::new(); data.push(42).unwrap(); let mut text: String<64> = String::new(); text.push_str("Hello").unwrap(); 6.3 零成本抽象实践 // 使用泛型而非 trait object fn process<S: Sensor>(sensor: &S) { // ✅ 单态化,无运行时开销 let data = sensor.read(); } // 避免使用 Box<dyn Sensor> // ❌ 需要 heap 和虚表 常见问题 Q1: 编译错误"undefined reference" 原因:缺少 memory.x 或链接脚本配置错误。 ...

April 2, 2026 · 5 min · 👁️ 2 · Tech Snippets