星标公众号,让嵌入式知识 “投喂” 不停歇!
大家好,我是杂烩君。
上篇文章介绍了嵌入式状态机架构,一文讲透!状态机框架时,提到Zephyr SMF可以独立抽取使用。不少读者私信问:怎么把它从Zephyr RTOS里”抠”出来用在自己的裸机项目里?今天就手把手演示一遍。
1. Zephyr SMF
Zephyr状态机框架 (SMF) 是一个与应用程序无关的框架,它为开发人员提供了一种将状态机轻松集成到应用程序中的方法。Zephyr SMF的核心代码不到500行,依赖极少,非常适合独立抽取。
推荐阅读:
Zephyr SMF官方文档:https://docs./latest/services/smf/index.html
Zephyr SMF的优势
-
API极简:只需3个核心函数: smf_set_initial、smf_run_state、smf_set_state -
零依赖:不依赖RTOS,纯C标准实现 -
资源占用小:代码段约2KB,RAM占用 < 100字节/实例
2. SMF核心文件分析
2.1 SMF只需三个文件
从Zephyr仓库抽取的文件清单:
zephyr_smf/
├── smf.h # 头文件(约220行)
├── smf.c # 实现文件(约430行)
└── smf_port.h # 移植适配层(我们自己创建)
2.2 依赖关系
SMF的依赖非常克制:
// smf.c中的依赖
#include <zephyr/smf.h>
#include <zephyr/logging/log.h>
我们需要做的移植工作:
-
移除Zephyr的日志系统,改用 printf -
移除Zephyr的 CONFIG_*宏,用自定义宏替换 -
处理 <zephyr/sys/util.h>中的几个工具宏
3. 实战:命令解析器
下面我们实现一个完整的Zephyr SMF工程:基于Zephyr SMF的文本命令解析器状态机。
用Zephyr SMF实现一个简单的命令解析器,支持CMD或CMD:PARAM格式的解析。
本文demo,可在【嵌入式大杂烩】公众号后台关键字:cmd_parser_demo,即可获取。
3.1 Zephyr SMF抽取步骤
步骤1:创建项目结构
mkdir cmd_parser_demo && cd cmd_parser_demo
mkdir -p smf src
步骤2:复制SMF核心文件
从Zephyr仓库复制文件(路径相对于Zephyr根目录):
# 假设你已经克隆了Zephyr仓库
cp $ZEPHYR_BASE/lib/smf/smf.c smf/
cp $ZEPHYR_BASE/include/zephyr/smf.h smf/
步骤3:创建移植适配层
创建smf/smf_port.h,提供Zephyr SMF需要的宏定义:
步骤4:修改SMF源文件
编辑smf/smf.c,在文件开头添加:
#include "smf_port.h" // 添加这一行

#include "smf.h" // 原有的include改为相对路径
// 删除或注释掉:#include <zephyr/smf.h>
// 删除或注释掉:#include <zephyr/logging/log.h>
编辑smf/smf.h,移除Zephyr特定的include:
// 删除:#include <zephyr/sys/util.h>
// 删除:#include <zephyr/kernel.h>
#include "smf_port.h" // 添加这一行
用Zephyr SMF实现一个简单的命令解析器,支持CMD或CMD:PARAM格式。Zephyr SMF不依赖于平台,以下代码可以直接在PC上编译运行。
3.2 定义上下文结构
创建src/cmd_parser_smf.h:
上下文结构体的第一个成员必须是struct smf_ctx,这是Zephyr SMF的要求。其他成员根据实际需求定义。
状态机工作流程:
以GET:tempn为例,看看状态机如何工作:
-
IDLE状态 → 收到 G→ 进入CMD状态,开始收集命令 -
CMD状态 → 收到 E、T→ 继续收集命令字符 -
CMD状态 → 收到 :→ 进入PARAM状态,开始收集参数 -
PARAM状态 → 收到 t、e、m、p→ 继续收集参数字符 -
PARAM状态 → 收到 n→ 进入EXEC状态 -
EXEC状态 → 执行命令 → 返回IDLE状态等待下一条命令
3.3 实现状态机
创建src/cmd_parser_smf.c:
核心要点:
-
状态定义:用 SMF_CREATE_STATE宏定义每个状态的entry/run/exit函数 -
状态转换:用 smf_set_state()切换状态 -
事件驱动: parser_input()接收字符,触发smf_run_state()执行当前状态的run函数
3.4 测试程序
创建src/main.c:
以上操作得到工程结构:
编译运行:
本文demo,可在【嵌入式大杂烩】公众号后台关键字:cmd_parser_demo,即可获取。
总结
SMF的抽取过程比想象中简单:复制3个文件,稍微修改,就能在任何C项目中使用。相比手写状态机,SMF提供了:
-
结构化:状态定义、转换规则一目了然 -
可扩展:新增状态只需添加数组项 -
可调试:状态切换有明确的函数调用栈
QA
Q1:抽取的SMF可以用在商业项目吗?有没有License限制?
可以
可以。Zephyr SMF采用Apache-2.0许可证,这是一个非常宽松的开源协议:允许商业使用、允许修改和分发、不要求开源你的应用代码、需要保留原始版权声明和License文件。