| 编辑推荐: |
文章主要介绍了Embassy,一个用于 MCU(微控制器)的 Rust 异步框架,旨在替代传统的 RTOS(实时操作系统),希望对您的学习有所帮助。
本文来自于公众号:单片机那点儿事,由火龙果软件Alice编辑、推荐。
|
|
需求一多——ADC 采样、协议解析、刷屏、打日志——老办法就是上 RTOS:建任务、分栈、调优先级。栈给大了 RAM 吃紧,给小了哪天溢出 HardFault,还专挑不复现的时候炸。
GitHub 上 9.7k+ Star(2026-08 查)的 Embassy 给了另一条路:任务在编译期直接变成状态机,运行时全程一条栈,没有内核调度,也不用给每个任务拍脑袋算栈。ST、Nordic、 树莓派 、TI、NXP 的芯片都有官方 HAL。
Embassy 是什么:MCU 上的 Rust async 框架
名字就是来历:EMBedded ASYnc。2020 年开源,MIT/Apache-2.0 双许可,免费商用;主仓 1.6 万+ 次提交,到现在每周都在动。
它不是「跑在 RTOS 上的库」,而是直接顶掉 RTOS 的位置:调度交给 async executor,外设走自家 HAL。embassy-stm32 覆盖 STM32 全系,embassy-nrf 管 nRF52/53/54/91,embassy-rp 管 RP2040/RP235x,TI MSPM0 和 NXP MCX-A 也进了官方仓库;ESP32 的支持在乐鑫官方的 esp-rs/esp-hal 里做,文档就挂在 docs.espressif.com。国产这边,沁恒 CH32、普冉 PY32 有社区 HAL。
配套也齐:embassy-net(以太网/TCP/UDP/DHCP)、embassy-usb(CDC 串口、HID)、embassy-boot(断电安全的固件升级,带试跑和回滚)、BLE 主机栈 trouble。计时用 embassy-time 的全局 Timer/Instant,不用手撕硬件定时器,也不会溢出。
凭什么不用内核:任务在编译期变成状态机
Rust 的 async 函数,编译器会把它整个编成一个状态机:每个 await 是一次状态转移,executor 只干一件事——谁被中断唤醒就 poll 谁。任务静态分配,不需要堆,自然也没有 malloc 碎片这回事。
栈是关键区别。RTOS 里每个任务一条私有栈,切换时保存、恢复整套寄存器;Embassy 的任务只保存「跨 await 存活的变量」,所有任务共用一条主栈。栈大小不用猜,「任务栈溢出」这个老毛病从根上没了。
没活干时,executor 自动让核心进 WFI 睡觉,中断来了才醒,不做空转轮询,低功耗基本是白送的。
代码长这样(nRF52 例子,STM32 同款写法):
#[embassy_executor::task] async fn blink(pin: Peri<'static, AnyPin>) { let mut led = Output::new(pin, Level::Low, OutputDrive::Standard); loop { led.set_high(); Timer::after_millis(150).await; // 全局时基,不碰硬件定时器 led.set_low(); Timer::after_millis(150).await; } }
#[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_nrf::init(Default::default()); spawner.spawn(blink(p.P0_13.into())).unwrap();
let mut button = Input::new(p.P0_11, Pull::Up); loop { button.wait_for_low().await; // 等按键时核心在睡觉 info!("按下了!"); button.wait_for_high().await; } }
|
注意 wait_for_low().await 这行:等按键不占 CPU、不占额外栈,中断一来任务原地续跑。写起来是顺序逻辑,跑起来是事件驱动。
实测数字:和 FreeRTOS 摆上同一块 F446
官方 README 引用了 Tweede golf 2022 年的对比测试:同一块 STM32F446(180MHz),同样的按键+串口应用,Embassy/Rust 对 FreeRTOS/C——
- 中断处理耗时:2.96μs → 1.45μs,快 51%
- 中断延迟:4.97μs → 3.74μs,低 25%
- 程序体积(.text):20.7KB → 14.3KB,小 31%
- 静态 RAM(.data + .bss):5480B → 872B,省 84%
两句实话:一,单看「上下文切换」这一小段,FreeRTOS 反而更快(2.01μs 对 2.29μs),Embassy 赢在整体设计而不是每一环;二,这是 2022 年的 demo 级测试,当方向参考就行,别直接外推到你的项目。
三步跑起来:装 probe-rs、进例子、cargo run
# 1. 装 Rust 工具链和烧录工具(ST-Link / J-Link / DAPLink 都认) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh cargo install probe-rs-tools
# 2. 拉仓库,进你芯片对应的例子目录 git clone https://github.com/embassy-rs/embassy.git cd embassy/examples/stm32f4 # nRF 用 examples/nrf52840
# 3. 接上调试器直接跑 cargo run --release --bin blinky
|
两个提醒:Cargo.toml 里的芯片 feature 要改成你板子的具体型号,不然烧进去就崩;Embassy 现在保证在最新 stable Rust 上编译,网上老教程说「必须 nightly」的,已经过时了。
什么时候别用:C 资产重、要过认证、赶工期
- 手上 C 代码资产厚(自研协议栈、驱动、算法库):FFI 混编能做,但成本不低,别为尝鲜硬迁老项目。
- 要过功能安全认证(车规、医疗):FreeRTOS、 ThreadX 、Zephyr 有成熟认证路径,Embassy 没有。
- 硬实时卡得死:同一个 executor 内是协作式调度,一个任务闷头算 10ms 不 await,其他任务就干等 10ms;要抢占得开多个 executor 挂不同中断优先级,这套设计得自己兜住。
- 团队没人写过 Rust、项目下月交付:借用检查器前两周真劝退,别拿排期赌。
- 芯片不在官方 HAL 列表里:自己写 PAC /HAL 的工作量不小,先掂量。
上手前最常被问的:学多少 Rust、混 C、调试、商用
- 要先学多少 Rust? 所有权、借用、async 三件套。建议先在电脑上写一两周命令行小工具再上板,直接上板容易两头懵。
- 能和现有 C 代码混用吗? 能,FFI 调 C 库是常规操作,复用现成驱动没问题,unsafe 边界自己守好。
- 怎么调试? probe-rs + defmt,走 RTT 打日志,ST-Link、J-Link 都认;没有 Keil 那种全家桶界面,习惯要换。
- 中断优先级怎么给? 高优先级任务放到挂在高优先级中断上的 executor 里,官方有 multiprio 例子。
- 商用收费吗? MIT/Apache-2.0 双许可,免费商用,改了也不强制开源。
获取方式
- GitHub 仓库:https://github.com/embassy-rs/embassy
- 官方文档(Embassy Book):https://embassy.dev/book/
最后聊聊:你在 RTOS 项目里踩过最疼的一次坑是什么——任务栈溢出、优先级反转,还是内存碎片?
|