You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
}

方案优势

  1. 完全的分层隔离:状态机的所有规则(包括超时触发逻辑)都在逻辑crate中,固件层只负责执行具体操作,逻辑层可以脱离硬件单独测试(比如模拟发送AbortFlash命令,验证状态从Flashing转回Idle);
  2. 无冗余代码:新增其他定时器规则时,只需要在逻辑层添加对应的Effect和状态转换逻辑,固件层的调度器无需修改,自动支持新的定时器请求;
  3. 任务职责单一:core_task专注于状态机驱动,timer_scheduler_task专注于定时器管理,符合单一职责原则。

内容的提问来源于stack exchange,提问作者barak1412

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.10 06:04:50