embedded-rust

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Embedded Rust

Embedded Rust

Purpose

用途

Guide agents through embedded Rust development: flashing and debugging with probe-rs/cargo-embed, structured logging with defmt, the RTIC concurrency framework, cortex-m-rt startup, no_std configuration, and panic handler selection.
为开发者指引Embedded Rust开发的全流程:使用probe-rs/cargo-embed进行烧录与调试、通过defmt实现结构化日志、使用RTIC并发框架、配置cortex-m-rt启动流程、设置no_std环境,以及选择panic处理器。

Triggers

触发场景

  • "How do I flash my Rust firmware to an MCU?"
  • "How do I debug my embedded Rust program?"
  • "How do I use defmt for logging in embedded Rust?"
  • "How do I use RTIC for interrupt-driven concurrency?"
  • "What does #![no_std] #![no_main] mean for embedded Rust?"
  • "How do I handle panics in no_std embedded Rust?"
  • "如何将Rust固件烧录到MCU中?"
  • "如何调试我的嵌入式Rust程序?"
  • "如何在嵌入式Rust中使用defmt进行日志记录?"
  • "如何使用RTIC实现中断驱动的并发?"
  • "在嵌入式Rust中#![no_std] #![no_main]是什么意思?"
  • "如何在no_std嵌入式Rust中处理panic?"

Workflow

开发流程

1. Project setup

1. 项目配置

toml
undefined
toml
undefined

Cargo.toml

Cargo.toml

[package] name = "my-firmware" version = "0.1.0" edition = "2021"
[dependencies] cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] }
[package] name = "my-firmware" version = "0.1.0" edition = "2021"
[dependencies] cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] }

Embassy (async embedded) — alternative to RTIC

Embassy (异步嵌入式) — RTIC的替代方案

embassy-executor = { version = "0.5", features = ["arch-cortex-m"] }

embassy-executor = { version = "0.5", features = ["arch-cortex-m"] }

[profile.release] opt-level = "s" # size optimization for embedded lto = true codegen-units = 1 debug = true # keep debug info for defmt/probe-rs
[profile.release] opt-level = "s" # 嵌入式场景下的尺寸优化 lto = true codegen-units = 1 debug = true # 保留调试信息以支持defmt/probe-rs

.cargo/config.toml

.cargo/config.toml

[build] target = "thumbv7em-none-eabihf" # Cortex-M4F / M7
[target.thumbv7em-none-eabihf] runner = "probe-rs run --chip STM32F411CEUx" # auto-run after build rustflags = ["-C", "link-arg=-Tlink.x"] # cortex-m-rt linker script
undefined
[build] target = "thumbv7em-none-eabihf" # Cortex-M4F / M7
[target.thumbv7em-none-eabihf] runner = "probe-rs run --chip STM32F411CEUx" # 构建完成后自动运行 rustflags = ["-C", "link-arg=-Tlink.x"] # cortex-m-rt链接脚本
undefined

2. Minimal bare-metal program

2. 极简裸机程序

rust
// src/main.rs
#![no_std]
#![no_main]

use cortex_m_rt::entry;
use defmt::info;
use defmt_rtt as _;      // RTT transport for defmt
use panic_probe as _;    // panic handler that prints via defmt

#[entry]
fn main() -> ! {
    info!("Booting up!");

    // Access peripherals via PAC or HAL
    let _core = cortex_m::Peripherals::take().unwrap();
    // let dp = stm32f4xx_hal::pac::Peripherals::take().unwrap();

    loop {
        info!("Running...");
        cortex_m::asm::delay(8_000_000);  // rough delay
    }
}
Target triples for common MCUs:
MCU familyTarget triple
Cortex-M0/M0+
thumbv6m-none-eabi
Cortex-M3
thumbv7m-none-eabi
Cortex-M4 (no FPU)
thumbv7em-none-eabi
Cortex-M4F / M7
thumbv7em-none-eabihf
Cortex-M33
thumbv8m.main-none-eabihf
RISC-V RV32IMAC
riscv32imac-unknown-none-elf
bash
rustup target add thumbv7em-none-eabihf
rust
// src/main.rs
#![no_std]
#![no_main]

