【demo】最小状态机系统

1.状态机的优点

相对于switch case能够实现的一个完整流程,使用状态机的好处是架构更加清晰,在一个调用周期内可以完成不同“case”的切换,同时可以通过状态机函数中的形参传递期望的参数,避免了在switch case中标志位,全局变量满天飞的现象。

2.函数实现

首先我们在状态机系统中需要用到的参数包括:状态机识别符、事件类型、定时器的识别符、状态机形参数据类型以及定时器参数数据类型,下面给出需要用到的结构体定义

/*Time machine state variable struct*/
typedef enum AppSmState_Tag{
    APP_STATE_PROCESS1,
    APP_STATE_PROCESS2,
    APP_STATE_PROCESS3
}AppSmState_ENUM;

/*Time machine event enum*/

typedef enum AppSmEvent_Tag{
    APP_EVENT_INIT,
    APP_EVENT_TIMEOUT,
    APP_EVENT_MESSAGE
}AppSmEvent_ENUM;

/*Time machine timer enum*/

typedef enum AppSmTimer_Tag{
    APP_TIMER_TIMER0,
    APP_TIMER_TIMER1,
    APP_TIMER_TIMER2,
    APP_TIMER_TIMEREND
}AppSmTimer_ENUM;

/*Time machine data struct*/
typedef struct AppSmData_Tag{
    AppSmEvent_ENUM Eventype;
    union
    {
        AppSmTimer_ENUM AppTimer;
        unsigned int    MessageID;
    }Data;
}AppSmData_ST;

/*Time machine timer struct*/

typedef struct AppSmTimeCnt_Tag{
    BOOL bperiodic;
    U16 Res;
    U16 Cnt;
}AppSmTimer_ST;

其次,我们在状态机系统中涉及到的操作包括状态机初始化,定时器初始化,状态机切换,消息发送函数,开启定时器,关闭定时器,定时器管理函数,定时器超时函数,下面给出这些函数的定义:

/*state machine variable*/
AppSmState_ENUM app_estate;
AppSmData_ST app_edata;
AppSmTimer_ST app_etimer[APP_TIMER_TIMEREND];
U8 MsgSendFlag = 0;

/*************************************************
Function:      int main(void)
Description:     main function
Input:             none
Output:         none
Return:          none
Others:             none
*************************************************/  
int main(void)
{
    (void)AppTminit();
    (void)AppSminit();
    while(1)
    {
        AppTmProcess();
        if(0u != MsgSendFlag)
        {
            AppSmMsg(MsgSendFlag);
            MsgSendFlag = 0;
        }
    }
}

/*************************************************
Function:      BOOL AppSm(AppSmData_ST appsmdata)
Description:     state machine handle
Input:             AppSmData_ST appsmdata
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/  
BOOL AppSm(AppSmData_ST *appsmdata)
{
    static AppSmState_ENUM ePrestate = app_estate;
    BOOL ret = TRUE;

    /*if state changes, event back to init,according to current state ,jump to corresponding state*/
    do
    {
        if(ePrestate != app_estate)
        {
            appsmdata->Eventype = APP_EVENT_INIT;
        }
        else
        {
            /*do nothing*/
        }
        ePrestate = app_estate;
        switch(ePrestate)
        {
            case APP_STATE_PROCESS1:
            AppSmProcess1(appsmdata);
            break;
            
            case APP_STATE_PROCESS2:
            AppSmProcess2(appsmdata);
            break;

            case APP_STATE_PROCESS3:
            AppSmProcess3(appsmdata);
            break;

            default:
            ret = FALSE;
            printf("appstate wrong = %d\n",ePrestate);
            break;
        }
    }
    while((ePrestate != app_estate) &&(TRUE == ret));
    return ret;
}

