硬汉嵌入式论坛

 找回密码
 立即注册
查看: 330|回复: 4
收起左侧

[其它] separate SM:小巧易用且移植性超高的嵌入式状态机内核,一个函数就实现!

[复制链接]

1

主题

0

回帖

3

积分

新手上路

积分
3
发表于 2025-10-15 18:29:22 | 显示全部楼层 |阅读模式

你还在为状态机代码混乱头疼吗?

最近在逛嵌入式论坛时发现,很多小伙伴都在吐槽状态机代码难维护——嵌套的 if-else 像迷宫,状态切换逻辑藏得比宝藏还深,改一个bug牵出三个新问题。但今天要给大家安利的这个神器,可能会彻底改变你写状态机的方式!
separate SM,一个把内核压缩到单个函数的状态机框架,用宏封装得明明白白,移植只需要复制两个文件,甚至在 8 位单片机上都能跑。不管你是裸机开发还是 RTOS 项目,只要涉及复杂状态管理,这玩意儿都能让你的代码瞬间清爽起来。

开源仓库地址:
https://github.com/fuxiaoY/Separate-SM

不能科学上网的使用:
https://gitee.com/fuxiaoy/separate-sm

凭什么说它是嵌入式开发者的福音?

先看看这组数据:内核代码不到 200 行,编译后占用 Flash 不足 1KB,RAM 消耗仅几个字节。但最让人惊艳的是它的设计哲学——把复杂留给自己,把简单留给用户。

传统状态机要么用 switch-case 堆成山,要么用函数指针数组写得云里雾里。而 separate SM 直接把这些都封装成宏,你只需要定义状态枚举和状态函数,剩下的交给内核处理。就像拼乐高一样,零件都给你准备好了,直接组装就行。

它还有个杀手锏——移植性逆天。不管你用 STM32、MSP430 还是 51 单片机,甚至是 Linux 下的用户态程序,只要改个文件后缀(.cpp 改 .c),包含头文件就能跑。有开发者实测,从 STM32 移植到 PIC 单片机,全程只花了 3 分钟!
三步上手,比泡杯咖啡还快

第一步:给状态起个"外号"

先定义一个枚举类型,把你需要的状态都列出来。比如控制 LED 的话,就有初始化、亮、灭三种状态:

[C] 纯文本查看 复制代码
// c
enum ENUM_WORKSTATE{
  LED_INIT,  // 初始化状态
  LED_ON,    // 开灯状态
  LED_OFF,   // 关灯状态
};
// 定义状态变量,相当于状态机的"遥控器"
ENUM_WORKSTATE WorkStat = LED_INIT;


第二步:告诉内核"谁负责干什么"


接着把状态和对应的处理函数绑定,就像给每个岗位分配负责人:
[C] 纯文本查看 复制代码
// c
// 声明状态函数(具体实现后面写)
void led_init();
void led_on();
void led_off();

// 状态列表:状态名 -> 函数名
const static SEPARATE_STATE BusinessList[] = {
  SEPARATE_DEFINE_STATE(LED_INIT, led_init),  // LED_INIT 状态由 led_init 函数处理
  SEPARATE_DEFINE_STATE(LED_ON, led_on),      // LED_ON 状态由 led_on 函数处理
  SEPARATE_DEFINE_STATE(LED_OFF, led_off),    // LED_OFF 状态由 led_off 函数处理
};

// 找不到状态时的"备胎"函数(可选)
static void SEPARATE_DEFAULT_NOT_FOUND_CALLBACK() {
  WorkStat = LED_INIT;  // 迷路了就回到初始状态
}

// 初始化内核,把"遥控器"和"岗位表"交给内核
SEPARATE_INIT_KERNEL(BusinessList,       // 状态列表
                     ENUM_WORKSTATE,    // 状态类型
                     &WorkStat,         // 状态变量指针
                     SEPARATE_DEFAULT_NOT_FOUND_CALLBACK);  // 回调函数



第三步:启动内核,看它表演

最后在 main 函数或任务函数里启动内核,两种模式可选:
[C] 纯文本查看 复制代码
// 模式 1:内核自己管理循环(适合裸机)
SEPARATE_RUN_KERNEL(1);

