# efsm **Repository Path**: mengplus/efsm ## Basic Information - **Project Name**: efsm - **Description**: 这个项目提供了一个用于实现有限状态机(Finite State Machine, FSM)的简单框架,用于管理状态和处理事件。该框架支持状态的初始化、退出、周期性任务执行,以及状态切换和事件处理。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2024-01-31 - **Last Updated**: 2026-06-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # State Machine Framework (EFSM) ## 项目简介 EFSM(Event-based Finite State Machine,基于事件的有限状态机)是一个轻量级、灵活的嵌入式系统状态机框架。 **核心特性:** - 事件驱动:基于事件触发状态切换 - 模块化设计:支持自定义状态和步骤 - 步骤管理:支持线性执行、时间限制、条件跳转 - 轻量级:代码体积约 700 字节 --- ## API 参考 ### 状态机管理 (efsm_manage_t) | 函数 | 说明 | |------|------| | `efsm_manage_init(obj)` | 初始化状态机结构体(清零) | | `efsm_register(obj)` | 注册到全局链表,自动调用 `ops->init` | | `efsm_remove(obj)` | 从链表移除,自动调用 `ops->exit` | | `efsm_manage_tick()` | 遍历所有注册状态机,调用 `ops->tick` | | `efsm_manage_tick_user(obj)` | 手动触发单个状态机的 tick | **注意:** `efsm_manage_init` 会清零整个结构体,包括 `user_data`。如需保留 `user_data`,请在 init 后重新设置。 ### 状态切换 | 函数 | 说明 | |------|------| | `efsm_transition(obj, next_state)` | 切换到新状态(自动调用旧状态 exit 和新状态 init) | | `efsm_manage_get_state(obj)` | 获取当前状态指针 | | `efsm_manage_get_userdata(obj)` | 获取用户数据 | **hold_on 机制:** 设置 `obj->hold_on = 1` 后,`efsm_transition` 将被阻止。 ### 事件处理 | 函数 | 说明 | |------|------| | `efsm_event_process(obj, cmd, param)` | 处理事件(系统命令 0x00-0xFF,用户命令 0x100+) | | `efsm_event_broadcast(cmd, param)` | 广播事件到所有注册状态机 | | `efsm_manage_control(obj, cmd, param)` | 调用状态机的 control 回调 | **系统命令:** - `EFSM_CMD_STOP` - 停止 tick 执行(stop=1) - `EFSM_CMD_START` - 恢复 tick 执行(stop=0) - `EFSM_CMD_HOLD` - 设置 hold_on 标志 ### 步骤系统 (efsm_step_t) 步骤系统用于在状态内部线性执行多个操作。 | 宏 | 说明 | |----|------| | `EFSM_STEP_DEFAULT(name, type, set, check)` | 基础步骤定义 | | `EFSM_STEP_TIMED_DEFAULT(name, set, check, min, max)` | 带时间限制的步骤 | | `EFSM_STEP_CONDITIONAL_DEFAULT(name, set, check, true, false)` | 带条件跳转的步骤 | | `EFSM_STEP_ADDR(step)` | 获取步骤指针(用于 step_vec 数组) | **步骤类型:** - `EFSM_STEP_BASE` - 基础步骤,check 返回 true 时前进 - `EFSM_STEP_TIMED` - 时间限制步骤,支持 min/max 时间约束 - `EFSM_STEP_CONDITIONAL` - 条件跳转步骤,根据 check 结果跳转 - `EFSM_STEP_WARNING` - 保留(异常处理) - `EFSM_STEP_CUSTOM` - 保留(自定义) **步骤执行函数:** - `efsm_step_init(ss, vec, num, tick)` - 初始化步骤状态 - `efsm_step_process(ss, tick)` - 执行当前步骤,返回 true 继续,false 表示完成 --- ## 使用示例 ### 示例 1:基本状态切换 ```c #include "efsm.h" /* 定义状态 */ static void state_a_init(efsm_state_t *obj) { printf("Enter A\n"); } static void state_a_exit(efsm_state_t *obj) { printf("Exit A\n"); } static void state_a_action(efsm_state_t *obj, uint32_t cmd, efsm_param_t *param) { if (cmd == 0x100) efsm_transition(obj->parent, &state_b); } static const efsm_state_ops_t ops_a = { .name = "A", .init = state_a_init, .exit = state_a_exit, .action = state_a_action, }; efsm_state_t state_a = {.ops = &ops_a}; static void state_b_action(efsm_state_t *obj, uint32_t cmd, efsm_param_t *param) { if (cmd == 0x101) efsm_transition(obj->parent, &state_a); } static const efsm_state_ops_t ops_b = { .name = "B", .action = state_b_action, }; efsm_state_t state_b = {.ops = &ops_b}; /* 使用状态机 */ efsm_manage_t fsm; efsm_manage_init(&fsm); efsm_register(&fsm); efsm_transition(&fsm, &state_a); efsm_event_process(&fsm, 0x100, NULL); // 切换到 B ``` ### 示例 2:步骤管理(servo-F401 风格) ```c /* 步骤定义 */ static bool step_cfg_set(efsm_state_step_t *self) { /* 下发配置 */ return true; } static bool step_cfg_check(efsm_state_step_t *self) { return config_done(); } static bool step_start_set(efsm_state_step_t *self) { /* 发送启动 */ return true; } static bool step_start_check(efsm_state_step_t *self) { return start_done(); } /* 步骤数组 */ static efsm_step_t g_steps[] = { EFSM_STEP_DEFAULT("cfg", EFSM_STEP_BASE, step_cfg_set, step_cfg_check), EFSM_STEP_DEFAULT("start", EFSM_STEP_BASE, step_start_set, step_start_check), }; static const_efsm_step_t g_step_vec[] = { EFSM_STEP_ADDR(g_steps[0]), EFSM_STEP_ADDR(g_steps[1]), }; /* 状态处理 */ static void start_action(efsm_state_t *obj, uint32_t cmd, efsm_param_t *param) { efsm_state_step_t *ss = (efsm_state_step_t *)obj; // 嵌套结构体 if (!efsm_step_process(ss, rt_tick_get_millisecond())) { /* 所有步骤完成,切换到下一个状态 */ efsm_transition(obj->parent, &state_running); } } ``` ### 示例 3:定时步骤 ```c static bool wait_check(efsm_state_step_t *self) { /* 等待 1000ms 后返回 true */ uint32_t elapsed = rt_tick_get_millisecond() - self->state.timestamp; return elapsed >= 1000; } static efsm_step_timed_t g_wait_step = EFSM_STEP_TIMED_DEFAULT( "wait", NULL, wait_check, 0, 2000 /* 最多等 2s */ ); static const_efsm_step_t g_vec[] = { EFSM_STEP_ADDR(g_wait_step), }; ``` ### 示例 4:条件跳转 ```c static bool check_condition(efsm_state_step_t *self) { return some_condition_is_true(); } static efsm_step_conditional_t g_cond_step = EFSM_STEP_CONDITIONAL_DEFAULT( "check", NULL, check_condition, 2, 1 /* 条件真跳 2 步,假跳 1 步 */ ); static const_efsm_step_t g_vec[] = { &g_step1, EFSM_STEP_ADDR(g_cond_step), &g_step3, }; ``` --- ## 常见问题 ### Q: `efsm_manage_init` 会清掉 `user_data` 吗? A: 是的。`efsm_manage_init` 使用 `memset` 清零整个结构体。如果需要保留 `user_data`,请在 init 后重新设置: ```c fsm.user_data = &my_data; efsm_manage_init(&fsm); fsm.user_data = &my_data; // 重新设置 ``` ### Q: `efsm_register` 的 `ops->init` 是什么? A: `efsm_register` 会调用 `obj->ops->init(obj)`,这是**状态机管理器**的初始化回调(`efsm_manage_ops_t`),不是**状态**的初始化回调(`efsm_state_ops_t`)。 - `obj->ops->init` - 状态机管理器初始化(可选) - `obj->pstate->ops->init` - 当前状态的进入回调 ### Q: `efsm_step_process` 返回 false 是什么含义? A: 表示所有步骤执行完毕,或者步骤索引超出范围。在状态机中,通常用 `!efsm_step_process(...)` 判断步骤完成,然后切换到下一个状态。 ### Q: 如何在状态中访问用户数据? A: 通过 `obj->parent->user_data`: ```c static void state_action(efsm_state_t *obj, uint32_t cmd, efsm_param_t *param) { MyData *data = (MyData *)obj->parent->user_data; // 使用 data } ``` --- ## 资源占用 ``` .text (代码): ~700 字节 - efsm.c: ~400 字节 - efsm_step.c: ~300 字节 ``` --- ## 许可证 MIT License --- **日期**: 2026-06-07