引言:“硬件”时代正在改变语言
\n\n如果你曾经用 C 语言为微控制器编写过固件,你会知道那种感觉。那种在调试三个小时后发现程序挂死的感觉——指针飞到了天上,而看门狗却无能为力。嵌入式开发多年来一直是 C 语言和汇编语言的领地。但在过去几年里,一位新玩家登上了舞台——Rust。它不仅仅是“另一种语言”,更是嵌入式系统的一次真正突破。
\n\n在本文中,我们将探讨为什么 Rust 嵌入式开发 不是炒作,而是一种必需。我们将查看真实的代码示例,与 C 语言进行比较,并了解如何入门。如果你正在为 STM32、ESP32 或 ARM Cortex-M 编写代码,那么你来对地方了。
\n\n为什么 Rust 对于嵌入式来说是理想的结合
\n\n嵌入式系统是一个充满限制的世界。内存少,没有操作系统,没有堆,没有异常。编译时错误是好的,运行时错误是坏的。Rust 正是为这些条件而设计的。
\n\n无垃圾回收的内存安全性
\n\nC 语言嵌入式开发者的主要痛点包括 释放后使用、双重释放内存和缓冲区溢出。Rust 在类型层面解决了这些问题。所有权和借用模型即使在 no_std 环境中也能工作——无需分配器、无需操作系统、无需垃圾回收器。
想象一下,你正在为 LED 编写一个驱动程序。在 C 语言中,你会这样写:
\n\n// C 代码\nvoid led_on(GPIO_TypeDef *gpio, uint16_t pin) {\n gpio->BSRR = pin;\n}\n\n一切正常,直到有人传入一个空指针。Rust 不会让你这样做:
\n\n// Rust 代码\nfn led_on(gpio: &mut GpioRegisters, pin: u16) {\n gpio.bsrr.write(|w| w.bits(pin.into()));\n}\n\n这里的 &mut GpioRegisters 保证了引用是有效的,并且对寄存器的访问是独占的。编译器根本不会让危险代码通过。
零成本抽象
\n\n嵌入式开发者最大的恐惧之一是高级语言会“吞噬”处理器时钟周期。Rust 遵循 零成本抽象 原则:你只为使用的东西付费。迭代器、闭包、trait——所有这些都能编译成与手动展开的 C 语言循环一样高效的机器码。
\n\n此外,在 Rust 的嵌入式生态系统中,有些 crate 可以在单指令级别工作。例如,cortex-m-rt 生成中断向量和启动代码,没有一条多余的指令。
实际项目:用 Rust 让 LED 闪烁 (STM32)
\n\n让我们为 STM32F3Discovery 板编写一个简单的程序。我们将使用 cortex-m-quickstart 和来自 embedded rust 社区的 HAL。
步骤 1:项目设置
\n\n确保你已经安装了 Rust 和 ARM Cortex-M 工具链:
\n\nrustup target add thumbv7em-none-eabihf\ncargo install cargo-generate\n\n根据模板创建项目:
\n\ncargo generate --git https://github.com/rust-embedded/cortex-m-quickstart\n\n步骤 2:编写代码
\n\n打开 src/main.rs 并编写经典的 Blinky 程序:
#![no_std]\n#![no_main]\n\nuse cortex_m_rt::entry;\nuse panic_halt as _;\nuse stm32f3xx_hal::{pac, prelude::*};\n\n#[entry]\nfn main() -> ! {\n // 获取外设访问权限\n let dp = pac::Peripherals::take().unwrap();\n let mut rcc = dp.RCC.constrain();\n let mut gpioe = dp.GPIOE.split(&mut rcc.ahb);\n\n // 将 PE9 引脚配置为输出(LED LD1)\n let mut led = gpioe\n .pe9\n .into_push_pull_output(&mut gpioe.moder, &mut gpioe.otyper);\n\n loop {\n led.set_high().unwrap();\n cortex_m::asm::delay(8_000_000);\n led.set_low().unwrap();\n cortex_m::asm::delay(8_000_000);\n }\n}\n\n这里发生了什么?
\n- \n
#![no_std]— 我们没