我是靠谱客的博主 大方柜子,最近开发中收集的这篇文章主要介绍C语言状态机模块实现1. 状态机模块实现2. 状态机模块使用,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

1. 状态机模块实现

状态机编程思想,能够使复杂的逻辑代码变得更加的简单,且逻辑思路更加清晰严谨。下面根据另一篇博文介绍的状态机思想,用C语言实现了状态机可复用的模块化代码。

状态机 fsm.h 头文件代码如下:

#ifndef _FSM_H_
#define _FSM_H_

#include <stdint.h>
#include <stddef.h>

typedef struct FsmTable_s
{
    uint8_t event;                /* 触发事件 */
    uint8_t CurState;             /* 当前状态 */
    void (*eventActFun)(void *);  /* 动作函数 */
    uint8_t NextState;            /* 跳转状态 */
}FsmTable_T;

typedef struct FSM_s
{
    FsmTable_T *FsmTable;         /* 状态迁移表 */
    uint8_t curState;             /* 状态机当前状态 */
    uint8_t stuMaxNum;            /* 状态机状态迁移数量 */
}FSM_T;

/*********************************************************************************
使用方法:1.创建FSM_T对象;
          2.创建FsmTable_T表;
          3.调用FSM_Init()初始化;
          4.程序轮询FSM_EventHandle()运行状态机。
*********************************************************************************/
void FSM_Init(FSM_T *pFsm, FsmTable_T *pTable, uint8_t stuMaxNum, uint8_t curState);
void FSM_EventHandle(FSM_T *pFsm, uint8_t event, void *parm);

#endif

状态机 fsm.c 源文件代码如下:

#include "fsm.h"

/*==================================================================
* Function  : FSM_StateTransfer
* Description : 状态转换
* Input Para  : 
* Output Para : 
* Return Value: 
==================================================================*/
static void FSM_StateTransfer(FSM_T *pFsm, uint8_t state)
{
    pFsm->curState = state;
}

/*==================================================================
* Function  : FSM_EventHandle
* Description : 状态机处理函数
* Input Para  : pFsm状态机对象, event触发事件, parm动作执行参数
* Output Para : 
* Return Value: 
==================================================================*/
void FSM_EventHandle(FSM_T *pFsm, uint8_t event, void *parm)
{
    FsmTable_T *pAcTable = pFsm->FsmTable;
    void (*eventActFun)(void *) = NULL;
    uint8_t NextState;
    uint8_t CurState = pFsm->curState;
    uint8_t flag = 0;
      
    for (uint8_t i = 0; i < pFsm->stuMaxNum; i++)// 遍历状态表
    {
        if (event == pAcTable[i].event && CurState == pAcTable[i].CurState)
        {
            flag = 1;
            eventActFun = pAcTable[i].eventActFun;
            NextState = pAcTable[i].NextState;
            break;
        }
    }
    if (flag)
    {
        if (eventActFun != NULL)
        {
            eventActFun(parm);  // 执行相应动作
        }
        FSM_StateTransfer(pFsm, NextState); // 状态转换
    }
    else
    {
        // do nothing
    }
}

/*==================================================================
* Function  : FSM_Init
* Description : 状态机初始化
* Input Para  : pFsm状态机对象,pTable状态迁移表,stuMaxNum迁移表数量
*               curState当前状态
* Output Para : 
* Return Value: 
==================================================================*/
void FSM_Init(FSM_T *pFsm, FsmTable_T *pTable, uint8_t stuMaxNum, uint8_t curState)
{
    pFsm->FsmTable = pTable;
    pFsm->curState = curState;
    pFsm->stuMaxNum = stuMaxNum;
}

2. 状态机模块使用

使用状态机结合消息队列的编程思想,实例代码如下

/** @enum FSM_STATE_E
 *  @brief 状态机运行状态
 *  
 */
typedef enum
{
    RUNNING = 0x00, // 运行态
    FAULT,          // 故障态
}FSM_STATE_E;

/** @enum TRIG_EVENT_E
 *  @brief 状态机触发事件
 *  
 */
typedef enum
{       
    SENSOR_FAULT,       // 传感器故障事件
    SENSOR_RESUME,      // 传感器故障恢复事件
}TRIG_EVENT_E, *PTRIG_EVENT_E;


/** @struct MSG_EVENT_TRIG_T
 *  @brief 事件触发消息结构
 *  
 */
typedef struct
{
    uint8_t eDevPort;    			/* 事件触发传感器端口 */
    TRIG_EVENT_E eEventType;        /* 事件触发类型 */
    
}MSG_EVENT_TRIG_T, *PMSG_EVENT_TRIG_T;

/** @struct DEV_DET_T
 *  @brief  探测器
 *  
 */
