Rust从入门到精通之精通篇:30.嵌入式Rust开发

嵌入式 Rust 开发

在 Rust 精通篇中,我们将深入探索 Rust 在嵌入式系统开发中的应用。Rust 的零成本抽象、内存安全和无运行时特性使其成为嵌入式开发的理想选择。在本章中,我们将学习如何在资源受限的环境中使用 Rust,掌握嵌入式 Rust 开发的核心概念和最佳实践。

嵌入式开发基础

什么是嵌入式系统?

嵌入式系统是专为特定功能设计的计算机系统,通常具有以下特点:

  • 资源受限(内存、处理能力、能源等)
  • 实时性要求(需要在确定的时间内响应)
  • 直接与硬件交互
  • 长时间运行且可靠性要求高

为什么选择 Rust 进行嵌入式开发?

Rust 在嵌入式开发中具有以下优势:

  1. 内存安全:编译时检查防止内存错误,无需垃圾收集
  2. 零成本抽象:高级抽象不带来运行时开销
  3. 精确控制:可以直接控制内存布局和硬件访问
  4. 丰富的工具链:强大的构建系统和包管理器
  5. 跨平台支持:支持多种嵌入式目标平台

嵌入式 Rust 开发环境

工具链设置

首先,我们需要为目标平台安装 Rust 工具链:

# 安装 rustup(如果尚未安装)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 安装嵌入式开发所需的组件
rustup component add llvm-tools-preview
rustup target add thumbv7m-none-eabi  # ARM Cortex-M3
rustup target add thumbv7em-none-eabihf  # ARM Cortex-M4F 和 M7F(带硬件浮点)
rustup target add riscv32imac-unknown-none-elf  # RISC-V

# 安装辅助工具
cargo install cargo-binutils
cargo install cargo-embed  # 用于烧录和调试

项目设置

创建一个新的嵌入式 Rust 项目:

# 创建新项目
cargo new --bin my_embedded_project
cd my_embedded_project

编辑 Cargo.toml,添加必要的依赖:

[package]
name = "my_embedded_project"
version = "0.1.0"
edition = "2021"

# 不使用标准库
[dependencies]
cortex-m = "0.7.6"  # Cortex-M 处理器支持
cortex-m-rt = "0.7.2"  # Cortex-M 运行时
panic-halt = "0.2.0"  # 发生 panic 时停止执行

[profile.release]
opt-level = "s"  # 优化代码大小
lto = true  # 链接时优化
codegen-units = 1  # 更好的优化,但编译更慢
debug = true  # 在 release 构建中包含调试信息
panic = "abort"  # 发生 panic 时直接中止,减小二进制大小

创建 .cargo/config.toml 文件,配置目标平台和链接器:

[target.'cfg(all(target_arch = "arm", target_os = "none"))'] 
runner = "probe-run --chip STM32F103C8"  # 根据你的芯片调整

[build]
target = "thumbv7m-none-eabi"  # Cortex-M3

[unstable]
build-std = ["core", "compiler_builtins"]
build-std-features = ["compiler-builtins-mem"]

裸机编程基础

无标准库开发

嵌入式 Rust 通常在 no_std 环境中运行,这意味着不能使用标准库:

#![no_std]  // 不使用标准库
#![no_main]  // 不使用标准的 main 函数入口

use cortex_m_rt::entry;
use panic_halt as _;

#[entry]
fn main() -> ! {
    // 程序入口点
    loop {
        // 无限循环,防止程序退出
    }
}

内存管理

在嵌入式系统中,内存管理尤为重要:

// 静态分配内存,避免堆分配
static mut BUFFER: [u8; 1024] = [0; 1024];

#[entry]
fn main() -> ! {
    // 安全地访问静态可变变量
    let buffer = unsafe { &mut BUFFER };
    
    // 使用缓冲区
    buffer[0] = 42;
    
    loop {}
}

中断处理

处理硬件中断是嵌入式编程的核心部分:

use cortex_m::peripheral::NVIC;
use cortex_m_rt::exception;

#[entry]
fn main() -> ! {
    // 获取外设访问
    let peripherals = cortex_m::Peripherals::take().unwrap();
    let mut nvic = peripherals.NVIC;
    
    // 配置中断
    unsafe {
        // 启用 EXTI0 中断
        nvic.enable(Interrupt::EXTI0);
    }
    
    loop {}
}

#[interrupt]
fn EXTI0() {
    // 中断处理代码
    // 注意:中断处理函数不能返回 !
}

