Rust no-std嵌入式開發:從零構建裸機韌體完整指南 2026

编程语言

Rust no-std嵌入式開發:從零構建裸機韌體完整指南 2026

在嵌入式開發領域,C語言長期佔據統治地位。但隨著Rust生態的成熟,越來越多的團隊開始用Rust替代C來構建安全可靠的裸機韌體。Rust的所有權系統在編譯期就能消除記憶體安全隱患,而no_std環境讓Rust可以在沒有任何作業系統的裸機硬體上運行。本文將帶你從零開始,完整掌握Rust嵌入式開發的核心模式。

核心概念速覽

概念 說明 適用場景
no_std 禁用Rust標準庫,僅使用corealloc 裸機/RTOS環境
cortex-m-rt Cortex-M執行時入口與連結腳本 ARM Cortex-M系列MCU
PAC 週邊存取包,直接映射暫存器 底層驅動開發
HAL 硬體抽象層,提供進階API 應用層開發
critical-section 臨界區抽象,跨平台安全 中斷安全程式碼
defmt 零成本日誌框架 嵌入式除錯
RTIC 基於中斷的即時並行框架 即時系統
embassy 非同步嵌入式執行器 高並行週邊操作

五大痛點分析

  1. 環境搭建複雜:Rust嵌入式工具鏈配置繁瑣,交叉編譯、除錯器連接、燒錄工具鏈缺一不可,新手往往在環境配置階段就放棄了
  2. 裸機啟動流程不透明:從MCU上電到main()函式執行之間的啟動過程(向量表、堆疊初始化、資料段搬運)對很多開發者來說是個黑盒
  3. 週邊驅動開發門檻高:暫存器操作、時序控制、DMA配置等底層操作需要同時理解硬體手冊和Rust unsafe語義
  4. 中斷處理容易出Bug:中斷與主迴圈的資料競爭、優先級配置、巢狀中斷等都是高頻Bug來源
  5. RTOS整合缺乏最佳實踐:FreeRTOS/RT-Thread與Rust的互操作、記憶體分配策略、任務間通訊等缺少成熟方案

分步實操:5個核心模式

模式一:no_std環境搭建

執行環境:Rust 1.85+ / cortex-m target / probe-rs 0.24+

首先建立no_std專案並配置交叉編譯目標:

# 安裝Rust嵌入式目標
rustup target add thumbv7em-none-eabihf

# 建立no_std專案
cargo new --lib embedded-firmware
cd embedded-firmware
# Cargo.toml
[package]
name = "embedded-firmware"
version = "0.1.0"
edition = "2021"

