目录
📝 摘要
一、背景介绍
1.1 为什么 Rust 适合嵌入式?
1.2 支持的硬件平台
二、基础概念
2.1 no_std 环境
2.2 裸机编程
2.3 内存模型
三、HAL 抽象层与外设驱动
3.1 HAL 原理
3.2 使用 embedded-hal
3.3 UART 通信
四、中断处理
4.1 中断服务例程(ISR)
4.2 NVIC 配置
五、实时操作系统集成
5.1 嵌入式实时操作系统对比
5.2 Embassy 示例
六、实战案例:智能温度计
七、性能与优化
7.1 编译大小优化
7.2 电源管理
八、调试与测试
8.1 GDB 调试
8.2 单元测试
九、总结与讨论
参考链接
📝 摘要
嵌入式开发是 Rust 最有前景的应用领域之一。通过 no_std 环境、裸机编程、HAL 抽象层等特性,Rust 能够安全地控制底层硬件,同时避免内存不安全问题。本文将系统讲解 Rust 嵌入式开发的基础概念、常见的微控制器平台支持、中断处理、外设驱动开发、实时操作系统集成,以及完整的项目案例。通过深入探讨 Rust 在嵌入式领域相对于 C 的优势与劣势,帮助读者入门和精通 Rust 嵌入式开发。
一、背景介绍
1.1 为什么 Rust 适合嵌入式?
Rust vs C:嵌入式编程对比:
Rust 嵌入式的优势:
- 内存安全 - 无缓冲区溢出、悬垂指针
- 零成本抽象 - 高级语言但不牺牲性能
-
无运行时 -
no_std支持极小嵌入式设备 - 并发安全 - 编译时检查避免竞态条件
1.2 支持的硬件平台
┌────────────────────────────────────────┐
│ ARM (Cortex-M / Cortex-A) │
│ ✓ STM32 (ST Microelectronics) │
│ ✓ ESP32 (Espressif) │
│ ✓ nRF52 (Nordic) │
│ ✓ ATSAM (Microchip) │
├────────────────────────────────────────┤
│ RISC-V │
│ ✓ SiFive HiFive │
│ ✓ GigaDevice GD32VF │
├────────────────────────────────────────┤
│ 其他 │
│ ✓ AVR (Arduino) │
│ ✓ MIPS │
│ ✓ x86_64 (开发板) │
└────────────────────────────────────────┘
二、基础概念
2.1 no_std 环境
标准库 vs no_std:
// ❌ 标准库项目
fn main() {
let v = vec![1, 2, 3]; // 动态分配
println!("Hello"); // 需要 I/O
}
// ✓ no_std 项目
#![no_std]
#![no_main]
use core::panic::PanicInfo;
// 必须定义 panic 处理函数
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
#[no_mangle]
pub extern "C" fn _start() -> ! {
// 嵌入式程序入口
loop {}
}
2.2 裸机编程
从零开始的嵌入式代码:
// 定义内存映射的外设寄存器
const GPIO_BASE: *mut u32 = 0x4002_1000 as *mut u32;
const GPIO_PIN_13: *mut u32 = (GPIO_BASE as usize + 0x20) as *mut u32;
unsafe fn set_pin_high() {
*GPIO_PIN_13 = 1;
}
unsafe fn set_pin_low() {
*GPIO_PIN_13 = 0;
}
#[no_mangle]
pub extern "C" fn main() -> ! {
unsafe {
set_pin_high();
}
loop {}
}
2.3 内存模型
嵌入式常见的内存布局:
0x0000_0000 ┌─────────────┐
│ Flash ROM │ (程序代码、常量)
├─────────────┤
0x0800_0000 │ │
├─────────────┤
0x2000_0000 │ SRAM │ (运行时数据)
├─────────────┤
0x2000_FFFF │ │
├─────────────┤
0x4000_0000 │ 外设寄存器 │ (GPIO, Timer 等)
├─────────────┤
0xFFFF_FFFF └─────────────┘
定义内存段:
// memory.x(嵌入式链接脚本)
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
.text : {
*(.text*)
} > FLASH
.data : {
*(.data*)
} > RAM AT > FLASH
}
三、HAL 抽象层与外设驱动
3.1 HAL 原理
3.2 使用 embedded-hal
[dependencies]
embedded-hal = "1.0"
cortex-m = "0.7"
stm32f4xx-hal = "0.14" # STM32F4 HAL
GPIO 示例:
use stm32f4xx_hal::prelude::*;
#[entry]
fn main() -> ! {
let dp = stm32f4::stm32f407::Peripherals::take().unwrap();
let r*** = dp.R***.constrain();
let clocks = r***.cfgr.freeze();
// 获取 GPIOD 端口
let gpiod = dp.GPIOD.split(&clocks);
// 配置 PD13 为输出
let mut led = gpiod.pd13.into_push_pull_output();
loop {
// 点亮 LED
led.set_high();
delay(1_000_000);
// 熄灭 LED
led.set_low();
delay(1_000_000);
}
}
3.3 UART 通信
use embedded_hal::serial::{Read, Write};
use stm32f4xx_hal::serial::{Serial, Config};
let serial = Serial::uart1(
dp.UART1,
(tx_pin, rx_pin),
Config::default().baudrate(115_200.bps()),
clocks,
).unwrap();
let (mut tx, mut rx) = serial.split();
// 发送数据
writeln!(tx, "Hello, embedded world!\r\n").ok();
// 接收数据
let mut buffer = [0u8; 64];
match rx.read(&mut buffer) {
Ok(n) => {
// 处理接收的 n 字节
},
Err(_) => {},
}
四、中断处理
4.1 中断服务例程(ISR)
use cortex_m_rt::interrupt;
use stm32f4::stm32f407;
// 定义中断处理函数
#[interrupt]
fn TIM2() {
// 处理 Timer2 中断
// 这个函数会在中断发生时自动被调用
}
// 更复杂的例子:带 Mutex 的共享状态
use cortex_m::interrupt::Mutex;
use core::cell::RefCell;
static COUNTER: Mutex<RefCell<u32>> = Mutex::new(RefCell::new(0));
#[interrupt]
fn TIM2() {
cortex_m::interrupt::free(|cs| {
let mut counter = COUNTER.borrow_mut(cs);
*counter += 1;
});
}
4.2 NVIC 配置
use cortex_m::peripheral::NVIC;
// 使能中断
NVIC::unmask(stm32f407::Interrupt::TIM2);
// 设置中断优先级
unsafe {
NVIC::set_priority(
stm32f407::Interrupt::TIM2,
8, // 优先级 (0-255, 值越大优先级越低)
);
}
五、实时操作系统集成
5.1 嵌入式实时操作系统对比
| 系统 | 开源 | Rust | 特点 |
|---|---|---|---|
| RTOS (FreeRTOS) | ✅ | ❌ | 广泛使用,C实现 |
| μROS (micro-ROS) | ✅ | ⚠️ | ROS 嵌入式版本 |
| Zephyr | ✅ | ⚠️ | 模块化,支持多架构 |
| RIOT | ✅ | ⚠️ | IoT 专用 |
| Embassy | ✅ | ✅ | 纯 Rust RTOS |
5.2 Embassy 示例
[dependencies]
embassy-executor = { version = "0.1", features = ["defmt"] }
embassy-time = "0.1"
embassy-stm32 = { version = "0.1", features = ["stm32f407vg"] }
use embassy_executor::Spawner;
use embassy_time::{Duration, Timer};
#[embassy::main]
async fn main(spawner: Spawner) {
// 生成多个异步任务
spawner.spawn(task_led()).unwrap();
spawner.spawn(task_serial()).unwrap();
}
#[embassy::task]
async fn task_led() {
loop {
// 异步 LED 闪烁
led.set_high();
Timer::after(Duration::from_millis(500)).await;
led.set_low();
Timer::after(Duration::from_millis(500)).await;
}
}
#[embassy::task]
async fn task_serial() {
loop {
// 异步串口通信
let byte = uart.read().await;
uart.write(byte).await;
}
}
六、实战案例:智能温度计
#![no_std]
#![no_main]
use embedded_hal::adc::OneShot;
use embedded_hal::blocking::delay::DelayMs;
use stm32f4xx_hal::prelude::*;
#[entry]
fn main() -> ! {
let dp = stm32f407::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
let r*** = dp.R***.constrain();
let clocks = r***.cfgr.freeze();
// 初始化延迟
let mut delay = cortex_m::delay::Delay::new(cp.SYST, &clocks);
// GPIO 配置
let gpioa = dp.GPIOA.split(&clocks);
let mut led = gpioa.pa5.into_push_pull_output();
// ADC 配置
let adc = dp.ADC1.adc(&clocks);
let mut pa0 = gpioa.pa0.into_analog();
// UART 配置
let gpiob = dp.GPIOB.split(&clocks);
let serial = Serial::uart1(
dp.UART1,
(gpiob.pb6, gpiob.pb7),
Config::default().baudrate(115_200.bps()),
clocks,
).unwrap();
let (mut tx, _rx) = serial.split();
loop {
// 读取 ADC 值
let adc_value: u16 = adc.read(&mut pa0).unwrap();
// 转换为温度(简化公式)
let temp = adc_value as f32 / 1241.0 * 100.0;
// 输出温度
writeln!(tx, "温度: {:.2}°C\r\n", temp).ok();
// 如果温度高于 30°C,点亮 LED
if temp > 30.0 {
led.set_high();
} else {
led.set_low();
}
delay.delay_ms(1000u32);
}
}
七、性能与优化
7.1 编译大小优化
[profile.release]
opt-level = "z" # 优化大小
lto = true # 链接时优化
codegen-units = 1
panic = "abort" # panic 直接终止
strip = true # 去除符号表
优化效果:
| 配置 | 大小 | 备注 |
|---|---|---|
| 默认 release | 512KB | - |
| opt-level=“z” | 280KB | -45% |
| + LTO | 240KB | -53% |
| + panic=“abort” | 235KB | -54% |
7.2 电源管理
// 配置低功耗模式
cortex_m::asm::wfi(); // Wait For Interrupt
// 或深度睡眠
dp.PWR.cr.modify(|_, w| {
w.pdds().set_bit()
});
// 唤醒
cortex_m::asm::sev(); // Send Event
八、调试与测试
8.1 GDB 调试
# 启动 GDB 服务器
openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg
# 在另一个终端
arm-none-eabi-gdb target/thumbv7em-none-eabihf/release/firmware
(gdb) target remote localhost:3333
(gdb) load
(gdb) break main
(gdb) continue
(gdb) step
8.2 单元测试
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_temperature_conversion() {
let adc_value = 620;
let temp = adc_value as f32 / 1241.0 * 100.0;
assert!((temp - 50.0).abs() < 1.0);
}
}
九、总结与讨论
核心要点:
✅ no_std 环境 - 无依赖,最小化开销
✅ HAL 抽象 - 统一接口,易于迁移
✅ 类型安全 - 编译时检查硬件配置错误
✅ 异步支持 - Embassy 提供实时性
✅ 性能优化 - 达到 C 级别的效率
Rust vs C 对比:
性能: Rust ≈ C (都可以达到最优)
安全性: Rust >> C (大大降低 bug)
开发效率: Rust > C (更高级的语法)
生态: C > Rust (工具和库更多)
学习曲线: C < Rust (Rust 更陡峭)
讨论问题:
- Rust 嵌入式开发目前的最大障碍是什么?
- Embassy 与传统 RTOS (FreeRTOS) 相比有何优劣?
- Rust 在物联网领域的发展前景如何?
- 如何有效降低嵌入式 Rust 项目的学习成本?
- 是否应该用 Rust 完全替代嵌入式 C 开发?
欢迎分享你的嵌入式 Rust 经验!🔌
参考链接
- Embedded Rust Book:https://rust-embedded.github.io/book/
- Embassy RTOS:https://embassy.dev/
- STM32F4xx HAL:https://docs.rs/stm32f4xx-hal/
- embedded-hal:https://github.***/rust-embedded/embedded-hal
- OpenOCD:http://openocd.org/