use cortex_m_rt::entry;
use defmt::info;
use defmt_rtt as _;      // defmt的RTT传输层
use panic_probe as _;    // 通过defmt打印信息的panic处理器

#[entry]
fn main() -> ! {
    info!("Booting up!");

    // 通过PAC或HAL访问外设
    let _core = cortex_m::Peripherals::take().unwrap();
    // let dp = stm32f4xx_hal::pac::Peripherals::take().unwrap();

    loop {
        info!("Running...");
        cortex_m::asm::delay(8_000_000);  // 粗略延时
    }
}
常见MCU对应的目标三元组:
MCU系列目标三元组
Cortex-M0/M0+
thumbv6m-none-eabi
Cortex-M3
thumbv7m-none-eabi
Cortex-M4 (无FPU)
thumbv7em-none-eabi
Cortex-M4F / M7
thumbv7em-none-eabihf
Cortex-M33
thumbv8m.main-none-eabihf
RISC-V RV32IMAC
riscv32imac-unknown-none-elf
bash
rustup target add thumbv7em-none-eabihf

3. probe-rs — flash and debug

3. probe-rs — 烧录与调试

bash
undefined
bash
undefined

Install probe-rs

安装probe-rs

Flash firmware

烧录固件

probe-rs run --chip STM32F411CEUx target/thumbv7em-none-eabihf/release/firmware
probe-rs run --chip STM32F411CEUx target/thumbv7em-none-eabihf/release/firmware

Interactive debug session

交互式调试会话

probe-rs debug --chip STM32F411CEUx target/thumbv7em-none-eabihf/release/firmware
probe-rs debug --chip STM32F411CEUx target/thumbv7em-none-eabihf/release/firmware

List connected probes

列出已连接的调试器

probe-rs list
probe-rs list

Supported chips

支持的芯片

probe-rs chip list | grep STM32

With `cargo`:

```bash
probe-rs chip list | grep STM32

配合`cargo`使用:

```bash

Using the runner in .cargo/config.toml

使用.cargo/config.toml中配置的runner

cargo run --release # builds, flashes, and streams defmt logs cargo build --release # build only
undefined
cargo run --release # 构建、烧录并流式传输defmt日志 cargo build --release # 仅构建
undefined

4. defmt — efficient logging

4. defmt — 高效日志

defmt (de-formatter) encodes log strings to integers, transmits minimal bytes, decodes on host:
rust
use defmt::{info, warn, error, debug, trace, Format};

// Basic logging
info!("Temperature: {} °C", temp);
warn!("Stack usage: {}/{}",  used, total);
error!("I2C error: {:?}", err);

// Derive Format for custom types
#[derive(Format)]
struct Packet { id: u8, len: u16 }

info!("Received: {:?}", pkt);

// Assertions (panic with defmt message)
defmt::assert_eq!(result, expected);
defmt::assert!(condition, "message with {}", value);
defmt backends (choose one):
toml
undefined
defmt(解格式化工具)将日志字符串编码为整数,传输最小化的字节数,在主机端进行解码:
rust
use defmt::{info, warn, error, debug, trace, Format};

// 基础日志
info!("Temperature: {} °C", temp);
warn!("Stack usage: {}/{}",  used, total);
error!("I2C error: {:?}", err);

// 为自定义类型派生Format特性
#[derive(Format)]
struct Packet { id: u8, len: u16 }

info!("Received: {:?}", pkt);

// 断言(携带defmt信息的panic)
defmt::assert_eq!(result, expected);
defmt::assert!(condition, "message with {}", value);
defmt后端(选择其一):
toml
undefined

RTT (fastest, needs debug probe connected)

RTT(速度最快,需要连接调试器)

defmt-rtt = "0.4"
defmt-rtt = "0.4"

Semihosting (slower, works without RTT support)

Semihosting(速度较慢,无需RTT支持即可工作)

defmt-semihosting = "0.1"
undefined
defmt-semihosting = "0.1"
undefined

5. RTIC — Real-Time Interrupt-driven Concurrency

5. RTIC — 实时中断驱动并发

