c语言之fsm状态机实现

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;
}

queue.h

#ifndef __QUEUE_H_
#define __QUEUE_H_

/*********************************************************************
 * @brief       队列类型定义
 *********************************************************************/
typedef struct
{
    volatile unsigned int   front;           // front为起始元素
    volatile unsigned int   rear;            // rear为最后一个元素
    volatile unsigned int   count;           // 元素个数
    unsigned int            element_size;    // 每个元素的字节数(这里的元素可以是任意类型的变量)
    unsigned int            element_num;     // 队列深度(元素最大个数,以元素的类型为基本单位,例如元素是结构体,那element_num就是这个结构体的数量)
    void                    *buf;            // 缓存区(大小为element_size * element_num)
}queue_type;

/*********************************************************************
 * @brief       队列初始化
 * @param[in]   queue: 队列指针
 * @param[in]   buf: 队列中缓存
 * @param[in]   element_size:队列每个元素的字节数
 * @param[in]   element_num: 队列深度(元素最大个数)
 * @return      None
 *********************************************************************/
void queue_create(queue_type *queue, void *buf, unsigned int element_size, unsigned int element_num);

/*********************************************************************
 * @brief       判断队列是否为空
 * @param[in]   queue: 队列指针
 * @return      1-TRUE or 0-FALSE
 *********************************************************************/
unsigned char queue_is_empty(queue_type *queue);

/*********************************************************************
 * @brief       判断队列是否已满
 * @param[in]   queue: 队列指针
 * @return      TRUE or FALSE
 *********************************************************************/
unsigned char queue_is_full(queue_type *queue);

/*********************************************************************
 * @brief       向队列添加一个元素
 * @param[in]   queue: 队列指针
 * @param[in]   value: 要添加的元素

*********************************************************************/
unsigned char queue_append_byte(queue_type *queue, void *value);

/*********************************************************************
 * @brief       向队列添加多个元素
 * @param[in]   queue: 队列指针
 * @param[in]   values: 要添加的元素指针
 * @param[in]   element_num: 要添加元素个数
 * @return      实际添加的元素个数
 *********************************************************************/
unsigned int queue_append_nbyte(queue_type *queue, void *values, unsigned int element_num);

/*********************************************************************
 * @brief       从队列读取一个元素
 * @param[in]   queue: 队列指针
 * @param[in]   value: 存放要读取的元素指针
 * @return      1-TRUE or 0-FALSE
 *********************************************************************/
unsigned char queue_delete_byte(queue_type *queue, void *value);

/*********************************************************************
 * @brief       从队列读取多个元素
 * @param[in]   queue: 队列指针
 * @param[out]  values: 存放要读取的元素指针
 * @param[in]   element_num: 要读取的元素个数
 * @return      实际读取的元素个数
 *********************************************************************/
unsigned int queue_delete_nbyte(queue_type *queue, void *values, unsigned int element_num);

/*********************************************************************
 * @brief       清空队列
 * @param[in]   queue: 队列指针
 * @return      None
 *********************************************************************/
void queue_clear(queue_type *queue);

#endif

queue.c

#include <string.h>
#include "queue.h"
#include <stdio.h>

/*********************************************************************
 * @brief       创建队列
 * @param[in]   queue: 队列指针
 * @param[in]   buf: 队列缓存(大小为element_size * element_num)
 * @param[in]   element_size: 队列每个元素的大小(这里的元素可以是任意类型的变量)
 * @param[in]   element_num: 队列最大元素个数(以元素的类型为基本单位,例如元素是一个结构体,那么element_num就是这个结构体的数量)
 * @return      None
 *********************************************************************/
void queue_create(queue_type *queue, void *buf, unsigned int element_size, unsigned int element_num)
{
    queue->buf = buf;
    queue->element_size = element_size;
    queue->element_num = element_num;
    queue->front = 0;
    queue->rear = 0;
    queue->count = 0;
}

/*********************************************************************
 * @brief       判断队列是否为空
 * @param[in]   queue: 队列指针
 * @return      1-TRUE or 0-FALSE
 *********************************************************************/
unsigned char queue_is_empty(queue_type *queue)
{
    return (queue->count == 0);
}