[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "1.0"
defmt = "0.3"
defmt-rtt = "0.4"

[profile.release]
opt-level = "s"
lto = true
codegen-units = 1

[[bin]]
name = "embedded-firmware"
path = "src/main.rs"
// src/main.rs
#![no_main]
#![no_std]

use cortex_m_rt::entry;
use panic_halt as _;
use defmt::info;

#[entry]
fn main() -> ! {
    info!("Hello from no_std Rust!");

    loop {
        cortex_m::asm::delay(8_000_000); // 約1秒延時(8MHz時脈)
    }
}
# .cargo/config.toml
[build]
target = "thumbv7em-none-eabihf"

[target.thumbv7em-none-eabihf]
runner = "probe-rs run --chip STM32F411CEUx"

rustflags = [
    "-C", "link-arg=-Tlink.x",
    "-C", "link-arg=--nmagic",
    "-C", "link-arg=-Tdefmt.x",
]

模式二:Cortex-M裸機啟動

理解並自訂啟動流程,掌握向量表和連結腳本:

// src/startup.rs
//! 自訂啟動程式碼 - 理解MCU從上電到main的全過程

use core::arch::global_asm;

// 向量表:MCU上電後第一個讀取的資料結構
// 位於Flash起始位址,包含初始堆疊指標和中斷向量
global_asm!(
    ".section .vector_table, \"a\"",
    ".global _vector_table",
    "_vector_table:",
    "    .word _estack           // 初始堆疊指標(指向RAM末尾)",
    "    .word Reset             // Reset處理函式",
    "    .word NMI               // NMI處理函式",
    "    .word HardFault         // HardFault處理函式",
    "    .word 0                 // MemManage(Cortex-M7特有)",
    "    .word 0                 // BusFault",
    "    .word 0                 // UsageFault",
    "    .word 0                 // Reserved",
    "    .word 0                 // Reserved",
    "    .word 0                 // Reserved",
    "    .word 0                 // Reserved",
    "    .word SVCall            // SVC呼叫",
    "    .word DebugMonitor      // Debug Monitor",
    "    .word 0                 // Reserved",
    "    .word PendSV            // PendSV(上下文切換)",
    "    .word SysTick           // SysTick計時器",
);

/// Reset處理函式:MCU上電或復位後執行
/// 負責:初始化.data段、清零.bss段、呼叫main
#[no_mangle]
pub unsafe extern "C" fn Reset() -> ! {
    // 1. 初始化.data段:從Flash複製初始化資料到RAM
    extern "C" {
        static mut _sdata: u32;
        static mut _edata: u32;
        static _sidata: u32;
    }

    let mut src = &_sidata as *const u32;
    let mut dst = &mut _sdata as *mut u32;
    while dst < &mut _edata as *mut u32 {
        dst.write_volatile(src.read_volatile());
        src = src.add(1);
        dst = dst.add(1);
    }

    // 2. 清零.bss段
    extern "C" {
        static mut _sbss: u32;
        static mut _ebss: u32;
    }

    let mut bss_dst = &mut _sbss as *mut u32;
    while bss_dst < &mut _ebss as *mut u32 {
        bss_dst.write_volatile(0);
        bss_dst = bss_dst.add(1);
    }

    // 3. 呼叫Rust main函式
    extern "Rust" {
        fn main() -> !;
    }
    main()
}

/// HardFault處理 - 嵌入式最關鍵的異常處理
#[no_mangle]
pub unsafe extern "C" fn HardFault() -> ! {
    let scb = cortex_m::peripheral::SCB::ptr();
    let hfsr = (*scb).hfsr.read();
    defmt::error!("HardFault! HFSR = {:08x}", hfsr);

    let psp = cortex_m::register::psp::read();
    if psp != 0 {
        let stack_frame = psp as *const u32;
        defmt::error!("R0  = {:08x}", *stack_frame.offset(0));
        defmt::error!("R3  = {:08x}", *stack_frame.offset(3));
        defmt::error!("R12 = {:08x}", *stack_frame.offset(4));
        defmt::error!("LR  = {:08x}", *stack_frame.offset(5));
        defmt::error!("PC  = {:08x}", *stack_frame.offset(6));
    }

    loop {}
}

fn NMI() {}
fn SVCall() {}
fn DebugMonitor() {}
fn PendSV() {}
fn SysTick() {}

模式三:週邊驅動開發

基於PAC和HAL開發週邊驅動,以GPIO和UART為例:

// src/drivers/led.rs
//! LED驅動 - 基於HAL的GPIO封裝

use embedded_hal::digital::OutputPin;
use cortex_m::delay::Delay;

/// LED驅動結構體
pub struct Led<PIN: OutputPin> {
    pin: PIN,
    active_low: bool,
}

impl<PIN: OutputPin> Led<PIN> {
    /// 建立新的LED實例
    /// - `pin`: GPIO輸出引腳
    /// - `active_low`: true表示低電位點亮(共陽極LED)
    pub fn new(pin: PIN, active_low: bool) -> Self {
        let mut led = Self { pin, active_low };
        led.off();
        led
    }

    /// 點亮LED
    pub fn on(&mut self) {
        if self.active_low {
            self.pin.set_low().ok();
        } else {
            self.pin.set_high().ok();
        }
    }

    /// 關閉LED
    pub fn off(&mut self) {
        if self.active_low {
            self.pin.set_high().ok();
        } else {
            self.pin.set_low().ok();
        }
    }

    /// LED呼吸效果(軟體PWM)
    pub fn breathe(&mut self, delay: &mut Delay, cycles: u32) {
        const STEPS: u32 = 100;
        for _ in 0..cycles {
            for i in 0..STEPS {
                self.on();
                delay.delay_us(i);
                self.off();
                delay.delay_us(STEPS - i);
            }
            for i in (0..STEPS).rev() {
                self.on();
                delay.delay_us(i);
                self.off();
                delay.delay_us(STEPS - i);
            }
        }
    }
}
// src/drivers/uart.rs
//! UART驅動 - 基於HAL的串列通訊封裝

use embedded_hal::serial::{Read, Write};
use nb::block;

/// UART錯誤類型
#[derive(Debug, defmt::Format)]
pub enum UartError {
    Framing,
    Noise,
    Overrun,
    Parity,
    BufferFull,
}

/// 環形緩衝區 - 用於中斷接收
pub struct RingBuffer<const N: usize> {
    buffer: [u8; N],
    head: usize,
    tail: usize,
    full: bool,
}

impl<const N: usize> RingBuffer<N> {
    pub const fn new() -> Self {
        Self {
            buffer: [0u8; N],
            head: 0,
            tail: 0,
            full: false,
        }
    }

    pub fn push(&mut self, byte: u8) -> bool {
        if self.full {
            return false;
        }
        self.buffer[self.head] = byte;
        self.head = (self.head + 1) % N;
        if self.head == self.tail {
            self.full = true;
        }
        true
    }

    pub fn pop(&mut self) -> Option<u8> {
        if self.is_empty() {
            return None;
        }
        let byte = self.buffer[self.tail];
        self.tail = (self.tail + 1) % N;
        self.full = false;
        Some(byte)
    }

    pub fn is_empty(&self) -> bool {
        self.head == self.tail && !self.full
    }

    pub fn len(&self) -> usize {
        if self.full {
            return N;
        }
        if self.head >= self.tail {
            self.head - self.tail
        } else {
            N - self.tail + self.head
        }
    }
}

/// UART通訊介面
pub struct UartDriver<SERIAL> {
    serial: SERIAL,
    rx_buffer: RingBuffer<256>,
}

impl<SERIAL, E> UartDriver<SERIAL>
where
    SERIAL: Read<u8, Error = E> + Write<u8, Error = E>,
    E: defmt::Format,
{
    pub fn new(serial: SERIAL) -> Self {
        Self {
            serial,
            rx_buffer: RingBuffer::new(),
        }
    }

    /// 非阻塞讀取中斷接收的資料
    pub fn read_byte(&mut self) -> Option<u8> {
        self.rx_buffer.pop()
    }

    /// 中斷中呼叫:將接收到的位元組存入緩衝區
    pub fn on_rx_interrupt(&mut self) {
        match self.serial.read() {
            Ok(byte) => {
                if !self.rx_buffer.push(byte) {
                    defmt::warn!("UART RX buffer overflow!");
                }
            }
            Err(nb::Error::WouldBlock) => {}
            Err(nb::Error::Other(_)) => {
                defmt::error!("UART read error");
            }
        }
    }

    /// 阻塞發送位元組
    pub fn write_byte(&mut self, byte: u8) {
        block!(self.serial.write(byte)).ok();
    }

    /// 阻塞發送字串
    pub fn write_str(&mut self, s: &str) {
        for byte in s.bytes() {
            self.write_byte(byte);
        }
    }

    /// 釋放底層串列埠所有權
    pub fn free(self) -> SERIAL {
        self.serial
    }
}

模式四:中斷處理

安全的中斷處理模式,避免資料競爭:

// src/interrupts.rs
//! 安全的中斷處理 - 使用cortex_m::interrupt和atomic

use core::sync::atomic::{AtomicU32, AtomicBool, Ordering};
use cortex_m::interrupt::{self, Mutex};
use core::cell::RefCell;
use stm32f4xx_hal::pac::{self, interrupt};

/// 方式1:原子操作 - 最簡單的中斷安全方案
static Systick_COUNT: AtomicU32 = AtomicU32::new(0);
static BUTTON_PRESSED: AtomicBool = AtomicBool::new(false);

/// 方式2:Mutex保護的共享資源
type SharedSerial = Mutex<RefCell<Option<pac::USART1>>>;
static SHARED_SERIAL: SharedSerial = Mutex::new(RefCell::new(None));

/// 方式3:cortex_m::singleton! 編譯期保證單例
cortex_m::singleton!(
    static SHARED_BUFFER: [u8; 128] = [0; 128];
);

/// SysTick中斷 - 系統節拍
#[interrupt]
fn SysTick() {
    Systick_COUNT.fetch_add(1, Ordering::Relaxed);

    if Systick_COUNT.load(Ordering::Relaxed) % 1000 == 0 {
        defmt::trace!("1 second elapsed");
    }
}

/// 外部按鈕中斷 - GPIO中斷
#[interrupt]
fn EXTI0() {
    unsafe {
        let exti = &*pac::EXTI::ptr();
        exti.pr.write(|w| w.pr0().set_bit());
    }

    BUTTON_PRESSED.store(true, Ordering::Release);
}

/// USART1中斷 - 串列埠接收中斷
#[interrupt]
fn USART1() {
    interrupt::free(|cs| {
        let mut serial = SHARED_SERIAL.borrow(cs).borrow_mut();
        if let Some(ref mut usart1) = serial.deref_mut() {
            if usart1.sr.read().rxne().bit_is_set() {
                let byte = usart1.dr.read().bits() as u8;
                defmt::info!("RX: {:02x}", byte);
            }
        }
    });
}

/// 主迴圈中檢查中斷標誌
pub fn process_interrupt_flags() {
    if BUTTON_PRESSED.swap(false, Ordering::AcqRel) {
        defmt::info!("Button pressed!");
    }

    let tick = Systick_COUNT.load(Ordering::Acquire);
    if tick > 0 && tick % 5000 == 0 {
        defmt::info!("5 seconds elapsed, tick = {}", tick);
    }
}

use core::ops::DerefMut;

模式五:RTOS整合

Rust與FreeRTOS/embassy的整合方案:

// src/rtos_embassy.rs
//! Embassy非同步執行器 - Rust原生的"RTOS"方案

use embassy_executor::Spawner;
use embassy_stm32::{
    gpio::{Level, Output, Speed},
    usart::Uart,
    time::Hertz,
    Config,
};
use embassy_time::{Duration, Timer};

/// Embassy非同步任務:LED閃爍
#[embassy_executor::task]
async fn blink_led(mut led: Output<'static, embassy_stm32::gpio::PA5>) {
    let mut counter = 0u32;
    loop {
        led.set_high();
        Timer::after(Duration::from_millis(500)).await;
        led.set_low();
        Timer::after(Duration::from_millis(500)).await;
        counter += 1;
        defmt::info!("Blink #{}", counter);
    }
}

/// Embassy非同步任務:串列埠回顯
#[embassy_executor::task]
async fn uart_echo(mut uart: Uart<'static, embassy_stm32::usart::USART2>) {
    use embassy_stm32::usart::Error;
    loop {
        let mut buf = [0u8; 1];
        match uart.read(&mut buf).await {
            Ok(_) => {
                let _ = uart.write(&buf).await;
            }
            Err(Error::Framing) => defmt::error!("UART framing error"),
            Err(_) => defmt::error!("UART error"),
        }
    }
}

/// Embassy非同步任務:感測器讀取
#[embassy_executor::task]
async fn sensor_reader() {
    let mut values = [0u16; 64];
    let mut idx = 0;
    loop {
        Timer::after(Duration::from_secs(1)).await;

        let value = read_adc_channel(0);
        values[idx % 64] = value;
        idx += 1;

        if idx % 10 == 0 {
            let avg: u16 = values[..10].iter().sum::<u16>() / 10;
            defmt::info!("Sensor avg: {}", avg);
        }
    }
}

fn read_adc_channel(_ch: u8) -> u16 {
    42
}

/// 主入口 - Embassy執行時
#[embassy_executor::main]
async fn main(spawner: Spawner) {
    let config = Config::default();
    let p = embassy_stm32::init(config);

    let led = Output::new(p.PA5, Level::Low, Speed::Low);

    let uart_config = embassy_stm32::usart::Config::default()
        .baudrate(Hertz(115200));
    let uart = Uart::new(
        p.USART2, p.PA3, p.PA2,
        uart_config,
    ).unwrap();

    spawner.spawn(blink_led(led)).ok();
    spawner.spawn(uart_echo(uart)).ok();
    spawner.spawn(sensor_reader()).ok();

    defmt::info!("Embassy RTOS started!");

    loop {
        Timer::after(Duration::from_secs(5)).await;
        defmt::info!("Main loop heartbeat");
    }
}
// src/rtos_freertos.rs
//! FreeRTOS與Rust整合 - 使用freertos-rust crate

use freertos_rust::{CurrentTask, Delay, FreeRtosAllocator, Task, TaskPriority};

#[global_allocator]
static ALLOC: FreeRtosAllocator = FreeRtosAllocator;

fn led_task(_pv: *mut core::ffi::c_void) {
    loop {
        toggle_led();
        Delay::new(500);
    }
}

fn sensor_task(_pv: *mut core::ffi::c_void) {
    loop {
        let value = read_sensor();
        defmt::info!("Sensor: {}", value);
        Delay::new(1000);
    }
}

pub fn start_freertos_tasks() {
    Task::new()
        .name("LED")
        .stack_size(256)
        .priority(TaskPriority(2))
        .start(led_task)
        .expect("Failed to create LED task");

    Task::new()
        .name("Sensor")
        .stack_size(512)
        .priority(TaskPriority(1))
        .start(sensor_task)
        .expect("Failed to create Sensor task");

    freertos_rust::start_scheduler();
}

fn toggle_led() { /* 硬體相關 */ }
fn read_sensor() -> u16 { 42 }

避坑指南

坑1:忘記#![no_std]導致連結錯誤

// ❌ 錯誤:缺少no_std宣告
use std::println; // 這會拉入整個標準庫!

// ✅ 正確:必須在crate根部宣告
#![no_std]
#![no_main]

原因:Rust預設連結標準庫,而嵌入式目標沒有OS支援標準庫。忘記宣告會導致大量未定義符號的連結錯誤。

坑2:在中斷中呼叫阻塞函式

// ❌ 錯誤:中斷中執行耗時操作
#[interrupt]
fn USART1() {
    let data = blocking_read_sensor(); // 可能阻塞數ms!
    process_data(data);
}

// ✅ 正確:中斷中只設標誌,主迴圈處理
#[interrupt]
fn USART1() {
    DATA_READY.store(true, Ordering::Release);
}

loop {
    if DATA_READY.swap(false, Ordering::AcqRel) {
        let data = blocking_read_sensor();
        process_data(data);
    }
}

坑3:共享可變狀態未加保護

// ❌ 錯誤:裸共享可變靜態變數
static mut BUFFER: [u8; 64] = [0; 64];

#[interrupt]
fn DMA1_STREAM0() {
    unsafe { BUFFER[0] = 42; } // 資料競爭!
}

// ✅ 正確:使用Mutex或Atomic
use core::sync::atomic::AtomicU8;
static BUFFER_HEAD: AtomicU8 = AtomicU8::new(0);

坑4:PAC暫存器操作忘記unsafe

// ❌ 錯誤:直接存取PAC暫存器而不使用安全API
let gpioa = unsafe { &*pac::GPIOA::ptr() };
gpioa.bsrr.write(|w| w.bits(1)); // 未使用型別安全的API

// ✅ 正確:使用PAC提供的型別安全API
let gpioa = unsafe { &*pac::GPIOA::ptr() };
gpioa.bsrr.write(|w| w.bs5().set_bit()); // 編譯期檢查

坑5:堆疊溢位未偵測

// ❌ 錯誤:大陣列直接放在堆疊上
fn process() {
    let buffer = [0u8; 4096]; // STM32F103只有20KB RAM!
}

// ✅ 正確:使用靜態緩衝區或堆分配
static BUFFER: cortex_m::singleton!(Buffer = [0u8; 4096]) = [0; 4096];

use embedded_alloc::Heap;
#[global_allocator]
static HEAP: Heap = Heap::empty();

報錯排查表

報錯資訊 原因 解決方案
error: language item required, but not found: eh_personality 缺少no_std執行時支援 main.rs新增#![no_main]panic-halt依賴
error: linking with cc failed 連結腳本缺失 新增-C link-arg=-Tlink.x到rustflags
error[E0152]: duplicate lang item 多個crate定義相同lang item 檢查是否同時引用了stdno_std的panic handler
region FLASH overflowed 韌體超出Flash容量 開啟LTO、減小codegen-units為1、使用opt-level=s
region RAM overflowed RAM不足 減小堆疊/堆大小、使用靜態分配替代堆分配
HardFault at 0x08001234 空指標解參考或堆疊溢位 使用defmt列印故障暫存器、檢查堆疊大小
panic at src/main.rs:42 執行時panic 使用panic-probe替代panic-halt取得呼叫堆疊
error: cannot find -lprobe_rs probe-rs未安裝 cargo install probe-rs-tools
OpenOCD connection failed 除錯器連接失敗 檢查ST-Link驅動、USB線、openocd.cfg配置
error[E0277]: the trait bound is not satisfied HAL trait未實作 檢查目標MCU是否被HAL crate支援

進階最佳化

1. 使用defmt替代傳統日誌

// defmt在release模式下零開銷
// 編譯期格式化,不佔用執行時資源
defmt::info!("Sensor value: {}", value);
defmt::debug!("Buffer: {[u8; 4]}", &buf[..4]);
defmt::error!("HardFault HFSR={:08x}", hfsr);

2. 使用cortex_m::singleton!管理靜態資源

// 編譯期保證單例,避免執行時檢查
cortex_m::singleton!(
    static SHARED_STATE: SharedState = SharedState::new();
);

3. 使用RTIC實現零成本中斷驅動架構

#[rtic::app(device = stm32f4xx_hal::pac)]
mod app {
    use stm32f4xx_hal::prelude::*;

    #[shared]
    struct Shared {
        sensor_value: u16,
    }

    #[local]
    struct Local {
        led: PA5<Output<PushPull>>,
    }

    #[init]
    fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
        let mut led = cx.device.GPIOA.split().pa5.into_push_pull_output();
        led.set_high();

        (
            Shared { sensor_value: 0 },
            Local { led },
            init::Monotonics(),
        )
    }

    #[task(binds = TIM2, shared = [sensor_value], local = [led])]
    fn timer_interrupt(cx: timer_interrupt::Context) {
        let sensor_value = cx.shared.sensor_value;
        let led = cx.local.led;

        *sensor_value.lock(|v| *v = read_adc());
        led.toggle();
    }
}

