星标公众号,让嵌入式知识 “投喂” 不停歇!

大家好,我是杂烩君。

上篇文章介绍了嵌入式状态机架构,一文讲透!状态机框架时,提到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_initialsmf_run_statesmf_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> 

我们需要做的移植工作:

  1. 移除Zephyr的日志系统,改用printf
  2. 移除Zephyr的CONFIG_*宏,用自定义宏替换
  3. 处理<zephyr/sys/util.h>中的几个工具宏

3. 实战:命令解析器

下面我们实现一个完整的Zephyr SMF工程:基于Zephyr SMF的文本命令解析器状态机。

用Zephyr SMF实现一个简单的命令解析器,支持CMDCMD: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"  // 添加这一行
Zephyr SMF实战:几百行代码实现轻量状态机!

#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实现一个简单的命令解析器,支持CMDCMD:PARAM格式。Zephyr SMF不依赖于平台,以下代码可以直接在PC上编译运行。

3.2 定义上下文结构

创建src/cmd_parser_smf.h

上下文结构体的第一个成员必须是struct smf_ctx,这是Zephyr SMF的要求。其他成员根据实际需求定义。

状态机工作流程

GET:tempn为例,看看状态机如何工作:

  1. IDLE状态 → 收到G → 进入CMD状态,开始收集命令
  2. CMD状态 → 收到ET → 继续收集命令字符
  3. CMD状态 → 收到: → 进入PARAM状态,开始收集参数
  4. PARAM状态 → 收到temp → 继续收集参数字符
  5. PARAM状态 → 收到n → 进入EXEC状态
  6. 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提供了:

  1. 结构化:状态定义、转换规则一目了然
  2. 可扩展:新增状态只需添加数组项
  3. 可调试:状态切换有明确的函数调用栈

QA

Q1:抽取的SMF可以用在商业项目吗?有没有License限制?

可以

可以。Zephyr SMF采用Apache-2.0许可证,这是一个非常宽松的开源协议:允许商业使用、允许修改和分发、不要求开源你的应用代码、需要保留原始版权声明和License文件。