关于这篇文章
针对看了好几篇关于状态机的文章,然后自己动手操作写了两个程序,这是第二个,上一个程序是关于一级状态机的,我选的那个场景不错,也可以修改成二级状态机实现。
一级状态机和二级状态机的区别是:二级状态机在一级状态机的基础上添加了很多子状态,(子状态共同拥有上级父状态的某些共性,又各自拥有自己的一些共性)。状态表需要分为两级。
这只是网上找的一个能说明二级状态机的一个图片,实现的场景和这个无关,而是简化了的进程管理,严格来说是我看了公众号文章什么是状态机之后,觉得它查找StateInfo的方式是switch…case的,这种如果后期需求改动还需要“动”程序逻辑,不太好,于是自己想办法改成了表驱动的。
看过表驱动的一级状态机实现 和 指针数组 这两篇文章的小伙伴应该已经知道我写的这二级状态机是怎么实现的了,测试程序逻辑的main函数也和一级状态机的大同小异。
程序的文件结构如下,程序源码下载stateMachine2.zip
关于二级状态机实现了解
参考的公众号文章什么是状态机,建议细细阅读,比我这种没写几句直接贴代码的强太多了,我也想写得像他一样,能力还是其次,首先是时间不允许呀。写文章的目的也不一样,人家是分享知识,我是分享知识学习后的所得。
程序运行结果
main函数测试代码
int main(void)
{
StateMachine SM_Type;
state_machine_regist(&SM_Type, StateTable);
SM_Type.currentState = sta_origin;
SM_Type.stateTableNum = sizeof(StateTable) / sizeof(StateTable[0]);
printf("init state: %s \n\n", comparison_table[SM_Type.currentState].str);
runStateMachine(&SM_Type, evt_fork);
printf("current state: %s \n\n", comparison_table[SM_Type.currentState].str);
runStateMachine(&SM_Type, evt_sched);
printf("current state: %s \n\n", comparison_table[SM_Type.currentState].str);
runStateMachine(&SM_Type, evt_sched);
printf("current state: %s \n\n", comparison_table[SM_Type.currentState].str);
runStateMachine(&SM_Type, evt_wait);
printf("current state: %s \n\n", comparison_table[SM_Type.currentState].str);
runStateMachine(&SM_Type, evt_wake);
printf("final state: %s \n\n", comparison_table[SM_Type.currentState].str);
return 0;
}
statemachine2.h 程序代码
#ifndef __STATE_MACHINE_H__
#define __STATE_MACHINE_H__
#include <stdio.h>
#include <stdint.h>
#define STATE_TABLE(num) state_table_ ## num
typedef void (*Action_Func)(void);
void action_callback(void);
typedef enum
{
sta_origin = 0,
sta_running,
sta_owencpu,
sta_sleep_int,
sta_sleep_unint,
} State;
#define STRING(s) #s
typedef struct
{
State sta; ///< 状态对应的常量
char *str; ///< 状态对应的标识符(字符串)
} ComparisonInfo;
static ComparisonInfo comparison_table[] =
{
{
sta_origin, STRING(sta_origin)},
{
sta_running, STRING(sta_running)},
{
sta_owencpu, STRING(sta_owencpu)},
{
sta_sleep_int, STRING(sta_sleep_int)},
{
sta_sleep_unint, STRING(sta_sleep_unint)},
};
typedef enum
{
evt_fork = 0,
evt_sched,
evt_wait,
evt_wait_unint,
evt_wake_up,
evt_wake,
} Event;
typedef struct
{
State curState; ///< 当前所处状态
Event event; ///< 发生事件
State nextState; ///< 下一个状态
Action_Func action; ///< 执行动作
} StateInfo;
/* origin */
static StateInfo state_table_0[] =
{
{
sta_origin, evt_fork, sta_running, action_callback},
{
sta_origin, evt_sched, sta_origin, NULL},
{
sta_origin, evt_wait, sta_origin, NULL},
{
sta_origin, evt_wait_unint, sta_origin, NULL},
{
sta_origin, evt_wake_up, sta_origin, NULL},
{
sta_origin, evt_wake, sta_origin, NULL},
};
/*running*/
static StateInfo state_table_1[] =
{
{
sta_running, evt_fork, sta_running, NULL},
{
sta_running, evt_sched, sta_owencpu, action_callback},
{
sta_running, evt_wait, sta_running, NULL},
{
sta_running, evt_wait_unint, sta_running, NULL},
{
sta_running, evt_wake_up, sta_running, NULL},
{
sta_running, evt_wake, sta_running, NULL},
};
/*owencpu*/
static StateInfo state_table_2[] =
{
{
sta_owencpu, evt_fork, sta_owencpu, NULL},
{
sta_owencpu, evt_sched, sta_owencpu, NULL},
{
sta_owencpu, evt_wait, sta_sleep_int, action_callback},
{
sta_owencpu, evt_wait_unint, sta_sleep_unint, action_callback},
{
sta_owencpu, evt_wake_up, sta_owencpu, NULL},
{
sta_owencpu, evt_wake, sta_owencpu, NULL},
};
/*sleep_int*/
static StateInfo state_table_3[] =
{
{
sta_sleep_int, evt_fork, sta_sleep_int, NULL},
{
sta_sleep_int, evt_sched, sta_sleep_int, NULL},
{
sta_sleep_int, evt_wait, sta_sleep_int, NULL},
{
sta_sleep_int, evt_wait_unint, sta_sleep_int, NULL},
{
sta_sleep_int, evt_wake_up, sta_sleep_int, NULL},
{
sta_sleep_int, evt_wake, sta_running, action_callback},
};
/*sleep_unint*/
static StateInfo state_table_4[] =
{
{
sta_sleep_unint, evt_fork, sta_sleep_unint, NULL},
{
sta_sleep_unint, evt_sched, sta_sleep_unint, NULL},
{
sta_sleep_unint, evt_wait, sta_sleep_unint, NULL},
{
sta_sleep_unint, evt_wait_unint, sta_sleep_unint, NULL},
{
sta_sleep_unint, evt_wake_up, sta_running, action_callback},
{
sta_sleep_unint, evt_wake, sta_sleep_unint, NULL},
};
/* 指针数组 */
static StateInfo *StateTable[] =
{
state_table_0,
state_table_1,
state_table_2,
state_table_3,
state_table_4
};
/* 状态机数据结构 */
typedef struct
{
State currentState; ///< 当前状态
StateInfo **stateTable; ///< 状态表指针 数组
uint8_t stateTableNum; ///< 状态表个数
} StateMachine;
void state_machine_regist(StateMachine *pSM, StateInfo *pStateTable[]);
StateInfo *find_state_table(StateMachine *pSM, Event evt);
void runStateMachine(StateMachine *pSM, Event evt);
#endif
statemachine2.c 程序代码
#include "statemachine2.h"
void action_callback(void)
{
printf("function : %s , execution action \n", __FUNCTION__);
}
/* 状态机注册,给它一个状态表 */
void state_machine_regist(StateMachine *pSM, StateInfo *pStateTable[])
{
if(pSM == NULL)
{
return;
}
pSM->stateTable = pStateTable;
}
/* 查找状态表 */
StateInfo *find_state_table(StateMachine *pSM, Event evt)
{
if(pSM == NULL)
{
return NULL;
}
for (uint8_t i = 0; i < pSM->stateTableNum; i++)
{
if ((pSM->stateTable[i][evt].curState == pSM->currentState) && (pSM->stateTable[i][evt].event == evt))
{
return &pSM->stateTable[i][evt];
}
}
}
/* 运行状态机 */
void runStateMachine(StateMachine *pSM, Event evt)
{
StateInfo *pStateInfo;
if(pSM == NULL)
{
return;
}
pStateInfo = find_state_table(pSM, evt);
if (pStateInfo == NULL)
{
return;
}
Action_Func act = pStateInfo->action;
if (act == NULL)
{
return;
}
act();
pSM->currentState = pStateInfo->nextState;
}
最后总结
_state_table(pSM, evt);
if (pStateInfo == NULL)
{
return;
}
Action_Func act = pStateInfo->action;
if (act == NULL)
{
return;
}
act();
pSM->currentState = pStateInfo->nextState;
}
## 最后总结
   到此为止已经对状态机有一定程度的掌握了,两个程序都是表驱动的(跳转表),我觉得这样实现对于后期维护比较友好,我对现在的单片机编程工作的认识是需求只增不减,只改不删,每次隔上一段时间后改需求自己一下子都搞不清要改哪几处,虽然目前积极在写功能模块的说明,将程序分而化之,可以对功能实现细节有更好的把控,但逐渐意识到更需要一个好的结构,我之前认为形成自己的编程风格,积累相关的技术文档就能写出好的程序来,至于什么是程序架构,没听说过,更别说它能起到什么样的作用了,之后打算写几篇这方面的博文,现在嘛,还未准备充分。