Rust嵌入式项目中如何将Embassy与业务逻辑解耦?
针对嵌入式状态机超时逻辑的优化方案
先明确核心诉求:分离状态机逻辑与固件实现,保证逻辑可测试性,同时避免超时处理的代码冗余。先拆解你给出的两个选项的问题,再给出更合理的实现方式。
选项1的问题
把超时逻辑直接写在core_task里,等于把状态机的核心规则(闪烁5秒后自动中止)耦合到了固件任务代码中。这会导致:
- 逻辑crate无法单独测试超时触发后的状态转换,因为这部分逻辑不在状态机里;
- 后续新增其他定时器规则时,
core_task会变得臃肿,所有超时判断都要堆在这里。
选项2的优势与可优化点
让控制器返回Effect的思路是对的——逻辑层只定义"需要做什么",固件层负责"怎么实现",这完全符合分层设计和可测试性的要求。你担心的"多定时器导致代码冗余",可以通过封装定时器调度逻辑解决,不用让任务直接处理细节。
优化后的实现方案
1. 逻辑层:定义Effect与状态机
在逻辑crate中,让handle返回的Effect包含"调度定时器"的指令,明确定时器到期后要触发的命令:
// 逻辑层 controller.rs pub enum Command { Flash, AbortFlash, } pub enum Effect { StartFlashing, StopFlashing, ScheduleTimer { delay: Duration, on_expire: Command, // 定时器到期后要发送的命令 }, } pub struct Controller { state: State, } enum State { Idle, Flashing, } impl Controller { pub fn handle(&mut self, cmd: Command) -> Vec<Effect> { match (&self.state, cmd) { (State::Idle, Command::Flash) => { self.state = State::Flashing; vec![ Effect::StartFlashing, Effect::ScheduleTimer { delay: Duration::from_secs(5), on_expire: Command::AbortFlash, }, ] } (State::Flashing, Command::AbortFlash) => { self.state = State::Idle; vec![Effect::StopFlashing] } // 处理其他边界情况 _ => vec![], } } }
2. 固件层:封装定时器调度与任务执行
固件层拆分出两个独立任务,各司其职:
core_task:负责接收命令、驱动状态机、执行非定时器类Effect;timer_scheduler_task:专门处理定时器调度,到期后发送对应命令到命令通道。
// 固件层 main.rs use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use futures::channel::mpsc; // 全局命令通道 static mut COMMANDS: mpsc::Receiver<Command> = ...; // 定时器请求通道 static mut TIMER_REQUESTS: mpsc::Sender<TimerRequest> = ...; struct TimerRequest { delay: Duration, cmd: Command, } #[embassy_executor::task] async fn core_task(mut controller: Controller, mut timer_tx: mpsc::Sender<TimerRequest>) { loop { let cmd = unsafe { COMMANDS.receive().await.unwrap() }; let effects = controller.handle(cmd); for effect in effects { match effect { Effect::StartFlashing => { // 控制LED开始闪烁的硬件操作 led.set_high(); } Effect::StopFlashing => { // 控制LED停止闪烁的硬件操作 led.set_low(); } Effect::ScheduleTimer { delay, on_expire } => { // 将定时器请求转发给调度器 timer_tx.send(TimerRequest { delay, cmd: on_expire }).await.unwrap(); } } } } } #[embassy_executor::task] async fn timer_scheduler_task(mut timer_rx: mpsc::Receiver<TimerRequest>) { loop { let req = timer_rx.receive().await.unwrap(); // 等待定时器到期,发送对应命令到全局通道 Timer::after(req.delay).await; unsafe { COMMANDS.send(req.cmd).await.unwrap() }; } } #[embassy_executor::main] async fn main(spawner: Spawner) { // 初始化通道、控制器、硬件资源 spawner.spawn(core_task(controller, timer_tx)).unwrap(); spawner.spawn(timer_scheduler_task(timer_rx)).unwrap(); }
方案优势
- 完全的分层隔离:状态机的所有规则(包括超时触发逻辑)都在逻辑crate中,固件层只负责执行具体操作,逻辑层可以脱离硬件单独测试(比如模拟发送
AbortFlash命令,验证状态从Flashing转回Idle); - 无冗余代码:新增其他定时器规则时,只需要在逻辑层添加对应的
Effect和状态转换逻辑,固件层的调度器无需修改,自动支持新的定时器请求; - 任务职责单一:
core_task专注于状态机驱动,timer_scheduler_task专注于定时器管理,符合单一职责原则。
内容的提问来源于stack exchange,提问作者barak1412
相关产品推荐
相关产品推荐

