跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Rust

Rust 嵌入式开发实战:从 ARM 裸机编程到 RTOS 应用

Rust 在嵌入式领域的开发实战,涵盖 ARM 裸机编程与 RTOS 应用。内容包含开发环境搭建(交叉编译工具链、OpenOCD、GDB)、裸机项目创建(Cargo.toml、memory.x 配置)、GPIO 操作、串口通信及中断处理。重点讲解 RTIC 框架实现多任务调度、资源共享与中断管理。通过 STM32F411RE 开发板案例,演示温湿度传感器读取等实战项目。涉及内存管理、栈溢出处理、寄存器初始化及硬件调试优化方法。

DevOpsTeam发布于 2026/3/30更新于 2026/9/959 浏览
Rust 嵌入式开发实战:从 ARM 裸机编程到 RTOS 应用

Rust 嵌入式开发实战:从 ARM 裸机编程到 RTOS 应用

在这里插入图片描述

一、学习目标与重点

1.1 学习目标
  1. 理解嵌入式开发基础:深入掌握嵌入式系统的定义、特点、架构(ARM、RISC-V),对比 Rust 与传统嵌入式开发语言(C/C++)的优势
  2. 搭建 Rust 嵌入式开发环境:安装交叉编译工具链(arm-none-eabi、riscv64-unknown-elf)、调试工具(OpenOCD、GDB),配置 VS Code/CLion 开发环境
  3. 掌握 Rust 裸机编程:使用 cortex-m、cortex-m-rt 库进行 ARM 裸机开发,实现 GPIO 操作、串口通信、中断处理
  4. 学习 RTOS 开发:使用 RTIC(Real-Time Interrupt-driven Concurrency)实现多任务编程,理解任务调度、资源共享、中断管理
  5. 实战嵌入式项目:结合 STM32F4xx 系列开发板、Raspberry Pi Pico,实现 LED 闪烁、温度传感器数据读取、I2C/SPI 通信、电机控制
  6. 优化与调试:学习 Rust 嵌入式代码的优化方法,使用 GDB/OpenOCD 进行硬件调试,解决内存泄漏、栈溢出等问题
1.2 学习重点

💡 三大核心难点:

  1. 内存管理:理解嵌入式系统的内存架构(Flash、RAM、寄存器),使用 core、alloc 库管理内存,避免内存泄漏
  2. 中断处理:掌握 ARM Cortex-M 的中断向量表、中断优先级,使用 cortex-m 库实现中断函数和临界区保护
  3. RTOS 调度:深入理解 RTIC 的任务调度机制(优先级调度、时间片轮询),解决资源共享时的互斥问题

⚠️ 三大高频错误点:

  1. 栈溢出:未正确设置栈大小,导致程序崩溃
  2. 寄存器未初始化:未正确初始化硬件寄存器,导致功能无法正常实现
  3. 时序问题:在 I2C/SPI 通信时,未正确处理时序,导致数据传输错误

二、嵌入式开发基础

2.1 嵌入式系统的定义与特点

嵌入式系统是一种以应用为中心、以计算机技术为基础、软件硬件可裁剪、适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。嵌入式系统的核心特点是:

  1. 资源受限:内存(RAM)、存储空间(Flash)、处理能力(CPU)有限
  2. 实时性要求高:对响应时间有严格要求(硬实时、软实时)
  3. 低功耗:通常使用电池供电,需要优化功耗
  4. 可靠性高:需要长时间稳定运行,无人工干预
  5. 专用性强:针对特定应用场景设计
2.2 嵌入式系统架构

嵌入式系统的架构通常分为以下几种:

  1. 冯·诺依曼架构:程序和数据存放在同一内存空间,如 ARM Cortex-M0
  2. 哈佛架构:程序和数据存放在不同的内存空间,如 ARM Cortex-M4、RISC-V
2.3 Rust 在嵌入式开发中的优势

Rust 作为一门系统编程语言,非常适合嵌入式开发,其优势包括:

  1. 内存安全:通过所有权、借用、生命周期机制,在编译时检查内存访问错误,避免悬挂指针、内存泄漏
  2. 无垃圾回收:不需要垃圾回收器,减少内存开销和不确定性
  3. 高性能:执行速度接近 C/C++,适合实时性要求高的应用
  4. 类型安全:强类型系统,编译时检查类型错误
  5. 现代语言特性:支持泛型、模式匹配、异步编程等,提高开发效率

三、开发环境搭建

3.1 安装 Rust 交叉编译工具链

使用 rustup 安装 ARM 和 RISC-V 的交叉编译工具链:

# 安装 ARM Cortex-M0/M3/M4 的交叉编译工具链
rustup target add thumbv6m-none-eabi
rustup target add thumbv7m-none-eabi
rustup target add thumbv7em-none-eabi
rustup target add thumbv8m.base-none-eabi
rustup target add thumbv8m.main-none-eabi
rustup target add thumbv8m.main-none-eabihf

# 安装 RISC-V 的交叉编译工具链
rustup target add riscv32imc-unknown-none-elf
rustup target add riscv64gc-unknown-none-elf
3.2 安装调试工具
3.2.1 安装 OpenOCD

OpenOCD 是开源的调试器和编程器,支持多种调试接口(JTAG、SWD)。

⌨️ Ubuntu/Debian 安装 OpenOCD:

sudo apt-get install openocd

⌨️ Windows 安装 OpenOCD: 下载 OpenOCD 的 Windows 版本,解压后添加到系统 PATH 中。

3.2.2 安装 GDB

GDB 是GNU 调试器,支持 ARM 和 RISC-V 架构的调试。

⌨️ Ubuntu/Debian 安装 ARM GDB:

sudo apt-get install gdb-multiarch

⌨️ Windows 安装 ARM GDB: 下载 ARM GDB 的 Windows 版本,解压后添加到系统 PATH 中。

3.3 配置 VS Code 开发环境

安装以下 VS Code 扩展:

  1. Rust Analyzer:Rust 语言支持
  2. Cortex-Debug:ARM Cortex-M 调试支持
  3. OpenOCD:OpenOCD 调试支持

四、Rust 裸机编程基础

4.1 创建裸机项目

使用 cargo generate 创建一个新的 Rust 裸机项目:

# 安装 cargo generate
cargo install cargo-generate

# 使用 cortex-m-quickstart 模板创建项目
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart --name stm32f411re-demo
4.2 项目配置
4.2.1 Cargo.toml 配置

在 Cargo.toml 中添加依赖:

[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "0.2"

[dependencies.stm32f4xx-hal]
version = "0.18"
features = ["stm32f411", "rt"]
4.2.2 memory.x 配置

在 memory.x 文件中配置 Flash 和 RAM 的地址和大小:

/* Linker script for STM32F411RE Nucleo */
MEMORY {
 FLASH : ORIGIN = 0x08000000, LENGTH = 512K
 RAM : ORIGIN = 0x20000000, LENGTH = 128K
}
4.3 编写裸机代码
4.3.1 LED 闪烁

在 src/main.rs 文件中编写 LED 闪烁的代码:

#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
use stm32f4xx_hal::gpio::*;
use stm32f4xx_hal::pac;
use stm32f4xx_hal::prelude::*;
use stm32f4xx_hal::timer::*;

#[entry]
fn main() -> ! {
    // 获取硬件抽象层(HAL)
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    // 配置系统时钟
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.MHz()).freeze();

    // 配置 GPIO
    let gpioc = dp.GPIOC.split();
    let mut led = gpioc.pc13.into_push_pull_output();

    // 配置定时器
    let mut timer = dp.TIM2.timer(&clocks).start_ticker();

    // 配置系统定时器(SysTick)
    let mut systick = cp.SYST.counter_us(&clocks);

    // LED 闪烁
    loop {
        led.toggle();
        systick.delay(500_000); // 延迟 500ms
    }
}
4.3.2 串口通信

在 src/main.rs 文件中添加串口通信的代码:

use stm32f4xx_hal::serial::*;

#[entry]
fn main() -> ! {
    // 获取硬件抽象层(HAL)
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    // 配置系统时钟
    let rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.sysclk(84.MHz()).freeze();

    // 配置 GPIO
    let gpioa = dp.GPIOA.split();
    let tx = gpioa.pa2.into_alternate();
    let rx = gpioa.pa3.into_alternate();

    // 配置串口
    let mut serial = dp.USART2.serial((tx, rx), 9600.Bd(), &clocks).unwrap();
    serial.listen(Event::Rxne);

    // 发送数据
    serial.write_str("Hello, Rust Embedded!\n").unwrap();

    // 接收数据
    loop {
        if let Ok(byte) = nb::block!(serial.read()) {
            serial.write(byte).unwrap();
        }
    }
}
4.4 编译与烧录
4.4.1 编译

使用 cargo build 命令编译项目:

# 编译为 ARM Cortex-M4 架构
cargo build --target thumbv7em-none-eabihf --release
4.4.2 烧录

使用 OpenOCD 烧录程序:

# 连接开发板
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg

# 在另一个终端中使用 GDB 烧录
gdb-multiarch target/thumbv7em-none-eabihf/release/stm32f411re-demo
(gdb) target remote :3333
(gdb) monitor reset halt
(gdb) load
(gdb) monitor reset run

五、RTOS 开发

5.1 RTIC 简介

RTIC(Real-Time Interrupt-driven Concurrency)是Rust 社区开发的实时操作系统框架,它基于中断驱动的并发模型,支持:

  1. 任务调度:优先级调度、时间片轮询
  2. 资源共享:通过 resource 宏实现互斥访问
  3. 中断管理:通过 interrupt 宏实现中断函数
  4. 通信机制:通过 channel 宏实现任务间通信
5.2 创建 RTIC 项目

使用 cargo generate 创建一个新的 RTIC 项目:

cargo generate --git https://github.com/rtic-rs/cortex-m-quickstart --name rtic-demo
5.3 编写 RTIC 代码
5.3.1 LED 闪烁与串口通信

在 src/main.rs 文件中编写 RTIC 代码:

#![no_std]
#![no_main]
use panic_halt as _;
use rtic::app;
use stm32f4xx_hal::gpio::*;
use stm32f4xx_hal::pac;
use stm32f4xx_hal::prelude::*;
use stm32f4xx_hal::serial::*;
use stm32f4xx_hal::timer::*;

#[app(device = stm32f4xx_hal::pac, peripherals = true)]
const APP: () = {
    struct Resources {
        led: gpioc::PC13<Output<PushPull>>,
        timer: Timer<pac::TIM2>,
        serial: Serial<pac::USART2, (gpioa::PA2<Alternate<AF7>>, gpioa::PA3<Alternate<AF7>>), 9600>,
    }

    #[init]
    fn init(cx: init::Context) -> init::LateResources {
        // 获取硬件抽象层(HAL)
        let dp = cx.device;
        let cp = cx.core;

        // 配置系统时钟
        let rcc = dp.RCC.constrain();
        let clocks = rcc.cfgr.sysclk(84.MHz()).freeze();

        // 配置 GPIO
        let gpioc = dp.GPIOC.split();
        let led = gpioc.pc13.into_push_pull_output();

        let gpioa = dp.GPIOA.split();
        let tx = gpioa.pa2.into_alternate();
        let rx = gpioa.pa3.into_alternate();

        // 配置串口
        let serial = dp.USART2.serial((tx, rx), 9600.Bd(), &clocks).unwrap();
        serial.listen(Event::Rxne);

        // 配置定时器
        let timer = dp.TIM2.timer(&clocks).start_ticker();

        // 发送初始化信息
        serial.write_str("RTIC Demo Initialized!\n").unwrap();

        init::LateResources { led, timer, serial }
    }

    #[task(binds = TIM2, resources = [led, timer])]
    fn timer_interrupt(cx: timer_interrupt::Context) {
        // 清除定时器中断标志
        cx.resources.timer.clear_interrupt(TimerInterrupt::Update);
        // 切换 LED 状态
        cx.resources.led.toggle();
    }

    #[task(binds = USART2, resources = [serial])]
    fn serial_interrupt(cx: serial_interrupt::Context) {
        // 清除串口中断标志
        cx.resources.serial.clear_interrupt(Event::Rxne);
        // 接收数据
        if let Ok(byte) = nb::block!(cx.resources.serial.read()) {
            // 发送数据
            cx.resources.serial.write(byte).unwrap();
        }
    }
};

六、真实案例应用

6.1 案例 1:STM32F411RE Nucleo 板——温湿度传感器数据读取

💡 场景分析:需要使用 STM32F411RE Nucleo 板连接 DHT11 温湿度传感器,读取温湿度数据,并通过串口发送到电脑。

6.1.1 硬件连接
STM32F411REDHT11
5VVCC
GNDGND
PB12DATA
6.1.2 编写代码

在 src/main.rs 文件中添加温湿度传感器数据读取的代码:

#![no_std]
#![no_main]
use panic_halt as _;
use rtic::app;
use stm32f4xx_hal::gpio::*;
use stm32f4xx_hal::pac;
use stm32f4xx_hal::prelude::*;
use stm32f4xx_hal::serial::*;
use stm32f4xx_hal::timer::*;

// 定义 DHT11 传感器类型
struct DHT11 {
    pin: gpiob::PB12<Input<Floating>>,
}

impl DHT11 {
    // 初始化 DHT11 传感器
    fn new(pin: gpiob::PB12<Input<Floating>>) -> Self {
        DHT11 { pin }
    }

