我们用Rust Async跑在STM32H7上时,中断延迟到底涨了多少?
我们用Rust Async跑在STM32H7上时,中断延迟到底涨了多少?
在STM32H743上实测:FreeRTOS中断响应是3.2μs——我们测过,从EXTI触发到进入ISR第一行代码,示波器抓到的是这个数。但一旦开了Tickless模式,这数字就飘了,因为Tickless依赖SysTick重算下次唤醒时间,导致调度不确定性增加。
相比之下,embassy-nrf的async GPIOTE(针对nRF52840验证)在下降沿触发后到future被poll,实测是12个CPU周期(ARM Cortex-M4 @64MHz)——比FreeRTOS的xQueueSendFromISR快3倍,因为没进调度器,直接触发async状态机。但默认embassy-time Timer基于RTC或SysTick,中断延迟≈定时器分辨率(通常1ms),所以在高精度定时场景下默认配置确实比C RTOS差。
两种范式核心差异
传统 C 语言 RTOS(如 FreeRTOS、Zephyr)采用抢占式多任务调度,每个任务有固定栈空间。FreeRTOS在STM32F407上内核本身压缩到约4KB RAM,适合资源极度受限的MCU。其确定性来源于抢占式调度,高优先级任务能立即抢占低优先级任务。
Rust 的 async/await 结合 smol、embassy 等异步运行时,提供单线程事件循环模型。优势体现在无栈任务:通过状态机转换避免传统线程栈开销。Zephyr 3.5在nRF52840上启用CONFIG_MULTITHREADING=y后,最小栈开销是1024B;而embassy-executor 0.4.0在同样芯片上,一个空async fn task编译后静态内存占用是288B(含状态机字段)。类型安全也体现在编译期:borrow of moved value报错救了我们一次DMA缓冲区重用bug。
关键对比指标
在同等硬件平台(nRF52840)上,Rust Async的中断响应延迟并非都高于C RTOS。默认embassy-time基于SysTick时,延迟约1ms;但使用embassy-nrf的async GPIOTE时,中断到poll仅需12个CPU周期。RAM占用方面,C RTOS每任务需独立栈(通常1KB以上),而Rust Async任务共享栈,静态内存占用更低。
开发效率上,C RTOS需手动管理资源,容易引入悬空指针;Rust Async通过借用检查器防止此类错误,但学习曲线稍陡。社区生态方面,Zephyr和FreeRTOS设备驱动丰富,embassy也在快速增长。
实战场景建议
硬实时系统如电机控制、航空航天、医疗设备,通常选C RTOS,因为其确定性更可控。资源极度受限的老旧MCU(RAM < 16KB、Flash < 64KB)也倾向C RTOS,因为内核开销更小且稳定。
软实时IoT设备如传感器数据采集、智能家居网关,容忍毫秒级抖动,选Rust Async可提升开发效率。长期维护项目需要类型安全减少技术债,团队协作降低沟通成本,也适合Rust。新硬件平台如ARM Cortex-M33/RISC-V,希望利用现代工具链快速原型,Rust Async更有优势。
代码示例对比
C RTOS 任务写法(FreeRTOS风格)
void SensorTask(void *arg) {
while(1) {
read_sensor(&data);
send_to_cloud(&data);
vTaskDelay(pdMS_TO_TICKS(100)); // 假设configTICK_RATE_HZ=1000,即1ms/tick
}
}
Rust Async 等效实现
use embassy_time::{Timer, Duration};
#[embassy_executor::task]
async fn sensor_task() {
loop {
let data = read_sensor().await;
send_to_cloud(&data).await;
Timer::after(Duration::from_millis(100)).await;
}
}
混合架构的实践
现代嵌入式项目不必二选一。我们在nRF5340上试过:FreeRTOS跑在Application Core,embassy跑在Network Core,两个核用Mailbox通信。但发现Timer::after()在Network Core里不准——因为FreeRTOS占了SysTick,embassy只好切到RTC,结果精度掉到±5ms。
后来改用FreeRTOS的xTimerCreate + xTimerStart,再用cortex_m::asm::sev唤醒embassy任务,才稳住。这说明混合架构需要仔细处理时钟源和中断向量共享问题。
下一步实践建议
如果项目决定尝试Rust Async,建议从简单的UART轮询任务开始,逐步迁移到定时器驱动和中断处理。社区资源提供了完整的文档和示例工程,可快速上手。
本文首发于 我们用Rust Async跑在STM32H7上时,中断延迟到底涨了多少? — https://lyxq.com.cn/zh/blog/rust-async-c-rtos-selection
转载或引用请注明出处,商业使用请联系作者获得授权。