我的IMU开发之路第二天之状态机

来源:互联网 发布:js如何调用php函数 编辑:程序博客网 时间:2024/04/28 22:55
上两篇分别讲了硬件器件的选取注意的几个指标和软件架构。网友对硬件提出了一些建议,首先感谢你们!硬件先放一段落,今天继续按照上篇搭建的IMU软件架构,盲写代码。
   上篇IMU软件架构中,讲解了基础模块:系统时钟,那是调度的时间片基础。
   今天讲解调度模块的分支模块:状态机。

   有人说,状态机很简单嘛!几个Switch case或几个if...else,搞定!  没错,那样写的确也可以实现有限状态机,而且运行的也很好。但个人感觉代码可读性不是很好,主要原因是在实现状态之间的转换时,检查转换条件和进行状态转换都是混杂在当前状态中来完成,感觉代码会很冗杂,并且代码的可重构性和可维护性较差。


   今天我花了一天的时间写了一个状态机还有一些其他的辅助函数,先呈现给大家,大家多拍拍砖,看看写的如何,多给写建议!


  首先,我先说有限状态机的几个重要概念。

以下为网上搜索定义:

状态(State指的是对象在其生命周期中的一种状况,处于某个特定状态中的对象必然会满足某些条件、执行某些动作或者是等待某些事件。

事件(Event指的是在时间和空间上占有一定位置,并且对状态机来讲是有意义的那些事情。事件通常会引起状态的变迁,促使状态机从一种状态切换到另一种状态。

转换(Transition指的是两个状态之间的一种关系,表明对象将在第一个状态中执行一定的动作,并将在某个事件发生同时某个特定条件满足时进入第二个状态。

    动作(Action)指的是状态机中可以执行的那些原子操作,所谓原子操作指的是它们在运行的过程中不能被其他消息所中断,必须一直执行下去。


   认识了概念后,并了解了其内在含义,我们开始构建属于我们自己的状态机:


#ifndef FSM_H_
#define FSM_H_


#ifdef _cplusplus
extern "C"
{
#endif

#include "type.h"

typedef void (*Func_Event)(void);



typedef enum        
{
STATE_SLEEP = 0,
STATE_INIT,
STATE_NORMAL,
STATE_BROKEN,
...        


}EVENT_List;
typedef struct Event 
{
UINT8 ID;             //ê¼tIDoÅ
char *pName;                //ê¼tÃû3Æ
Func_Event  pEvent;   //¶Ôó|ê¼toˉêy

struct Event *pNext;
struct Event *pPrev;
        
}Event;


typedef Event *pEvent;


typedef struct State
{
UINT8 ID;               //×′ì¬ID
char *pName;                  //×′ì¬Ãû3Æ
Func_Run pState_Run;    //×′ì¬Ö′DDoˉêy
pEvent  pEventTable;    //×′ì¬ê¼t±í
        
struct State *pNext;
struct State *pPrev;        


}State;



typedef State *pState;



#ifdef _cplusplus
}
#endif


#endif /*FSM_H_ */
//------------------End of File----------------------------
SINT8 Register_States(pState pTable) //状态机注册
{
  if(pTable != NULL)
{
        
         pStateTable = pTable;     /*¸øÏμí3×′ì¬±í  */
                
         return SUCESS;        
        }
        else
        {
         return FAULT;
        }


}

状态机初始化
SINT8 States_Init(UINT8 ID,pState pStete_table)
{
pState ppState = NULL;
pState pCur = pStete_table;
   while (NULL != pCur)        
{
         if (ID == pCur->ID)//找到匹配的ID
         {
                 ppState = pCur;

                 ppState -> pState_Run(进入状态);
                 
                 break;
         }
         
         pCur = pCur->pNext;
}

}

上面是两个状态机的主要函数,一个是注册状态机,一个是初始化状态机。还有几个其他函数,就不详细列出来了,比如:通过ID查询状态,通过名称查询状态,原理都是通过链表查询,比较的方式

SINT8 State_Run(void)
{
  判断状态是否为NULL
获取当前状态ID

判断当前状态和上一周期状态是否一致,不一致则发生状态迁移

if(pCurState == pPreState)//×′ì¬Î′·¢éú±ä»ˉ
        {
         pCurState -> pState_Run运行状态);
        }

else
{
  状态迁移
}
}

当我们用该状态机的时候,只需要初始化一些状态表
建立事件表:
比如:static const pEvent Start[]
{
  {状态名,事件函数},
{}

}
建立状态表:

比如:static const pState Sys_States[]
{
  {状态,状态运行函数,事件表},
{}
}

事件表和状态表建立完毕后,我们就可以用刚才的函数进行注册初始化,运行你想要的状态了

UINT State = 0;
State |= Register_States(Sys_States);//注册状态
State |= States_Init(状态ID,状态);//初始状态



好了,我们自己编写想要的状态运行函数就OK了,这个状态机方便管理和维护,现在已经成功调试完毕,大家看看有什么问题没有?


这个帖子写的有点急,程序调试完了,代码放在这里的不全,我想起到抛砖引玉的作用,大家一起来探讨,深入思考,而不是代码一贴,一抄,了事,更多希望给大家带来一些灵感或思考吧。
0 0
原创粉丝点击