/*************************************************
Function:      BOOL AppSmMsg(AppSmData_ST appsmdata)
Description:     Msg handle
Input:             AppSmData_ST appsmdata.data.messageid
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/  
BOOL AppSmMsg(U16 MessageId)
{
    BOOL ret = TRUE;

    printf("send messageID = %u \n", MessageId);
    switch(MessageId)
    {
        case 0:
        {                            
            memset(&app_edata, 0, sizeof(AppSmData_ST));
            app_edata.Data.MessageID = MessageId;
            app_edata.Eventype = APP_EVENT_MESSAGE;
            ret = AppSm(&app_edata);            
        }            
        break;
        case 1:
        {
            memset(&app_edata, 0, sizeof(AppSmData_ST));
            app_edata.Data.MessageID = MessageId;
            app_edata.Eventype = APP_EVENT_MESSAGE;
            ret = AppSm(&app_edata);            
        }            
        break;
        case 2:
        {
            memset(&app_edata, 0, sizeof(AppSmData_ST));
            app_edata.Data.MessageID = MessageId;
            app_edata.Eventype = APP_EVENT_MESSAGE;
            ret = AppSm(&app_edata);            
        }
        break;
        case 3:
        {
            memset(&app_edata, 0, sizeof(AppSmData_ST));
            app_edata.Data.MessageID = MessageId;
            app_edata.Eventype = APP_EVENT_MESSAGE;
            ret = AppSm(&app_edata);            
        }
        break;
        default:
        ret = FALSE;
        printf("MessageId wrong = %d\n",MessageId);            
        break;
    }
    return ret;
}


/*************************************************
Function:      BOOL AppSmTimeout(AppSmData_ST appsmdata)
Description:     Timeout handle
Input:             AppSmTimer_ENUM eTimer
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/  
BOOL AppSmTimeout(AppSmTimer_ENUM eTimer)
{
    BOOL ret = TRUE;

    switch(eTimer)
    {
        case APP_TIMER_TIMER0:
        {
            memset(&app_edata, 0, sizeof(AppSmData_ST));
            app_edata.Eventype = APP_EVENT_TIMEOUT;
            app_edata.Data.AppTimer = eTimer;    
            ret = AppSm(&app_edata);                
        }
        break;
        case APP_TIMER_TIMER1:
        {
            memset(&app_edata, 0, sizeof(AppSmData_ST));
            app_edata.Eventype = APP_EVENT_TIMEOUT;
            app_edata.Data.AppTimer = eTimer;    
            ret = AppSm(&app_edata);                
        }

        break;
        case APP_TIMER_TIMER2:
        {
            memset(&app_edata, 0, sizeof(AppSmData_ST));
            app_edata.Eventype = APP_EVENT_TIMEOUT;
            app_edata.Data.AppTimer = eTimer;    
            ret = AppSm(&app_edata);            
        }
        break;

        default:
        ret = FALSE;
        printf("eTimer wrong = %d\n",eTimer);            
        break;
    }
    return ret;
}

/*************************************************
Function:      BOOL AppSmTimeout(AppSmData_ST appsmdata)
Description:     Timeout handle
Input:             AppSmTimer_ENUM eTimer
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/
void AppSmChange(AppSmState_ENUM eNextstate)
{
    if(app_estate != eNextstate)
    {
        printf("state change from %d to %d\n", app_estate, eNextstate);
    }
    else
    {
        /*do nothing*/
    }
    /*stop all timer reserve*/
    app_estate = eNextstate;
    //AppTmStopAll();
}

/*************************************************
Function:      BOOL AppSminit(void)
Description:     data init handle
Input:             none
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/
BOOL AppSminit(void)
{
    memset(&app_edata, 0, sizeof(AppSmData_ST));
    app_edata.Eventype = APP_EVENT_INIT;
    return AppSm(&app_edata);
}

/*************************************************
Function:      BOOL AppSminit(void)
Description:     timer init handle
Input:             none
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/
BOOL AppTminit(void)
{
    memset(app_etimer, 0, sizeof(app_etimer));
    return TRUE;
}

/*************************************************
Function:      void AppTmStart(AppSmTimer_ENUM eTimer, BOOL ePeriodic, U16 eRes)
Description:     start timer
Input:             AppSmTimer_ENUM eTimer, BOOL ePeriodic, U16 eRes
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/
void AppTmStart(AppSmTimer_ENUM eTimer, BOOL ePeriodic, U16 eRes)
{
    if(APP_TIMER_TIMEREND > eTimer)
    {
        printf("start timer %d\n", eTimer);
        app_etimer[eTimer].bperiodic = ePeriodic;
        app_etimer[eTimer].Res = eRes;
        app_etimer[eTimer].Cnt = 0;
    }
    else
    {
        printf("satrt tinmer out of range %d\n", eTimer);
    }
}

/*************************************************
Function:      void AppTmStart(AppSmTimer_ENUM eTimer, BOOL ePeriodic, U16 eRes)
Description:     start timer
Input:             AppSmTimer_ENUM eTimer, BOOL ePeriodic, U16 eRes
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/
void AppTmStop(AppSmTimer_ENUM eTimer)
{
    if(APP_TIMER_TIMEREND > eTimer)
    {
        printf("Stop timer %d\n", eTimer);
        app_etimer[eTimer].bperiodic = FALSE;
        app_etimer[eTimer].Res = 0;
        app_etimer[eTimer].Cnt = 0;
    }
    else
    {
        printf("stop tinmer out of range %d\n", eTimer);
    }
}

/*************************************************
Function:      void AppTmStopAll(void)
Description:     stop all timer
Input:             none
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/
void AppTmStopAll(void)
{
    AppTmStop(APP_TIMER_TIMER0);
    AppTmStop(APP_TIMER_TIMER1);
    AppTmStop(APP_TIMER_TIMER2);
}


/*************************************************
Function:      void AppTm(void)
Description:     timer handle
Input:             AppSmTimer_ENUM eTimer, BOOL ePeriodic, U16 eRes
Output:         none
Return:          TRUE/FALSE
Others:             
*************************************************/
void AppTmProcess(void)
{
    static U16 TimeCnt = 0;
    U16 index = 0;

    if(10000u > TimeCnt)
    {
        TimeCnt ++;
    }
    else
    {
        TimeCnt = 0;
        for(index = APP_TIMER_TIMER0; index < APP_TIMER_TIMEREND; index ++)
        {
            if(FALSE != app_etimer[index].Res)
            {
                app_etimer[index].Cnt ++;
                if(app_etimer[index].Cnt >= app_etimer[index].Res)
                {
                    app_etimer[index].Cnt = 0;
                    if(FALSE == app_etimer[index].bperiodic)
                    {
                        /*once timer*/
                        app_etimer[index].Res = 0;
                    }
                    else
                    {
                        /*periodic timer*/
                        /*do nothing */
                    }
                    printf("timeout id = %u \n", index);
                    AppSmTimeout((AppSmTimer_ENUM)index);
                }
            }
            
        }
    }
}