    // 读取温湿度数据
    fn read(&mut self) -> Option<(u8, u8)> {
        let mut data: [u8; 5] = [0; 5];

        // 发送启动信号
        let mut pin = self.pin.into_push_pull_output();
        pin.set_low();
        cortex_m::asm::delay(18000); // 延迟 18ms
        pin.set_high();
        cortex_m::asm::delay(30); // 延迟 30us

        // 等待 DHT11 响应
        let pin = pin.into_floating_input();
        while pin.is_high() {}
        while pin.is_low() {}
        while pin.is_high() {}

        // 读取数据
        for i in 0..5 {
            let mut byte = 0;
            for j in 0..8 {
                while pin.is_low() {}
                cortex_m::asm::delay(40); // 延迟 40us
                if pin.is_high() {
                    byte |= 1 << (7 - j);
                }
                while pin.is_high() {}
            }
            data[i] = byte;
        }

        // 校验数据
        let checksum = data[0] + data[1] + data[2] + data[3];
        if data[4] == checksum {
            Some((data[0], data[2]))
        } else {
            None
        }
    }
}

#[app(device = stm32f4xx_hal::pac, peripherals = true)]
const APP: () = {
    struct Resources {
        dht11: DHT11,
        serial: Serial<pac::USART2, (gpioa::PA2<Alternate<AF7>>, gpioa::PA3<Alternate<AF7>>), 9600>,
        timer: Timer<pac::TIM3>,
    }

    #[init]
    fn init(cx: init::Context) -> init::LateResources {
        // 获取硬件抽象层(HAL)
        let dp = cx.device;
        let cp = cx.core;

        // 配置系统时钟
        let rcc = dp.RCC.constrain();
        let clocks = rcc.cfgr.sysclk(84.MHz()).freeze();

        // 配置 GPIO
        let gpiob = dp.GPIOB.split();
        let dht11 = DHT11::new(gpiob.pb12.into_floating_input());

        let gpioa = dp.GPIOA.split();
        let tx = gpioa.pa2.into_alternate();
        let rx = gpioa.pa3.into_alternate();

        // 配置串口
        let serial = dp.USART2.serial((tx, rx), 9600.Bd(), &clocks).unwrap();
        serial.listen(Event::Rxne);

        // 配置定时器
        let timer = dp.TIM3.timer(&clocks).start_ticker();

        init::LateResources { dht11, serial, timer }
    }

    #[task(binds = TIM3, resources = [dht11, serial])]
    fn timer_interrupt(cx: timer_interrupt::Context) {
        // 清除定时器中断标志
        cx.resources.timer.clear_interrupt(TimerInterrupt::Update);

        // 读取温湿度数据
        if let Some((temperature, humidity)) = cx.resources.dht11.read() {
            // 发送温湿度数据
            let message = format!("温度: {}°C, 湿度: {}%\n", temperature, humidity);
            cx.resources.serial.write_str(&message).unwrap();
        } else {
            cx.resources.serial.write_str("读取温湿度数据失败\n").unwrap();
        }
    }

    #[task(binds = USART2, resources = [serial])]
    fn serial_interrupt(cx: serial_interrupt::Context) {
        // 清除串口中断标志
        cx.resources.serial.clear_interrupt(Event::Rxne);
        // 接收数据
        if let Ok(byte) = nb::block!(cx.resources.serial.read()) {
            // 发送数据
            cx.resources.serial.write(byte).unwrap();
        }
    }
};

七、常见问题与解决方案

7.1 栈溢出

问题现象:程序崩溃,调试器显示'HardFault'。

解决方案:

  1. 增大栈大小:在 memory.x 文件中修改 RAM 的大小或调整链接器脚本
  2. 减少栈使用:避免在中断函数中使用大量局部变量,避免递归调用
  3. 使用堆分配:对于大的数据结构,使用 alloc 库进行堆分配
7.2 寄存器未初始化

问题现象:硬件功能无法正常实现。

解决方案:

  1. 确保所有相关的寄存器都已正确初始化
  2. 查看芯片的参考手册,了解寄存器的功能和设置方法
  3. 使用 HAL 库提供的函数,避免直接操作寄存器
7.3 时序问题

问题现象:I2C/SPI 通信时,数据传输错误。

解决方案:

  1. 调整通信频率:降低 I2C/SPI 的通信频率
  2. 检查时钟配置:确保系统时钟和外设时钟配置正确
  3. 使用硬件中断:使用硬件中断代替软件延时,提高时序精度

八、总结与展望

8.1 总结

