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