/*********************************************************************
 * @brief       判断队列是否已满
 * @param[in]   queue: 队列指针
 * @return      TRUE or FALSE
 *********************************************************************/
unsigned char queue_is_full(queue_type *queue)
{
    return (queue->count == queue->element_num);
}

/*********************************************************************
 * @brief       向队列添加一个元素
 * @param[in]   queue: 队列指针
 * @param[in]   value: 要添加的元素
 * @return      1-TRUE or 0-FALSE
 *********************************************************************/
unsigned char queue_append_byte(queue_type *queue, void *value)
{
    unsigned char *p;

    if (queue_is_full(queue))
    {
        return 0;
    }

    p = (unsigned char *)queue->buf;
    memcpy(p + queue->rear, (unsigned char *)value, queue->element_size);

    queue->rear += queue->element_size;
    if (queue->rear >= queue->element_size * queue->element_num)
    {
        queue->rear = 0;
    }
    queue->count ++;
    return 1;
}

/*********************************************************************
 * @brief       向队列添加多个元素
 * @param[in]   queue: 队列指针
 * @param[in]   values: 要添加的元素指针
 * @param[in]   element_num: 要添加的元素个数
 * @return      实际添加的元素个数
 *********************************************************************/
unsigned int queue_append_nbyte(queue_type *queue, void *values, unsigned int element_num)
{
    unsigned char *p;
    unsigned int cnt = 0;

    p = (unsigned char *)values;
    while(element_num --)
    {
        if (queue_append_byte(queue, p))
        {
            p += queue->element_size;
            cnt++;
        }
        else
        {
            break;
        }
    }
    return cnt;
}

/*********************************************************************
 * @brief       从队列读取一个元素
 * @param[in]   queue: 队列指针
 * @param[in]   value: 存放要读取的元素指针
 * @return      1-TRUE or 0-FALSE
 *********************************************************************/
unsigned char queue_delete_byte(queue_type *queue, void *value)
{
    unsigned char *p;
    if (queue_is_empty(queue))
    {
        return 0;
    }

    p = (unsigned char *)queue->buf;
    memcpy(value, p + queue->front, queue->element_size);

    queue->front += queue->element_size;
    if (queue->front >= queue->element_size * queue->element_num)
    {
        queue->front = 0;
    }
    queue->count --;
    return 1;
}

/*********************************************************************
 * @brief       从队列读取多个元素
 * @param[in]   queue: 队列指针
 * @param[out]  values: 存放要读取的元素指针
 * @param[in]   element_num: 要读取的元素长度
 * @return      实际读取的元素个数
 *********************************************************************/
unsigned int queue_delete_nbyte(queue_type *queue, void *values, unsigned int element_num)
{
    unsigned int cnt = 0;
    unsigned char *p;

    p = (unsigned char *)values;
    while(element_num--)
    {
        if (queue_delete_byte(queue, p))
        {
            p += queue->element_size;
            cnt++;
        }
        else
        {
            break;
        }
    }
    return cnt;
}

/*********************************************************************
 * @brief       清空队列
 * @param[in]   queue: 队列指针
 * @return      None
 *********************************************************************/
void queue_clear(queue_type *queue)
{
    queue->count = 0;
    queue->front = 0;
    queue->rear = 0;
}

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
{
    queue_type 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)
{
    /* 创建事件触发队列 */
    queue_create(&g_stDetIns.stEventQue, (uint8_t *)g_stDetIns.eEventQueBuf, sizeof(MSG_EVENT_TRIG_T), 8);

    /* 初始化状态机 */
    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)
{
	/* 执行故障相关的动作 */	
	printf("trigger fault happen\n");
}
 
/*==================================================================
* Function  : ActFun_FaultResumeEvent
* Description : 故障恢复事件动作函数
* Input Para  : 
* Output Para : 
* Return Value: 
==================================================================*/
static void ActFun_FaultResumeEvent(void *parm)
{
	/* 执行故障恢复相关的动作 */
	printf("trigger fault reset\n");
}
 