最后,构建三个简单的状态机来验证我们的架构是否完整,基本逻辑为在init事件中开启定时器,定时器超时后设置消息发送标志位,主函数中轮询该标志位的状态,发送对应消息给状态机,接收到该消息后切换到下一个状态机状态,以此循环

/*************************************************
Function:      void AppSmProcess1(AppSmData_ST* p_edata)
Description:     process 1 function
Input:             AppSmData_ST* p_edata
Output:         none
Return:          none
Others:             
*************************************************/
void AppSmProcess1(AppSmData_ST* p_edata)
{
    switch(p_edata->Eventype)
    {
        case APP_EVENT_INIT:
        {
            AppTmStart(APP_TIMER_TIMER0, 0, 500);
        }
        break;

        case APP_EVENT_TIMEOUT:
        {
            if(APP_TIMER_TIMER0 == p_edata->Data.AppTimer)
            {
                MsgSendFlag = 1;
            }                
        }
        break;

        case APP_EVENT_MESSAGE:
        {
            if(1u == p_edata->Data.MessageID)
            {
                AppSmChange(APP_STATE_PROCESS2);
            }
        }
        break;

        default:
        break;
    }
}

/*************************************************
Function:      void AppSmProcess2(AppSmData_ST* p_edata)
Description:     process 2 function
Input:             AppSmData_ST* p_edata
Output:         none
Return:          none
Others:             
*************************************************/
void AppSmProcess2(AppSmData_ST* p_edata)
{
    switch(p_edata->Eventype)
    {
        case APP_EVENT_INIT:
        {
            AppTmStart(APP_TIMER_TIMER1, 0, 500);
        }
        break;

        case APP_EVENT_TIMEOUT:
        {
            if(APP_TIMER_TIMER1 == p_edata->Data.AppTimer)
            {
                MsgSendFlag = 2;
            }                
        }
        break;

        case APP_EVENT_MESSAGE:
        {
            if(2u == p_edata->Data.MessageID)
            {
                AppSmChange(APP_STATE_PROCESS3);
            }
        }


        default:
        break;
    }
}

/*************************************************
Function:      void AppSmProcess3(AppSmData_ST* p_edata)
Description:     process 3 function
Input:             AppSmData_ST* p_edata
Output:         none
Return:          none
Others:             
*************************************************/
void AppSmProcess3(AppSmData_ST* p_edata)
{
    switch(p_edata->Eventype)
    {
        case APP_EVENT_INIT:
        {
            AppTmStart(APP_TIMER_TIMER2, 0, 500);
        }
        break;
        
        case APP_EVENT_TIMEOUT:
        {
            if(APP_TIMER_TIMER2 == p_edata->Data.AppTimer)
            {
                MsgSendFlag = 3;
            }                
        }
        break;

        case APP_EVENT_MESSAGE:
        {
            if(3u == p_edata->Data.MessageID)
            {
                AppSmChange(APP_STATE_PROCESS1);
            }
        }
        default:
        break;
    }
}

至此,我们完成了一个最小状态机系统的构建。

tips:

1.状态机函数中传递的形参的数据类型以及我们要发送和接收的数据类型都可根据需求进行变更

2.谨记AppSm在一个周期内不可被重复调用(这也是我们在构建状态机函数时,没有在timeout时直接发送msg的原因),否则会出现函数嵌套的现象发生。

猜你喜欢

转载自blog.csdn.net/syc233588377/article/details/84745625