C语言状态机演示:开关机控制

在嵌入式系统、游戏开发和协议解析等领域,状态机是一种高效且常用的设计模式。它通过将系统的行为划分为不同的状态,并根据输入事件在这些状态之间进行转换,简化了复杂系统的设计和维护。本文将介绍一个由 happyDom 开发的 C 语言状态机框架,并演示如何使用它来构建一个简单的状态机。

框架概述

happyDom/stateMachineC 是一个轻量级的 C 语言状态机框架,适用于嵌入式环境。它提供了定义状态、事件和状态转换的功能,支持有限状态机(FSM)的创建和管理。该框架的主要特点包括:

  • 简洁易用:直观的 API 设计,便于集成和使用。

  • 高性能:经过优化,适合资源受限的嵌入式环境。

  • 可扩展性:支持动态添加状态和事件,灵活应对复杂场景。

快速入门

  1. 设计状态跳转图
    在本示例中,我们实现一个简单的开关机控制状态机。状态机的设计如下图所示:
    Snipaste_2025-03-29_11-23-03
  2. 克隆仓库
    首先,使用 Git 克隆状态机框架的源代码
git clone git@gitee.com:DyyYq/stateMachineC.git
  1. 包含头文件
    在项目中包含状态机框架的头文件:
#include "stateMachine.h"
  1. 定义状态
    定义枚举变量表示状态机的各个状态。在此示例中,我们设定了以下 4 个状态:
  • stPreOff:预关机状态

  • stOff:关机状态

  • stPreOn:预开机状态

  • stOn:开机状态

enum myStates {
    stPreOff = 0,   // 预关机状态
    stOff,          // 关机状态
    stPreOn,        // 预开机状态
    stOn,           // 开机状态

    stCnt           // 状态机的状态数量
};
  1. 定义跳转事件
    状态机根据事件进行状态转换。在本示例中,我们只定义一个事件,即用户按下按键 s 时触发状态转换。事件处理函数如下:
// 状态机跳转事件:检测是否按下 's' 键
smEventResult_t keyPressed(smUnit_t *pSt) {
    if (pSt->roundCounter > 3 && _kbhit()) {
        if ('s' == _getch()) {
            return go;
        }
    }
    return aWait;
}

为了判断预开机和预关机动作是否完成,我们添加了另一个事件判定:

// 判定预开机和预关机动作是否完成,简化处理为 3 秒后动作完成
smEventResult_t preActionCmplt(smUnit_t *pSt) {
    if (pSt->roundCounter >= 3) {
        return go;
    }
    return aWait;
}
  1. 定义状态动作
    为每个状态定义动作。每个状态的具体行为如下:
  • stOff 状态时,不执行任何操作。

  • stPreOn 状态时,打印“加载用户配置中”信息。

  • stPreOn 状态下,开机过程持续 3 秒。

  • stOn 状态时,持续显示开机时长。

  • stPreOff 状态时,打印“保存用户配置中”信息。

  • stPreOff 状态下,关机过程持续 3 秒。

// stPreOff 状态的 Entry 动作
void actionEntry_preOff(smUnit_t *pSt) {
    printf("保存用户配置中,请稍候!\n");
}

// stPreOff 状态的 Do 动作
void actionDo_preOff(smUnit_t *pSt) {
    if (pSt->roundCounter < 3) {
        printf("%d\n", 3 - pSt->roundCounter);
    } else if (pSt->roundCounter == 3) {
        printf("用户配置保存完成\n");
    }
}

// stPreOn 状态的 Entry 动作
void actionEntry_preOn(smUnit_t *pSt) {
    printf("加载用户配置中,请稍候!\n");
}

// stPreOn 状态的 Do 动作
void actionDo_preOn(smUnit_t *pSt) {
    if (pSt->roundCounter < 3) {
        printf("%d\n", 3 - pSt->roundCounter);
    } else if (pSt->roundCounter == 3) {
        printf("用户配置加载完成\n");
    }
}

// stOn 状态的 Do 动作
void actionDo_on(smUnit_t *pSt) {
    printf("已经开机 %ds\n", pSt->roundCounter);
}
  1. 初始化状态机并注册事件
    定义并初始化状态机变量,将状态动作和跳转事件注册到状态机中。最终,通过每秒轮询运行状态机。
int main(int argc, char const *argv[]) {
    // 定义状态机变量
    stateMachine_t demoSM;

    // 初始化状态机,指定状态数量,指定默认状态
    fsm_init(&demoSM, stCnt, stOff);

    // 为各状态注册状态动作
    demoSM.actionSignUp(&demoSM, stPreOff, actionEntry_preOff, actionDo_preOff, NULL);
    demoSM.actionSignUp(&demoSM, stPreOn, actionEntry_preOn, actionDo_preOn, NULL);
    demoSM.actionSignUp(&demoSM, stOn, NULL, actionDo_on, NULL);

    // 注册状态跳转事件
    demoSM.eventSingUp(&demoSM, stOff, stPreOn, keyPressed);
    demoSM.eventSingUp(&demoSM, stPreOn, stOn, preActionCmplt);
    demoSM.eventSingUp(&demoSM, stOn, stPreOff, keyPressed);
    demoSM.eventSingUp(&demoSM, stPreOff, stOff, preActionCmplt);

    // 运行状态机
    while (1) {
        demoSM.run(&demoSM);
        Sleep(1000); // 每秒钟轮询一次
    }
}

运行效果

以下是状态机运行时的效果示意:
20250329142854_rec_

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

团圆吧

1 分钱,求鼓励。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值