前言

对于之前的一篇博客《C语言实现状态机》(链接:https://blog.csdn.net/qq_36969264/article/details/105865099?spm=1001.2014.3001.5501),看网友的留言,似乎对pFsm->stNextState 和 EVENT_MAP_END的比较有一些误解。
这里需要解释一下,当时这么写是为了省事,但后面看这么写确实很容易让人误解,所以这篇文章的目的是为了修改掉这个容易让人误解的地方,同时对该状态机做了一些优化。

一、实现状态机的基本函数

1、定义相关结构体

typedef void (*STATE_ACTION)(void);  typedef struct ACTION_MAP
{uint8_t        stStateID;STATE_ACTION  EnterAct;   STATE_ACTION    RunningAct; STATE_ACTION    ExitAct;
}ACTION_MAP_t; /* 动作action表描述 */typedef struct EVENT_MAP
{uint8_t    stEventID;uint8_t stCurState;uint8_t stNextState;
}EVENT_MAP_t; /* 事件event表描述 */typedef struct FSM
{uint8_t u8Flag;            /* 状态切换标志位,1表示要进行状态切换 */uint8_t u8EventSum;         /* 事件event总数 */uint8_t u8ActSum;            /* 动作action总数 */uint8_t stCurState;uint8_t stNextState;ACTION_MAP_t *pActionMap;EVENT_MAP_t *pEventMap;
}FSM_t; /* 状态机控制结构 */

注:状态机控制结构FSM_t的元素u8Flag用于替代pFsm->stNextState 和 EVENT_MAP_END的比较,以实现相同的功能。

2、状态机初始化函数

初始化函数主要目的是为了将状态机与事件表、动作表关联起来。

void fsm_init(FSM_t* pFsm,EVENT_MAP_t* pEventMap,ACTION_MAP_t *pActionMap,uint8_t u8EventSum,uint8_t u8ActSum,uint8_t curState)
{pFsm->u8Flag = 0;pFsm->stNextState = 0;pFsm->u8EventSum = u8EventSum;pFsm->u8ActSum = u8ActSum;pFsm->stCurState = curState;pFsm->pEventMap = pEventMap;pFsm->pActionMap = pActionMap;
}

3、状态机转换函数

该函数的主要功能是通过查找事件表,以确定是否要进行状态切换,如果需要进行状态切换,则将标志位u8Flag 置1。

void fsm_state_transfer(FSM_t* pFsm, uint8_t stEventID)
{uint8_t i = 0;for(i=0; i<pFsm->u8EventSum; i++){if((stEventID == pFsm->pEventMap[i].stEventID) && (pFsm->stCurState == pFsm->pEventMap[i].stCurState)){pFsm->stNextState = pFsm->pEventMap[i].stNextState;pFsm->u8Flag = 1;return;}}
}

4、状态机动作执行函数

该函数为此状态机的精髓所在,一般的状态机在状态切换时只执行一次动作,或者周期执行同一动作。
而此状态机则在状态切换时执行当前动作的退出函数(一次),即ExitAct函数,实际应用当中可对该状态的一些变量进行清0,或者打印相关调试信息等;同时还会执行下一个状态的进入函数(一次),即EnterAct函数,实际应用当中可以对该状态的变量进行初始化等操作。在状态切换完成后,还会周期执行RunningAct函数。
这里的ExitAct函数和EnterAct函数类似于C++的构造函数和析构函数。

uint8_t get_action_sum(FSM_t* pFsm,uint8_t u8ID)
{int i = 0;for(i=0; i<pFsm->u8ActSum; i++){if(u8ID == pFsm->pActionMap[i].stStateID)return i;}return -1;
}
void action_perfrom(FSM_t* pFsm)
{uint8_t u8CurID = -1, u8NextID = -1;if(0 != pFsm->u8Flag)    /* 标志位不为0表示要进行状态切换 */{u8CurID = get_action_sum(pFsm,pFsm->stCurState);u8NextID = get_action_sum(pFsm,pFsm->stNextState);if((-1 != u8CurID) && (-1 != u8NextID)){pFsm->pActionMap[u8CurID].ExitAct();   /* 执行当前状态的退出动作 */pFsm->pActionMap[u8NextID].EnterAct(); /* 执行下一个状态的进入动作 */}else{pFsm->u8Flag = 0;                                /* 清标志位 */printf("State transition failed! curState = %d, nextState = %d\n",pFsm->stCurState,pFsm->stNextState);return;}pFsm->stCurState = pFsm->stNextState;          /* 当前状态切换 */pFsm->stNextState = -1;pFsm->u8Flag = 0;                                /* 清标志位 */}else{u8CurID = get_action_sum(pFsm,pFsm->stCurState);if(-1 != u8CurID)pFsm->pActionMap[u8CurID].RunningAct();/* 标志位为0不进行状态切换,执行当前状态的do动作 */}
}

二、如何使用状态机

这里还是使用之前的博客里状态机流程图为例:

1、以流程图为标准定义事件表

数组eventMap就是对以上流程图的全部描述:

#define EVENT1 0
#define EVENT2 1
#define EVENT3 2
#define EVENT4 3
#define EVENT5 4EVENT_MAP_t eventMap[] =
{{EVENT1,   STATE1, STATE2},{EVENT2,    STATE2, STATE3},    {EVENT3,    STATE3, STATE4},{EVENT4,    STATE4, STATE1},{EVENT5,    STATE1, STATE4},
};

2、定义动作表

#define STATE1 0
#define STATE2 1
#define STATE3 2
#define STATE4 3ACTION_MAP_t actionMap[] =
{{STATE1,   state1_entry,   state1_do,  state1_exit},{STATE2,   state2_entry,   state2_do,  state2_exit},{STATE3,   state3_entry,   state3_do,  state3_exit},{STATE4,   state4_entry,   state4_do,  state4_exit},
};

3、实现动作函数

这里的每个函数的内容可以根据实际情况进行填充。


void state1_entry(void)
{printf("state1_entry\n");
}
void state1_do(void)
{printf("state1_do\n");
}
void state1_exit(void)
{printf("state1_exit\n");
}void state2_entry(void)
{printf("state2_entry\n");
}
void state2_do(void)
{printf("state2_do\n");
}
void state2_exit(void)
{printf("state2_exit\n");
}void state3_entry(void)
{printf("state3_entry\n");
}
void state3_do(void)
{printf("state3_do\n");
}
void state3_exit(void)
{printf("state3_exit\n");
}void state4_entry(void)
{printf("state4_entry\n");
}
void state4_do(void)
{printf("state4_do\n");
}
void state4_exit(void)
{printf("state4_exit\n");
}

4、实现表获取函数

/* 获取动作表 */
ACTION_MAP_t* get_action_map(uint8_t *total)
{*total = sizeof(actionMap)/sizeof(ACTION_MAP_t);return actionMap;
}/* 获取事件表 */
EVENT_MAP_t* get_event_map(uint8_t *total)
{*total = sizeof(eventMap)/sizeof(EVENT_MAP_t);return eventMap;
}

5、测试

int main(void)
{int i = 0;        uint8_t u8ActNum = 0, u8EventNum = 0;FSM_t stFsm; /* 定义状态机 */ACTION_MAP_t* stActMap;EVENT_MAP_t* stEventMap;stActMap = get_action_map(&u8ActNum);stEventMap = get_event_map(&u8EventNum);fsm_init(&stFsm,stEventMap,stActMap,u8EventNum,u8ActNum,STATE1);while(1){sleep(1);printf("i = %d\n",i++);action_perfrom(&stFsm);/* 利用i产生EVENT1~EVENT5 */if(1 == (i%10)){fsm_state_transfer(&stFsm,EVENT1);}if(3 == (i%10)){fsm_state_transfer(&stFsm,EVENT2);}if(5 == (i%10)){fsm_state_transfer(&stFsm,EVENT3);}if(7 == (i%10)){fsm_state_transfer(&stFsm,EVENT4);}  if(9 == (i%10)){fsm_state_transfer(&stFsm,EVENT5);}}return 0;
}

调试信息如下:

三、附上代码

fsm.c

#include <stdio.h>
#include "fsm.h"void fsm_init(FSM_t* pFsm,EVENT_MAP_t* pEventMap,ACTION_MAP_t *pActionMap,uint8_t u8EventSum,uint8_t u8ActSum,uint8_t curState)
{pFsm->u8Flag = 0;pFsm->stNextState = 0;pFsm->u8EventSum = u8EventSum;pFsm->u8ActSum = u8ActSum;pFsm->stCurState = curState;pFsm->pEventMap = pEventMap;pFsm->pActionMap = pActionMap;
}void fsm_state_transfer(FSM_t* pFsm, uint8_t stEventID)
{uint8_t i = 0;for(i=0; i<pFsm->u8EventSum; i++){if((stEventID == pFsm->pEventMap[i].stEventID) && (pFsm->stCurState == pFsm->pEventMap[i].stCurState)){pFsm->stNextState = pFsm->pEventMap[i].stNextState;pFsm->u8Flag = 1;return;}}
}uint8_t get_action_sum(FSM_t* pFsm,uint8_t u8ID)
{int i = 0;for(i=0; i<pFsm->u8ActSum; i++){if(u8ID == pFsm->pActionMap[i].stStateID)return i;}return -1;
}void action_perfrom(FSM_t* pFsm)
{uint8_t u8CurID = -1, u8NextID = -1;if(0 != pFsm->u8Flag)    /* 标志位不为0表示要进行状态切换 */{u8CurID = get_action_sum(pFsm,pFsm->stCurState);u8NextID = get_action_sum(pFsm,pFsm->stNextState);if((-1 != u8CurID) && (-1 != u8NextID)){pFsm->pActionMap[u8CurID].ExitAct();   /* 执行当前状态的退出动作 */pFsm->pActionMap[u8NextID].EnterAct(); /* 执行下一个状态的进入动作 */}else{pFsm->u8Flag = 0;                                /* 清标志位 */printf("State transition failed! curState = %d, nextState = %d\n",pFsm->stCurState,pFsm->stNextState);return;}pFsm->stCurState = pFsm->stNextState;          /* 当前状态切换 */pFsm->stNextState = -1;pFsm->u8Flag = 0;                                /* 清标志位 */}else{u8CurID = get_action_sum(pFsm,pFsm->stCurState);if(-1 != u8CurID)pFsm->pActionMap[u8CurID].RunningAct();/* 标志位为0不进行状态切换,执行当前状态的do动作 */}
}

fsm.h

#ifndef FSM_H
#define FSM_H#include "common.h"typedef void (*STATE_ACTION)(void);   typedef struct ACTION_MAP
{uint8_t        stStateID;STATE_ACTION  EnterAct;   STATE_ACTION    RunningAct; STATE_ACTION    ExitAct;
}ACTION_MAP_t; /* 动作action表描述 */typedef struct EVENT_MAP
{uint8_t    stEventID;uint8_t stCurState;uint8_t stNextState;
}EVENT_MAP_t; /* 事件event表描述 */typedef struct FSM
{uint8_t u8Flag;            /* 状态切换标志位,1表示要进行状态切换 */uint8_t u8EventSum;         /* 事件event总数 */uint8_t u8ActSum;            /* 动作action总数 */uint8_t stCurState;uint8_t stNextState;ACTION_MAP_t *pActionMap;EVENT_MAP_t *pEventMap;
}FSM_t; /* 状态机控制结构 */void fsm_init(FSM_t* pFsm,EVENT_MAP_t* pEventMap,ACTION_MAP_t *pActionMap,uint8_t u8EventSum,uint8_t u8ActSum,uint8_t curState);
void fsm_state_transfer(FSM_t* pFsm, uint8_t stEventID);
void action_perfrom(FSM_t* pFsm);#endif

action.c

#include <stdio.h>
#include "action.h"void state1_entry(void);
void state1_do(void);
void state1_exit(void);
void state2_entry(void);
void state2_do(void);
void state2_exit(void);
void state3_entry(void);
void state3_do(void);
void state3_exit(void);
void state4_entry(void);
void state4_do(void);
void state4_exit(void);ACTION_MAP_t actionMap[] =
{{STATE1,   state1_entry,   state1_do,  state1_exit},{STATE2,   state2_entry,   state2_do,  state2_exit},{STATE3,   state3_entry,   state3_do,  state3_exit},{STATE4,   state4_entry,   state4_do,  state4_exit},
};
void state1_entry(void)
{printf("state1_entry\n");
}
void state1_do(void)
{printf("state1_do\n");
}
void state1_exit(void)
{printf("state1_exit\n");
}void state2_entry(void)
{printf("state2_entry\n");
}
void state2_do(void)
{printf("state2_do\n");
}
void state2_exit(void)
{printf("state2_exit\n");
}void state3_entry(void)
{printf("state3_entry\n");
}
void state3_do(void)
{printf("state3_do\n");
}
void state3_exit(void)
{printf("state3_exit\n");
}void state4_entry(void)
{printf("state4_entry\n");
}
void state4_do(void)
{printf("state4_do\n");
}
void state4_exit(void)
{printf("state4_exit\n");
}ACTION_MAP_t* get_action_map(uint8_t *total)
{*total = sizeof(actionMap)/sizeof(ACTION_MAP_t);return actionMap;
}

action.h

#ifndef ACTION_H
#define ACTION_H#include "common.h"
#include "fsm.h"#define STATE1 0
#define STATE2 1
#define STATE3 2
#define STATE4 3ACTION_MAP_t* get_action_map(uint8_t *total);#endif

event.c

#include "event.h"EVENT_MAP_t eventMap[] =
{{EVENT1,   STATE1, STATE2},{EVENT2,    STATE2, STATE3},    {EVENT3,    STATE3, STATE4},{EVENT4,    STATE4, STATE1},{EVENT5,    STATE1, STATE4},
};EVENT_MAP_t* get_event_map(uint8_t *total)
{*total = sizeof(eventMap)/sizeof(EVENT_MAP_t);return eventMap;
}

event.h

#ifndef EVENT_H
#define EVENT_H#include "fsm.h"
#include "common.h"
#include "action.h"#define EVENT1 0
#define EVENT2 1
#define EVENT3 2
#define EVENT4 3
#define EVENT5 4EVENT_MAP_t* get_event_map(uint8_t* total);#endif

common.h

#ifndef COMMON_H
#define COMMON_Htypedef  unsigned char uint8_t;#endif

main.c

int main(void)
{int i = 0;        uint8_t u8ActNum = 0, u8EventNum = 0;FSM_t stFsm; /* 定义状态机 */ACTION_MAP_t* stActMap;EVENT_MAP_t* stEventMap;stActMap = get_action_map(&u8ActNum);stEventMap = get_event_map(&u8EventNum);fsm_init(&stFsm,stEventMap,stActMap,u8EventNum,u8ActNum,STATE1);while(1){sleep(1);printf("i = %d\n",i++);action_perfrom(&stFsm);/* 利用i产生EVENT1~EVENT5 */if(1 == (i%10)){fsm_state_transfer(&stFsm,EVENT1);}if(3 == (i%10)){fsm_state_transfer(&stFsm,EVENT2);}if(5 == (i%10)){fsm_state_transfer(&stFsm,EVENT3);}if(7 == (i%10)){fsm_state_transfer(&stFsm,EVENT4);}  if(9 == (i%10)){fsm_state_transfer(&stFsm,EVENT5);}}return 0;
}

Makefile

objects = fsm.o action.o event.omain:*.occ -o $@ $^*.o:*.c cc -c $^clean:rm *.o -rfrm main -rf

C语言实现状态机(二)相关推荐

  1. 设计模式的C语言应用-状态机模式-第二章

    模式介绍 状态(state)模式是C语言实现相当常用的模式,也是能够在C语言***现出来的最显性的模式之一.在面向对象里,状态模式允许一个对象在内部状态改变的时候改变其行为. 状态用法很多,最常见的是 ...

  2. 基于VHDL语言的状态机设计

    基于VHDL语言的状态机(FSM)设计 状态机(Finite State Machine,FSM) 状态机的组成:如图所示 状态机的种类: Mealy型:当前状态.当前输入相关 Moore型:仅当前状 ...

  3. 国二C语言文字选择程序选择,全国计算机等级考试二级C语言题型总结(二)——选择循环结构程序设计部分.doc...

    全国计算机等级考试二级C语言题型总结(二)--选择循环结构程序设计部分 C语言第二部分上机题型总结 选择结构部分: if结构题型总结 (案例1) 企业发放的奖金根据利润提成.利润(I)低于或等于10万 ...

  4. 数据结构源码笔记(C语言):二叉平衡树的相关操作算法

    //二叉平衡树的相关运算 #include<stdio.h> #include<malloc.h> #include<string.h>typedef char I ...

  5. C语言指针和二维数组

    二维数组在概念上是二维的,有行和列,但在内存中所有的数组元素都是连续排列的,它们之间没有"缝隙".以下面的二维数组 a 为例: int a[3][4] = { {0, 1, 2, ...

  6. c++语言自定义操作符,C++语言复习笔记二

    C++语言复习笔记二 零.OOP 特征:抽象-封装-继承-多态 一.自定义数据类型 1.类 class 类名 { private: 私有成员(本类) public: 公共成员(所有) protecte ...

  7. c语言字符串二维数组的动态分配应,C语言中动态分配二维数组复习过程.doc

    C语言中动态分配二维数组复习过程.doc C语言中动态分配二维数组在C中动态分配内存的,对于单个变量,字符串,一维数组等,都是很容易的.C中动态分配二维数组的方法,很少有C语言书中描述,我查找了有的C ...

  8. cad二次开发 java_应用Java语言进行AutoCAD2000二次开发.PDF

    应用Java语言进行AutoCAD2000二次开发 实用第一 智慧密集 刘良华袁英战朱东海 摘 要 介绍了应用Java语言进行AutoCAD二次开发的方法和思路. 关键字 二次开发,AutoCAD定制 ...

  9. 一起学习C语言:函数(二)

    上一篇<一起学习C语言:函数(一)> 中,我们了解了函数的概念,以及函数实现与程序编译过程.本章节,我们分析内部函数和外部函数,以及变量的生命周期. 章节预览: 4. 外部函数与内部函数 ...

最新文章

  1. 思科设置密码及一些基本操作
  2. 长寿即服务:创业公司如何用AI技术颠覆传统药物研发
  3. linux c va_list 32位和64位的差异
  4. 成功解决MSB8020 The build tools for v141 (Platform Toolset = ‘v141‘) cannot be found. To build using the
  5. 盘点三个JavaScript案例——实现限时秒杀、定时跳转、改变盒子大小
  6. python的opencv模块_OpenCV Python - 没有名为cv2的模块(再次)
  7. 数据库主从延迟导致查询不准确的解决思路
  8. 彻底卸载VMware虚拟机的详细步骤
  9. 飞鱼星测试软件,飞鱼星VE984GW+
  10. mysql套接字连接_无法通过Remote上的套接字连接到本地MySQL服务器
  11. linux 防火墙reject,CentOS 防火墙配置与REJECT导致没有生效问题
  12. Apache Flink 零基础入门(四):客户端操作的 5 种模式
  13. 计算机的业务流程图是什么意思,什么是业务流程图?它的作用是什么?
  14. 机械制造作业考研题目答案分享——回转体的加工
  15. 记录一下使用微信小程序wx-open-launch-weapp组件
  16. 在eclipse上使用Maven创建动态web项目
  17. Executor框架的使用
  18. 网页AB Testing的七个原则(半成品)
  19. Windows11下安装Docker
  20. 诡异的 spring mail 发送邮件问题

热门文章

  1. myeclipse7.5注册码
  2. 理论+实操 :部署YUM仓库以及NFS资源共享服务————理论讲解
  3. easyx的使用(2)
  4. 七天学会「股票数据分析软件」的开发(上)
  5. 刘盈盈计算机科学与技术,关于成立乐清市中小学幼儿园新教师专业发展指导师团队的通知...
  6. Linux上搭建Discuz论坛
  7. JavaScript流程控制-循环(循环(for 循环,双重 for 循环,while 循环,do while 循环,continue break))
  8. 二、如何写好学术/学位论文
  9. kafka消息堆积且CPU过高代码优化
  10. 无穷级数的简单求解方法——高等数学