外设访问

寄存器抽象

使用 svd2rust 生成的外设访问代码:

use stm32f1xx_hal::{pac, prelude::*};

#[entry]
fn main() -> ! {
    // 获取外设访问
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();
    
    // 获取特定外设
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    
    // 配置时钟
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    
    // 获取 GPIO 端口
    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
    
    // 配置 PC13 为推挽输出(许多 STM32 开发板上的 LED)
    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
    
    // 获取延时提供者
    let mut delay = cp.SYST.delay(&clocks);
    
    loop {
        // 点亮 LED
        led.set_low().ok();
        delay.delay_ms(500_u16);
        
        // 熄灭 LED
        led.set_high().ok();
        delay.delay_ms(500_u16);
    }
}

使用 HAL 库

硬件抽象层(HAL)提供了更高级的外设访问接口:

use stm32f1xx_hal::{delay::Delay, pac, prelude::*};

#[entry]
fn main() -> ! {
    // 获取外设访问
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();
    
    // 获取特定外设
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    
    // 配置时钟
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    
    // 获取 GPIO 端口
    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
    
    // 配置 PC13 为推挽输出(许多 STM32 开发板上的 LED)
    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
    
    // 创建延时对象
    let mut delay = Delay::new(cp.SYST, clocks);
    
    loop {
        // 点亮 LED
        led.set_low().ok();
        delay.delay_ms(500_u16);
        
        // 熄灭 LED
        led.set_high().ok();
        delay.delay_ms(500_u16);
    }
}

串口通信

实现串口通信是嵌入式系统的常见需求:

use core::fmt::Write;
use stm32f1xx_hal::{pac, prelude::*, serial::{Config, Serial}};

#[entry]
fn main() -> ! {
    // 获取外设访问
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();
    
    // 获取特定外设
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    
    // 配置时钟
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    
    // 获取 GPIO 端口
    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
    
    // 配置 USART 引脚
    let tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
    let rx = gpioa.pa10.into_floating_input(&mut gpioa.crh);
    
    // 配置串口
    let mut serial = Serial::usart1(
        dp.USART1,
        (tx, rx),
        &mut rcc.apb2,
        Config::default().baudrate(9600.bps()),
        clocks,
    );
    
    // 创建延时对象
    let mut delay = cp.SYST.delay(&clocks);
    
    loop {
        // 发送消息
        writeln!(serial.tx, "Hello, Rust embedded world!").ok();
        delay.delay_ms(1000_u16);
    }
}

实时操作系统 (RTOS)

RTIC 框架

RTIC (Real-Time Interrupt-driven Concurrency) 是 Rust 的实时并发框架:

#![no_std]
#![no_main]

use panic_halt as _;
use rtic::app;

#[app(device = stm32f1xx_hal::pac, peripherals = true)]
const APP: () = {
    struct Resources {
        led: stm32f1xx_hal::gpio::gpioc::PC13<stm32f1xx_hal::gpio::Output<stm32f1xx_hal::gpio::PushPull>>,
        state: bool,
    }

    #[init]
    fn init(ctx: init::Context) -> init::LateResources {
        // 设备特定的初始化
        let dp = ctx.device;
        
        // 配置时钟和 GPIO
        let mut rcc = dp.RCC.constrain();
        let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
        
        // 配置 LED
        let led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
        
        // 配置系统定时器以每 1 秒触发一次中断
        let mut syst = ctx.core.SYST;
        syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core);
        syst.set_reload(8_000_000); // 假设 8MHz 时钟
        syst.enable_counter();
        syst.enable_interrupt();
        
        init::LateResources {
            led,
            state: false,
        }
    }

    #[idle]
    fn idle(_: idle::Context) -> ! {
        loop {
            // 等待中断
            cortex_m::asm::wfi();
        }
    }

    #[task(binds = SYS_TICK, resources = [led, state])]
    fn sys_tick(ctx: sys_tick::Context) {
        // 切换 LED 状态
        if *ctx.resources.state {
            ctx.resources.led.set_high().ok();
        } else {
            ctx.resources.led.set_low().ok();
        }
        
        // 更新状态
        *ctx.resources.state = !*ctx.resources.state;
    }
};

使用 Embassy

