总述
有限状态机是嵌入式开发中使用的常用方法,是针对一些连贯状态的控制响应。例如:按键按下灯亮,按键抬起灯灭
状态机结构设计
状态机是有许多的子状态构成,而子状态包括当前的状态、事件触发后的下一个状态、触发的事件以及触发后执行的动作,由此其数据结构可以如下:
typedef void (*func)(void *);
typedef struct fsm_table_t
{
int cur_status; //当前状态
int next_status; //触发后进入的下一状态
int event; //事件
func eventfunc; //执行动作
}fsm_table_t;
状态机需要维护一个整体的状态和包含子状态的列表,由此数据结构可以如下:
#define MAX_FSM_TABLE_SIZE (10)
//系统状态结构
typedef struct fsm_t
{
int system_status; //当前系统状态
int system_valid_item_len; //有效子项个数
fsm_table_t fsm_table[MAX_FSM_TABLE_SIZE]; //状态机子项链表
}fsm_t;
事件发生后,需要从状态机子项列表中查找符合条件的子状态,实现代码可如下:
int FsmEventHandler(fsm_t *fsm, int event,void *data)
{
int i = 0;
for(i = 0; i < fsm->system_valid_item_len; i++)
{
//查找与当前系统状态一样并且触发事件也相同的子状态项
if(fsm->fsm_table[i].cur_status == fsm->system_status &&
fsm->fsm_table[i].event == event)
{
fsm->fsm_table[i].eventfunc(data);
//系统状态改变
fsm->system_status =
fsm->fsm_table[i].next_status;
return 0;
}
}
}
添加子状态到整个状态的列表:
int AddItemToFsm(fsm_t *fsm, int cur_status, int next_status,
int event, func eventfunc)
{
//入参判断
if(fsm->system_valid_item_len >= MAX_FSM_TABLE_SIZE
|| eventfunc == NULL)
return - 1;
//赋值
fsm->fsm_table[fsm->system_valid_item_len].cur_status
= cur_status;
fsm->fsm_table[fsm->system_valid_item_len].next_status
= next_status;
fsm->fsm_table[fsm->system_valid_item_len].event = event;
fsm->fsm_table[fsm->system_valid_item_len].eventfunc
= eventfunc;
fsm->system_valid_item_len++;
return 0 ;
}
状态机模拟
上述基本实现了状态机的整体结构,接下来模拟一下状态机的运行效果,测试代码如下:
void func1(void *in)
{
printf("event 1\r\n");
}
void func2(void *in)
{
printf("event 2\r\n");
}
void func3(void *in)
{
printf("event 3\r\n");
}
fsm_t test_fsm;
memset(&test_fsm, 0, sizeof(test_fsm));
test_fsm.system_status = 1;//初始化系统状态
AddItemToFsm(&test_fsm, 1, 2, 10, func1);
AddItemToFsm(&test_fsm, 2, 3, 11, func2);
AddItemToFsm(&test_fsm, 3, 1, 12, func3);
while(1)
{
//触发事件10
FsmEventHandler(&test_fsm, 10, NULL);
//触发事件11
FsmEventHandler(&test_fsm, 11, NULL);
//触发事件12
FsmEventHandler(&test_fsm, 12, NULL);
}
//测试效果
event 1
event 2
event 3
...循环打印
总结
上述代码其实就是平常使用的switch case或者标志位的封装,只不过这样操作起来会更加简洁明了。