✅ 理解了嵌入式开发基础:深入掌握了嵌入式系统的定义、特点、架构,对比了 Rust 与传统嵌入式开发语言的优势 ✅ 搭建了 Rust 嵌入式开发环境:安装了交叉编译工具链、调试工具,配置了 VS Code 开发环境 ✅ 掌握了 Rust 裸机编程:使用 cortex-m、cortex-m-rt 库进行 ARM 裸机开发,实现了 GPIO 操作、串口通信、中断处理 ✅ 学习了 RTOS 开发:使用 RTIC 实现了多任务编程,理解了任务调度、资源共享、中断管理 ✅ 实战了嵌入式项目:结合 STM32F4xx 系列开发板,实现了 LED 闪烁、温湿度传感器数据读取、串口通信 ✅ 优化与调试:学习了 Rust 嵌入式代码的优化方法,使用 GDB/OpenOCD 进行硬件调试

目录

  1. Rust 嵌入式开发实战:从 ARM 裸机编程到 RTOS 应用
  2. 一、学习目标与重点
  3. 1.1 学习目标
  4. 1.2 学习重点
  5. 二、嵌入式开发基础
  6. 2.1 嵌入式系统的定义与特点
  7. 2.2 嵌入式系统架构
  8. 2.3 Rust 在嵌入式开发中的优势
  9. 三、开发环境搭建
  10. 3.1 安装 Rust 交叉编译工具链
  11. 安装 ARM Cortex-M0/M3/M4 的交叉编译工具链
  12. 安装 RISC-V 的交叉编译工具链
  13. 3.2 安装调试工具
  14. 3.2.1 安装 OpenOCD
  15. 3.2.2 安装 GDB
  16. 3.3 配置 VS Code 开发环境
  17. 四、Rust 裸机编程基础
  18. 4.1 创建裸机项目
  19. 安装 cargo generate
  20. 使用 cortex-m-quickstart 模板创建项目
  21. 4.2 项目配置
  22. 4.2.1 Cargo.toml 配置
  23. 4.2.2 memory.x 配置
  24. 4.3 编写裸机代码
  25. 4.3.1 LED 闪烁
  26. 4.3.2 串口通信
  27. 4.4 编译与烧录
  28. 4.4.1 编译
  29. 编译为 ARM Cortex-M4 架构
  30. 4.4.2 烧录
  31. 连接开发板
  32. 在另一个终端中使用 GDB 烧录
  33. 五、RTOS 开发
  34. 5.1 RTIC 简介
  35. 5.2 创建 RTIC 项目
  36. 5.3 编写 RTIC 代码
  37. 5.3.1 LED 闪烁与串口通信
  38. 六、真实案例应用
  39. 6.1 案例 1:STM32F411RE Nucleo 板——温湿度传感器数据读取
  40. 6.1.1 硬件连接
  41. 6.1.2 编写代码
  42. 七、常见问题与解决方案
  43. 7.1 栈溢出
  44. 7.2 寄存器未初始化
  45. 7.3 时序问题
  46. 八、总结与展望
  47. 8.1 总结

更多推荐文章

查看全部
  • FauxPilot:开源 GitHub Copilot 替代方案本地部署指南
  • C++ STL 无序容器 unordered_set 与 unordered_map 模拟实现
  • Java 时间类:JDK8 全新时间 API 详解
  • 基于 AKSHARE 的 AI 金融数据爬取与分析实践
  • 本地电脑部署个人 AI 大模型全攻略
  • 计算机科学之父艾伦·图灵的生平与贡献
  • Go Web 开发核心理论与实战基础
  • 双指针滑动窗口:4 道经典题的思路拆解
  • Kylin/Linux 服务器健康一键巡检脚本
  • NewBie-image-Exp0.1 与 Stable Diffusion 模型差异对比
  • C++ 二叉搜索树基础实现:插入、查找、删除与遍历
  • SketchUp STL 插件使用指南:从设计到 3D 打印
  • 轻量级 Docker 环境部署指南:Nginx + PHP 8.2 (Alpine) + Redis + MySQL
  • 行空板 K10 与 Mind+ 零基础 AI 入门实战
  • 灵感画廊:基于 Stable Diffusion XL 的极简 AI 绘画工具
  • 数据结构:堆与优先级队列
  • 网络安全专业就业前景与职业发展分析
  • Zread:智谱 AI 推出的 GitHub 项目阅读与文档生成工具
  • FastGithub 部署指南:智能 DNS 解析优化 GitHub 访问
  • 新版 Android Studio 修改 JDK 版本

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online