Embassy 是一个基于异步 Rust 的嵌入式框架:

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use embassy_executor::Spawner;
use embassy_stm32::gpio::{Level, Output, Speed};
use embassy_stm32::time::Hertz;
use embassy_stm32::{interrupt, Config};
use embassy_time::{Duration, Timer};
use panic_halt as _;

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
    // 配置设备
    let config = Config::default();
    let p = embassy_stm32::init(config);
    
    // 创建 LED 输出
    let mut led = Output::new(p.PC13, Level::High, Speed::Low);
    
    loop {
        // 切换 LED 状态
        led.toggle();
        
        // 等待 500 毫秒
        Timer::after(Duration::from_millis(500)).await;
    }
}

嵌入式调试技术

使用 probe-rs

probe-rs 是一个现代化的嵌入式调试工具:

# 安装 probe-rs 工具
cargo install probe-rs-cli

# 烧录程序
probe-rs-cli download --chip STM32F103C8 target/thumbv7m-none-eabi/release/my_embedded_project

# 运行并调试程序
probe-rs-cli run --chip STM32F103C8 target/thumbv7m-none-eabi/release/my_embedded_project

日志和跟踪

使用 RTT (Real-Time Transfer) 进行日志记录:

use panic_rtt_target as _;
use rtt_target::{rprintln, rtt_init_print};

#[entry]
fn main() -> ! {
    // 初始化 RTT
    rtt_init_print!();
    
    rprintln!("程序启动");
    
    let mut counter = 0;
    loop {
        counter += 1;
        rprintln!("计数: {}", counter);
        
        // 延时
        for _ in 0..1_000_000 {
            cortex_m::asm::nop();
        }
    }
}

使用 defmt

defmt 是一个专为嵌入式系统设计的日志框架:

use defmt_rtt as _;
use panic_probe as _;

#[entry]
fn main() -> ! {
    // 程序启动日志
    defmt::info!("程序启动");
    
    let mut counter = 0;
    loop {
        counter += 1;
        defmt::debug!("计数: {}", counter);
        
        // 延时
        for _ in 0..1_000_000 {
            cortex_m::asm::nop();
        }
    }
}

实际应用案例

温度传感器读取

使用 I2C 读取温度传感器数据:

use stm32f1xx_hal::{i2c::{BlockingI2c, DutyCycle, Mode}, pac, prelude::*};

// 传感器地址(示例)
const SENSOR_ADDR: u8 = 0x48;

#[entry]
fn main() -> ! {
    // 获取外设访问
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();
    
    // 配置时钟和 GPIO
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    
    // 配置 I2C 引脚
    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
    let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
    
    let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl);
    let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl);
    
    // 配置 I2C
    let i2c = BlockingI2c::i2c1(
        dp.I2C1,
        (scl, sda),
        &mut afio.mapr,
        Mode::Fast {
            frequency: 400_000.hz(),
            duty_cycle: DutyCycle::Ratio2to1,
        },
        clocks,
        &mut rcc.apb1,
        1000,
        10,
        1000,
        1000,
    );
    
    // 创建延时对象
    let mut delay = cp.SYST.delay(&clocks);
    
    // 读取寄存器的缓冲区
    let mut buffer = [0u8; 2];
    
    loop {
        // 读取温度寄存器
        match i2c.read(SENSOR_ADDR, &mut buffer) {
            Ok(_) => {
                // 处理温度数据(根据传感器规格)
                let temp_raw = ((buffer[0] as u16) << 8) | buffer[1] as u16;
                let temp_c = temp_raw as f32 * 0.0625; // 示例转换,根据实际传感器调整
                
                // 在实际应用中,可以将温度发送到串口或显示在屏幕上
            },
            Err(_) => {
                // 处理错误
            }
        }
        
        // 每秒读取一次
        delay.delay_ms(1000_u16);
    }
}

简单的数字时钟

使用 OLED 显示屏实现数字时钟:

use embedded_graphics::{fonts::{Font6x8, Text}, pixelcolor::BinaryColor, prelude::*, style::TextStyle};
use embedded_hal::digital::v2::OutputPin;
use rtcc::Rtcc;
use ssd1306::{prelude::*, Builder, I2CDIBuilder};
use stm32f1xx_hal::{i2c::{BlockingI2c, DutyCycle, Mode}, pac, prelude::*};