4. 使用embassy實現非同步並行

#[embassy_executor::task]
async fn task_a() {
    loop {
        do_something().await;
        Timer::after(Duration::from_millis(100)).await;
    }
}

#[embassy_executor::task]
async fn task_b() {
    loop {
        another_thing().await;
        Timer::after(Duration::from_secs(1)).await;
    }
}
// 每個任務僅需數十位元組的堆疊(協程狀態機)

5. Flash最佳化策略

[profile.release]
opt-level = "z"          # 最佳化體積而非速度
lto = true               # 連結時最佳化,消除未使用程式碼
codegen-units = 1        # 單編譯單元,最佳最佳化
strip = true             # 去除除錯符號
panic = "abort"          # abort替代unwind,減小體積

# 典型最佳化效果:
# 最佳化前:128KB
# 最佳化後:32KB(減少75%)

對比分析

特性 Embassy RTIC FreeRTOS+Rust 裸機輪詢
記憶體開銷 極低(協程) 極低 高(每任務獨立堆疊) 最低
學習曲線 中等 中等
並行模型 async/await 中斷優先級 搶佔式多任務
生態成熟度 ★★★★ ★★★ ★★ ★★★★★
除錯便利性 ★★★★ ★★★★ ★★★ ★★★★★
即時性保證 軟即時 硬即時 硬即時 取決於實作
Rust原生 ❌(C互操作)
適用場景 IoT/感測器 馬達控制 複雜多任務 簡單控制

總結

Rust嵌入式開發正在從「嘗鮮」走向「生產可用」。2026年的今天,no_std生態已經足夠成熟:

  • 環境搭建:probe-rs統一了燒錄和除錯體驗,cargo embed一鍵完成
  • 裸機啟動:cortex-m-rt封裝了啟動細節,但理解底層原理仍是排障關鍵
  • 週邊驅動:PAC→HAL→應用的三層架構,兼顧安全與效率
  • 中斷處理:原子操作、Mutex、RTIC三種方案覆蓋不同複雜度需求
  • RTOS整合:Embassy作為Rust原生方案正在成為首選,FreeRTOS適合存量專案遷移

選擇建議:新專案優先考慮Embassy;對即時性要求極高的場景選RTIC;已有FreeRTOS程式碼庫的用freertos-rust漸進遷移。

線上工具推薦

本站提供瀏覽器本地工具,免註冊即可試用 →

#Rust no-std#嵌入式开发#裸机固件#ARM Cortex#嵌入式Rust#2026#编程语言