rust
// Cargo.toml
// rtic = { version = "2", features = ["thumbv7-backend"] }

#[rtic::app(device = stm32f4xx_hal::pac, peripherals = true, dispatchers = [SPI1])]
mod app {
    use stm32f4xx_hal::{pac, prelude::*};
    use defmt::info;

    #[shared]
    struct Shared {
        counter: u32,
    }

    #[local]
    struct Local {}

    #[init]
    fn init(cx: init::Context) -> (Shared, Local) {
        info!("RTIC init");
        periodic_task::spawn().unwrap();
        (Shared { counter: 0 }, Local {})
    }

    #[task(shared = [counter])]
    async fn periodic_task(mut cx: periodic_task::Context) {
        loop {
            cx.shared.counter.lock(|c| *c += 1);
            info!("Count: {}", cx.shared.counter.lock(|c| *c));
            rtic_monotonics::systick::Systick::delay(500.millis()).await;
        }
    }

    #[task(binds = EXTI0, local = [], priority = 2)]
    fn button_isr(cx: button_isr::Context) {
        info!("Button pressed!");
    }
}
rust
// Cargo.toml
// rtic = { version = "2", features = ["thumbv7-backend"] }

#[rtic::app(device = stm32f4xx_hal::pac, peripherals = true, dispatchers = [SPI1])]
mod app {
    use stm32f4xx_hal::{pac, prelude::*};
    use defmt::info;

    #[shared]
    struct Shared {
        counter: u32,
    }

    #[local]
    struct Local {}

    #[init]
    fn init(cx: init::Context) -> (Shared, Local) {
        info!("RTIC init");
        periodic_task::spawn().unwrap();
        (Shared { counter: 0 }, Local {})
    }

    #[task(shared = [counter])]
    async fn periodic_task(mut cx: periodic_task::Context) {
        loop {
            cx.shared.counter.lock(|c| *c += 1);
            info!("Count: {}", cx.shared.counter.lock(|c| *c));
            rtic_monotonics::systick::Systick::delay(500.millis()).await;
        }
    }

    #[task(binds = EXTI0, local = [], priority = 2)]
    fn button_isr(cx: button_isr::Context) {
        info!("Button pressed!");
    }
}

6. Panic handlers

6. Panic处理器

CrateBehaviorUse when
panic-halt
Infinite loopProduction, no debug probe
panic-probe
defmt message + haltDevelopment with probe-rs
panic-semihosting
GDB semihosting outputDevelopment with GDB
panic-reset
Hard resetWatchdog-style recovery
toml
undefined
依赖包行为适用场景
panic-halt
无限循环生产环境,无调试器
panic-probe
输出defmt信息后暂停使用probe-rs的开发环境
panic-semihosting
通过GDB semihosting输出信息使用GDB的开发环境
panic-reset
硬重置看门狗式恢复场景
toml
undefined

Choose exactly one panic handler

仅选择一个panic处理器

[dependencies] panic-halt = "0.2" # or: panic-probe = { version = "0.3", features = ["print-defmt"] }

For embedded Rust target triples reference, see [references/embedded-rust-targets.md](references/embedded-rust-targets.md).
[dependencies] panic-halt = "0.2" # 或: panic-probe = { version = "0.3", features = ["print-defmt"] }

关于嵌入式Rust目标三元组的参考,见[references/embedded-rust-targets.md](references/embedded-rust-targets.md)。

Related skills

相关技能

  • Use
    skills/embedded/openocd-jtag
    for OpenOCD-based debugging alternative to probe-rs
  • Use
    skills/rust/rust-no-std
    for
    #![no_std]
    patterns and constraints
  • Use
    skills/embedded/linker-scripts
    for memory layout configuration
  • Use
    skills/rust/rust-cross
    for cross-compilation toolchain setup
  • 若需替代probe-rs的OpenOCD调试方案,使用
    skills/embedded/openocd-jtag
  • 若需了解
    #![no_std]
    的模式与约束,使用
    skills/rust/rust-no-std
  • 若需配置内存布局,使用
    skills/embedded/linker-scripts
  • 若需搭建交叉编译工具链,使用
    skills/rust/rust-cross