将Zephyr 中的 SMF(状态机) 移植到FreeRTOS

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_tuint32_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 件事

  1. #include <zephyr/kernel.h> 改为 <stdint.h> + <stdbool.h>
  2. CONFIG_SMF_ANCESTOR_SUPPORT 改为普通 #define,并将 IS_ENABLED(...) 改为 #ifdef
  3. 删除 Zephyr 日志相关代码(LOG_MODULE_REGISTERLOG_WRN
  4. __unused 改为 __attribute__((unused)) 或直接删除