/*==================================================================
* Function  : FsmEventHandleTask
* Description : 在定时器中定时轮询,避免动作函数中含有长任务函数
* Input Para  : 
* Output Para : 
* Return Value: 
==================================================================*/
void FsmEventHandleTask(void)
{
    MSG_EVENT_TRIG_T p_TrigMsg;

    if(!queue_is_empty(&g_stDetIns.stEventQue))
    {
        /* 取出触发事件队列中的事件 */
        if(queue_delete_byte(&g_stDetIns.stEventQue, &p_TrigMsg))
        {
            /* 在其它模块中改变触发事件,即可完成相应动作的执行 */
            FSM_EventHandle(&g_stFsm, 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 (!queue_append_byte(&g_stDetIns.stEventQue, (uint8_t*)&stTrigEventMsg))
    {
        printf("ELEC: electrical fire detector tigger event entry queue fail!\r\n");
    }
}
 
/*==================================================================
* Function  : TestEvent
* Description : 测试事件函数
* Input Para  : 
* Output Para : 
* Return Value: 
==================================================================*/
void TestEvent1(void)
{
	MSG_EVENT_TRIG_T stTrigEventMsg;
	
	APP_Init();
	/* 触发故障事件 */
	stTrigEventMsg.eEventType = SENSOR_FAULT; 
	Elec_UpdateEvent(stTrigEventMsg);
}

void TestEvent2(void)
{
	MSG_EVENT_TRIG_T stTrigEventMsg;
	
	APP_Init();
	/* 触发故障事件 */
	stTrigEventMsg.eEventType = SENSOR_RESUME; 
	Elec_UpdateEvent(stTrigEventMsg);
}

输出结果如下

trigger fault happen
trigger fault reset

拓展

关于状态表使用拓展

FsmTable fsmtb[] = {
    /* 事件         当前状态        动作        下一个状态 */
    { E_IDLE,       S_IDLE,     idle_func,      S_IDLE },
    { E_BELL,       S_IDLE,     bell_func,      S_BELL }, 
    { E_DIAL,       S_IDLE,     dial_func,      S_DIAL },
    { E_TIME_OUT,   S_DIAL,     timeout_func,   S_TIMEOUT },
    { E_TIME_OUT,   S_BELL,     timeout_func,   S_TIMEOUT },
    { E_BUSY,       S_DIAL,     hangup_func,    S_HANGUP },
    { E_CONNECT,    S_DIAL,     talk_func,      S_TALK },
    { E_WHITE_LIST, S_BELL,     talk_func,      S_TALK }, 
    { E_BLACK_LIST, S_BELL,     hangup_func,    S_HANGUP },
    { E_FINISH,     S_TALK,     hangup_func,    S_HANGUP },
    { E_IDLE,       S_HANGUP,   idle_func,      S_IDLE },
    { E_IDLE,       S_TIMEOUT,  idle_func,      S_IDLE  }
};
下面是一个简单的FSM状态机示例代码(使用Python编写): ```python class State: def __init__(self, name): self.name = name def on_enter(self): pass def on_exit(self): pass def handle_event(self, event): pass class StateMachine: def __init__(self, initial_state): self.current_state = initial_state def transition_to(self, new_state): self.current_state.on_exit() self.current_state = new_state self.current_state.on_enter() def handle_event(self, event): self.current_state.handle_event(event) # 示例状态 class IdleState(State): def on_enter(self): print("进入空闲状态") def on_exit(self): print("退出空闲状态") def handle_event(self, event): if event == "start": print("开始任务") return "running" return self.name class RunningState(State): def on_enter(self): print("进入运行状态") def on_exit(self): print("退出运行状态") def handle_event(self, event): if event == "stop": print("停止任务") return "idle" return self.name # 创建状态机并设置初始状态为IdleState state_machine = StateMachine(IdleState()) # 处理事件 state_machine.handle_event("start") # 输出: 进入空闲状态 # 开始任务 state_machine.transition_to(RunningState()) # 输出: 退出空闲状态 # 进入运行状态 state_machine.handle_event("stop") # 输出: 停止任务 # 退出运行状态 # 进入空闲状态 ``` 以上示例代码展示了一个简单的状态机,其中包括两个状态,即"空闲"和"运行"。状态之间的切换由事件触发,例如"start"事件将从"空闲"状态切换到"运行"状态,"stop"事件将从"运行"状态切换回"空闲"状态。每个状态都有进入(on_enter)和退出(on_exit)方法,在切换到新状态时会调用这些方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值