#[entry]
fn main() -> ! {
    // 获取外设访问
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();
    
    // 配置时钟和 GPIO
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    
    // 配置 I2C 引脚
    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
    let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
    
    let scl = gpiob.pb6.into_alternate_open_drain(&mut gpiob.crl);
    let sda = gpiob.pb7.into_alternate_open_drain(&mut gpiob.crl);
    
    // 配置 I2C
    let i2c = BlockingI2c::i2c1(
        dp.I2C1,
        (scl, sda),
        &mut afio.mapr,
        Mode::Fast {
            frequency: 400_000.hz(),
            duty_cycle: DutyCycle::Ratio2to1,
        },
        clocks,
        &mut rcc.apb1,
        1000,
        10,
        1000,
        1000,
    );
    
    // 创建 OLED 显示器实例
    let mut display = I2CDIBuilder::new().init(i2c);
    display.init().unwrap();
    display.clear().unwrap();
    
    // 创建延时对象
    let mut delay = cp.SYST.delay(&clocks);
    
    // 模拟时钟(在实际应用中,可以使用 RTC 芯片)
    let mut hours = 12;
    let mut minutes = 0;
    let mut seconds = 0;
    
    loop {
        // 清除显示
        display.clear().unwrap();
        
        // 显示时间
        let time_str = format!("{:02}:{:02}:{:02}", hours, minutes, seconds);
        Text::new(&time_str, Point::new(10, 20))
            .into_styled(TextStyle::new(Font6x8, BinaryColor::On))
            .draw(&mut display)
            .unwrap();
        
        // 更新显示
        display.flush().unwrap();
        
        // 更新时间
        seconds += 1;
        if seconds >= 60 {
            seconds = 0;
            minutes += 1;
            if minutes >= 60 {
                minutes = 0;
                hours += 1;
                if hours >= 24 {
                    hours = 0;
                }
            }
        }
        
        // 等待一秒
        delay.delay_ms(1000_u16);
    }
}

嵌入式 Rust 最佳实践

资源管理

  1. 静态分配:尽量使用静态分配而非动态分配
  2. 避免递归:递归可能导致栈溢出
  3. 限制栈使用:注意函数调用深度和局部变量大小

中断安全

  1. 最小化关键区域:中断禁用时间应尽可能短
  2. 使用原子操作:在可能的情况下使用原子操作而非锁
  3. 避免死锁:小心设计中断处理程序,避免死锁情况

电源管理

use stm32f1xx_hal::{pac, prelude::*};

#[entry]
fn main() -> ! {
    // 获取外设访问
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();
    
    // 配置时钟和 GPIO
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr);
    
    // 创建延时对象
    let mut delay = cp.SYST.delay(&clocks);
    
    loop {
        // 执行任务
        perform_task();
        
        // 进入低功耗模式
        cortex_m::asm::wfi(); // 等待中断
        
        // 或使用更深的睡眠模式
        // unsafe { dp.PWR.cr.modify(|_, w| w.pdds().set_bit().lpds().set_bit()); }
        // cortex_m::asm::wfi();
    }
}

fn perform_task() {
    // 执行必要的任务
}

代码组织

  1. 模块化设计:将代码分解为逻辑模块
  2. 硬件抽象层:创建硬件抽象层,使代码更易于移植
  3. 配置分离:将硬件配置与业务逻辑分离
// 硬件抽象模块
mod hal {
    pub struct Led {
        // 硬件特定实现
    }
    
    impl Led {
        pub fn new() -> Self {
            // 初始化硬件
            Led { /* ... */ }
        }
        
        pub fn on(&mut self) {
            // 硬件特定操作
        }
        
        pub fn off(&mut self) {
            // 硬件特定操作
        }
    }
    
    // 其他硬件抽象...
}

// 应用逻辑模块
mod app {
    use super::hal::Led;
    
    pub struct Blinker {
        led: Led,
        state: bool,
    }
    
    impl Blinker {
        pub fn new(led: Led) -> Self {
            Blinker {
                led,
                state: false,
            }
        }
        
        pub fn toggle(&mut self) {
            self.state = !self.state;
            if self.state {
                self.led.on();
            } else {
                self.led.off();
            }
        }
    }
}

练习

  1. 创建一个基本的 LED 闪烁程序,使用不同的闪烁模式(例如 SOS 信号)
  2. 实现一个使用按钮控制 LED 的程序,包括去抖动处理
  3. 使用 I2C 或 SPI 与传感器通信,读取环境数据
  4. 创建一个简单的数字时钟,使用 OLED 显示屏显示时间
  5. 实现一个低功耗应用,在不活动时进入睡眠模式

通过本章的学习,你应该能够理解嵌入式 Rust 开发的核心概念,并能够开始在各种嵌入式平台上使用 Rust 进行开发。嵌入式 Rust 结合了 Rust 的安全性和性能,为嵌入式系统开发提供了强大

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aimmon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值