typedef struct
{
    SQ_QUEUE stEventQue;                /* 事件触发队列 */
    MSG_EVENT_TRIG_T eEventQueBuf[8];  /* 事件触发缓存 */
    
}DEV_DET_T, *PDEV_DET_T;


/* 创建监控探测器实例 */
static DEV_DET_T g_stDetIns;


/* 状态机变量 */
static FSM_T g_stFsm;

/* 动作函数 */
static void ActFun_FaultEvent(void *parm);
static void ActFun_FaultResumeEvent(void *parm);

/* 状态迁移表 */
static FsmTable_T g_stFsmTable[] = 
{
    /* 触发事件         初态            动作函数             次态  */	
	{SENSOR_FAULT,      RUNNING,     ActFun_FaultEvent,             FAULT},
	{SENSOR_RESUME,     FAULT,       ActFun_FaultResumeEvent,       RUNNING},
};

/* 计算状态迁移表长度 */
#define FSM_TABLE_MAX_NUM (sizeof(g_stFsmTable)/sizeof(FsmTable_T))


/*==================================================================
* Function  : APP_Init
* Description : 创建消息队列,初始化状态机
* Input Para  : 
* Output Para : 
* Return Value: 
==================================================================*/
void APP_Init(void)
{   
    /* 创建事件触发队列 */
    if (!Que_InitQueue(&g_stDetIns.stEventQue,
        sizeof(g_stDetIns.eEventQueBuf)/sizeof(g_stDetIns.eEventQueBuf[0]),
        sizeof(g_stDetIns.eEventQueBuf[0]), 1, (uint8_t *)g_stDetIns.eEventQueBuf))
    {
        USER_DEBUG(PRINT_SER, "ELEC: electrical fire detector tigger event queue create fail!rn");
    }

    /* 初始化状态机 */
    FSM_Init(&g_stFsm, g_stFsmTable, FSM_TABLE_MAX_NUM, RUNNING);
    
}

/*==================================================================
* Function  : ActFun_FaultEvent
* Description : 触发故障事件动作函数
* Input Para  : LocalActiveStatus
* Output Para : 
* Return Value: 
==================================================================*/
static void ActFun_FaultEvent(void *parm)
{
	/* 执行故障相关的动作 */	
}

/*==================================================================
* Function  : ActFun_FaultResumeEvent
* Description : 故障恢复事件动作函数
* Input Para  : 
* Output Para : 
* Return Value: 
==================================================================*/
static void ActFun_FaultResumeEvent(void *parm)
{
	/* 执行故障恢复相关的动作 */
}

/*==================================================================
* Function  : FsmEventHandleTask
* Description : 在定时器中定时轮询,避免动作函数中含有长任务函数
* Input Para  : 
* Output Para : 
* Return Value: 
==================================================================*/
void FsmEventHandleTask(void)
{       
    uint8_t* p_EventBuf = NULL;
    PMSG_EVENT_TRIG_T p_TrigMsg = NULL;
    
    /* 取出触发事件队列中的事件 */
    if (Que_DeQueue(&g_stDetIns.stEventQue, &p_EventBuf))
    {   
        p_TrigMsg = (PMSG_EVENT_TRIG_T)p_EventBuf;
        
        /* 在其它模块中改变触发事件,即可完成相应动作的执行 */
        FSM_EventHandle(&g_stElecFsm, p_TrigMsg->eEventType, (void *)&p_TrigMsg->eDevPort);
    }    
}

/*==================================================================
* Function  : Elec_UpdateEvent
* Description : 更新触发事件
* Input Para  : 
* Output Para : 
* Return Value: 
==================================================================*/
void Elec_UpdateEvent(MSG_EVENT_TRIG_T stTrigEventMsg)
{    
    /* 触发事件入队 */
    if (!Que_EnQueue(&g_stDetIns.stEventQue, (uint8_t*)&stTrigEventMsg, sizeof(g_stDetIns.eEventQueBuf[0])))
    {
        USER_DEBUG(PRINT_SYS, "ELEC: electrical fire detector tigger event entry queue fail!rn");
    }
}

/*==================================================================
* Function  : TestEvent
* Description : 测试事件函数
* Input Para  : 
* Output Para : 
* Return Value: 
==================================================================*/
void TestEvent(void)
{
	MSG_EVENT_TRIG_T stTrigEventMsg;
	
	/* 触发故障事件 */
	stTrigEventMsg.eEventType = SENSOR_FAULT; 
	Elec_UpdateEvent(stTrigEventMsg);
}

 另外还有实际项目开发中,如何使用状态机设计软件架构的案例:

状态机设计模式:电动车报警器项目实战_智小星的博客-CSDN博客

 

最后

以上就是大方柜子为你收集整理的C语言状态机模块实现1. 状态机模块实现2. 状态机模块使用的全部内容,希望文章能够帮你解决C语言状态机模块实现1. 状态机模块实现2. 状态机模块使用所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部