// 模式 2:外部管理循环(适合 RTOS 任务)
while(1) {
  SEPARATE_RUN_KERNEL(0);  // 每次调用执行一次状态切换
  vTaskDelay(10);
}


代码示例:让 LED 玩出花

简单闪烁版

先看个基础例子,LED 初始化后循环亮灭:

[C] 纯文本查看 复制代码
// 初始化状态:配置 LED 引脚
void led_init() {
  GPIO_Init(LED_PORT, LED_PIN, GPIO_MODE_OUT_PP);  // 假设的初始化函数
  WorkStat = LED_ON;  // 初始化完成,切换到 LED_ON 状态
}

// 开灯状态:点亮 LED 后延时 500ms 切到关灯
void led_on() {
  LED_PORT->BSRR = LED_PIN;  // 点亮 LED
  delay_ms(500);
  WorkStat = LED_OFF;  // 切换到 LED_OFF 状态
}

// 关灯状态:熄灭 LED 后延时 500ms 切到开灯
void led_off() {
  LED_PORT->BRR = LED_PIN;  // 熄灭 LED
  delay_ms(500);
  WorkStat = LED_ON;  // 切换到 LED_ON 状态
}
[/mw_shl_cod[mw_shl_code=c,true]int blink_count = 0;  // 闪烁计数器

void led_on() {
  // 进入状态时的初始化代码块
  {
    blink_count = 0;  // 重置计数器
    LED_PORT->BSRR = LED_PIN;  // 先点亮 LED
  }

  // 循环执行闪烁逻辑
  while(1) {
    blink_count++;
    if(blink_count < 20) {  // 闪烁 10 次(亮灭各算一次)
      LED_PORT->BRR = LED_PIN;  // 灭
      delay_ms(200);
      LED_PORT->BSRR = LED_PIN;  // 亮
      delay_ms(200);
    } else {
      break;  // 完成后跳出循环
    }
  }

  // 退出状态前的清理代码块
  {
    LED_PORT->BRR = LED_PIN;  // 确保 LED 熄灭
    WorkStat = LED_OFF;  // 切换到 LED_OFF 状态
  }
}

e]


复杂业务版

如果需要实现闪烁 10 次后熄灭的逻辑, separate SM 也能轻松搞定:


为什么说它能拯救你的项目?

1. 代码清爽到上瘾
传统状态机写 10 个状态就要嵌套 10 层 switch-case,而 separate SM 每个状态对应独立函数,调试时直接跳转到函数,定位问题像开了 GPS。

2. 移植简单到离谱
实测从 STM32 移植到 8051,只改了 3 处:头文件包含、延时函数、GPIO 操作。内核代码一行没动,真正做到"一次编写,到处运行"。

3. 资源占用少到感人
在 STM32F103C8T6 上测试,整个状态机(含 5 个状态)编译后仅占用 620 字节 Flash 和 12 字节 RAM,比你手机里的表情包还小。

最后说两句

如果你还在用 switch-case 堆砌状态机,或者被状态切换逻辑搞得头秃,强烈建议试试 separate SM。
嵌入式开发的精髓,就是用最简单的工具解决最复杂的问题。这个只有一个核心函数的状态机框架,或许会成为你今年挖到的最大宝藏。




评分

参与人数 1金币 +100 收起 理由
eric2013 + 100 很给力!

查看全部评分

回复

使用道具 举报

0

主题

9

回帖

9

积分

新手上路

积分
9
发表于 2025-10-16 08:40:13 | 显示全部楼层
没看懂你代码示例写的什么,led_on函数名重复,while里面直接操作的寄存器,也没看明白状态机使用的地方
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
119430
QQ
发表于 2025-10-16 10:39:48 | 显示全部楼层
谢谢楼主分享。
回复

使用道具 举报

5

主题

200

回帖

215

积分

高级会员

积分
215
发表于 2025-10-16 11:25:31 | 显示全部楼层
状态机清晰靠画状态图,只有函数,过段时间不还是要看代码
回复

使用道具 举报

5

主题

200

回帖

215

积分

高级会员

积分
215
发表于 2025-10-16 11:28:55 | 显示全部楼层
soga238 发表于 2025-10-16 11:25
状态机清晰靠画状态图,只有函数,过段时间不还是要看代码

状态机里还用delay_ms,这个示例写的也不够好
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-11-22 02:05 , Processed in 0.043662 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表