Zephyr 提供了一个内置的状态机框架(SMF – State Machine Framework),位于 smf.h 和 smf.c。
核心数据结构
状态定义:struct smf_state
struct smf_state {
const state_execution entry; // 进入状态时调用
const state_execution run; // 状态运行时反复调用
const state_execution exit; // 退出状态时调用
const struct smf_state *parent; // 父状态(仅层次状态机)
};
状态机上下文:struct smf_ctx
struct smf_ctx {
const struct smf_state *current; // 当前状态
const struct smf_state *previous; // 上一个状态
int32_t terminate_val; // 终止值
uint32_t internal; // 内部标志
};
核心 API
| API | 说明 |
|---|---|
SMF_CREATE_STATE(entry, run, exit) |
创建扁平状态 |
SMF_CREATE_STATE(entry, run, exit, parent) |
创建层次状态 |
smf_set_initial(ctx, state) |
初始化并设置初始状态 |
smf_set_state(ctx, new_state) |
触发状态转换 |
smf_run_state(ctx) |
执行一次状态迭代 |
smf_set_terminate(ctx, val) |
终止状态机 |
两种模式
1. 扁平状态机(默认)
每个状态相互独立,无父子关系:
#include <zephyr/smf.h>
/* 定义状态枚举 */
enum my_state { STATE_A, STATE_B, STATE_C };
/* 用户对象(第一个成员必须是 smf_ctx)*/
static struct my_obj {
struct smf_ctx ctx; // 必须是第一个成员
int event;
} obj;
/* 状态处理函数 */
static void state_a_entry(void *o) { /* 进入 A */ }
static void state_a_run(void *o) {
struct my_obj *self = (struct my_obj *)o;
if (self->event == 1)
smf_set_state(SMF_CTX(o), &states[STATE_B]); // 转换到 B
}
static void state_a_exit(void *o) { /* 退出 A */ }
/* 状态表 */
static const struct smf_state states[] = {
[STATE_A] = SMF_CREATE_STATE(state_a_entry, state_a_run, state_a_exit),
[STATE_B] = SMF_CREATE_STATE(NULL, state_b_run, NULL),
[STATE_C] = SMF_CREATE_STATE(NULL, state_c_run, NULL),
};
int main(void) {
smf_set_initial(SMF_CTX(&obj), &states[STATE_A]); // 初始化
while (1) {
// 处理事件,更新 obj.event ...
int ret = smf_run_state(SMF_CTX(&obj)); // 运行一次迭代
if (ret) break; // 非零表示终止
}
}
2. 层次状态机(开启 CONFIG_SMF_ANCESTOR_SUPPORT)
子状态共享父状态的行为,父状态的 entry/exit 在进入/退出子状态时自动执行(同级切换时不重复执行):
// 父状态
static const struct smf_state states[] = {
[PARENT_AB] = SMF_CREATE_STATE(parent_entry, parent_run, parent_exit, NULL),
[STATE_A] = SMF_CREATE_STATE(a_entry, a_run, a_exit, &states[PARENT_AB]),
[STATE_B] = SMF_CREATE_STATE(b_entry, b_run, b_exit, &states[PARENT_AB]),
[STATE_C] = SMF_CREATE_STATE(c_entry, c_run, c_exit, NULL), // 无父状态
};
层次状态机中执行顺序规则:
- entry:父状态先于子状态执行
- run:子状态先执行,若无状态转换则父状态再执行
- exit:子状态先于父状态执行
- 同级切换(A→B,共享 PARENT_AB):父状态的 exit/entry 不重新执行
状态转换流程
smf_run_state() 调用 current->run()
│
└─ 内部调用 smf_set_state(ctx, new_state)
│
├─ 执行 current->exit()
├─(层次模式)执行祖先 exit()
├─ 更新 ctx->current = new_state
├─(层次模式)执行祖先 entry()
└─ 执行 new_state->entry()
Kconfig 选项
在 prj.conf 中启用:
CONFIG_SMF=y
CONFIG_SMF_ANCESTOR_SUPPORT=y # 启用层次状态机(可选)
测试用例在 test_lib_flat_smf.c 和 test_lib_hierarchical_smf.c 中有完整示例,可参考学习。
SMF 对 Zephyr 的依赖非常少,移植到 FreeRTOS 主要需要处理以下几个方面:
依赖分析
smf.h 中的 Zephyr 依赖
| 依赖项 | 说明 | FreeRTOS 替换方案 |
|---|---|---|
#include <zephyr/kernel.h> |
提供 int32_t、uint32_t 等类型 |
替换为 #include <stdint.h> + #include <stdbool.h> |
CONFIG_SMF_ANCESTOR_SUPPORT |
Kconfig 编译开关 | 改用普通 #define SMF_ANCESTOR_SUPPORT |
头文件保护宏 ZEPHYR_INCLUDE_SMF_H_ |
命名规范 | 改为 SMF_H_ |
smf.c 中的 Zephyr 依赖
| 依赖项 | 说明 | FreeRTOS 替换方案 |
|---|---|---|
#include smf.h> |
头文件路径 | 改为 #include "smf.h" |
#include <zephyr/logging/log.h> |
Zephyr 日志系统 | 删除,或自定义宏 |
LOG_MODULE_REGISTER(smf) |
注册日志模块 | 删除 |
LOG_WRN("...") |
日志警告输出 | 替换为 printf 或自定义宏 |
IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT) |
Kconfig 运行时检查宏 | 替换为 #ifdef SMF_ANCESTOR_SUPPORT 判断 |
__unused |
Zephyr 编译器属性宏 | 替换为 __attribute__((unused)) 或删除 |
具体修改步骤
第一步:新建 smf.h
#ifndef SMF_H_
#define SMF_H_
#include <stdint.h>
#include <stdbool.h>
/* 开启层次状态机支持,不需要则注释掉 */
#define SMF_ANCESTOR_SUPPORT
#ifdef SMF_ANCESTOR_SUPPORT
#define SMF_CREATE_STATE(_entry, _run, _exit, _parent) \
{ \
.entry = _entry, \
.run = _run, \
.exit = _exit, \
.parent = _parent \
}
#else
#define SMF_CREATE_STATE(_entry, _run, _exit) \
{ \
.entry = _entry, \
.run = _run, \
.exit = _exit \
}
#endif
#define SMF_CTX(o) ((struct smf_ctx *)o)
typedef void (*state_execution)(void *obj);
struct smf_state {
const state_execution entry;
const state_execution run;
const state_execution exit;
#ifdef SMF_ANCESTOR_SUPPORT
const struct smf_state *parent;
#endif
};
struct smf_ctx {
const struct smf_state *current;
const struct smf_state *previous;
int32_t terminate_val;
uint32_t internal;
};
void smf_set_initial(struct smf_ctx *ctx, const struct smf_state *init_state);
void smf_set_state(struct smf_ctx *ctx, const struct smf_state *new_state);
void smf_set_terminate(struct smf_ctx *ctx, int32_t val);
int32_t smf_run_state(struct smf_ctx *ctx);
#endif /* SMF_H_ */
第二步:修改 smf.c
// 原来
#include <zephyr/smf.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(smf);
// 替换为
#include "smf.h"
#include <stdio.h> // 用于 printf(可选)
// 原来
LOG_WRN("Calling %s from exit action", __func__);
// 替换为(或直接删掉这行)
printf("[SMF WRN] Calling %s from exit action\n", __func__);
// 原来
__unused static bool smf_execute_ancestor_entry_actions(...)
__unused static bool smf_execute_ancestor_run_actions(...)
__unused static bool smf_execute_ancestor_exit_actions(...)
// 替换为(GCC 通用属性)
__attribute__((unused)) static bool smf_execute_ancestor_entry_actions(...)
// 原来
if (IS_ENABLED(CONFIG_SMF_ANCESTOR_SUPPORT)) { ... }
// 替换为
#ifdef SMF_ANCESTOR_SUPPORT
// ... 相关代码
#endif
总结
SMF 的核心逻辑(smf.c 中约 286 行)完全不依赖 FreeRTOS 任何 API,它只是一个纯 C 的状态机调度框架,没有涉及任务、队列、信号量等 RTOS 原语。
移植工作量非常小,归纳为 4 件事:
- 将
#include <zephyr/kernel.h>改为<stdint.h>+<stdbool.h> - 将
CONFIG_SMF_ANCESTOR_SUPPORT改为普通#define,并将IS_ENABLED(...)改为#ifdef - 删除 Zephyr 日志相关代码(
LOG_MODULE_REGISTER、LOG_WRN) - 将
__unused改为__attribute__((unused))或直接删除
