1. 程式人生 > >【程式設計之美】用C語言實現狀態機(實用)

【程式設計之美】用C語言實現狀態機(實用)

版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。

本文連結:https://www.cnblogs.com/lihuidashen/p/11510532.html

https://mp.weixin.qq.com/s/xDAfaEFY4INHzr7MFnR5dg

 

    關於狀態機,基礎的知識點可以自行理解,講解的很多,這裡主要是想寫一個有限狀態機FSM通用的寫法,目的在於更好理解,移植,節省程式碼閱讀與除錯時間,體現出程式設計之美。

 

傳統的實現方案
  • if...else : 搞一大堆if else, 一個函式寫很長很長......

  • swich...case : 也搞一大堆一個函式寫很長很長......

 

    先來看看最近做的一個專案,無線通訊協議實現的狀態機是什麼樣子的:

 

 

    有三種類型的事件:上層下達的命令事件;下層到達的標誌和資料傳輸事件;超時定時器超時事件。有10種狀態,關聯性很大,複雜了吧,這要是各種if/else的要寫到什麼時候呢。

 

    偷偷放一張討論的圖,亂七八糟形容很恰當。

 

    在事件中判斷狀態,在狀態中判斷事件,橫豎兩種寫法的程式碼都比較冗長,看起來呢也不大好,一旦增減,就又要動腦子重新梳理一遍,很累的。

 

    怎麼去寫呢?其狀態機原理:在根據當前狀態(cur_state) 下,發生事件(event)後,轉移到下一個狀態號(nxt_state),決定執行的動作(action)。盜用一個圖吧

 

 

    這裡我們首先定義一個結構體如下:

typedef struct {
  State curState;//當前狀態
  EventID eventId;//事件ID
  State nextState;//下個狀態
  Action action;//具體表現}
StateTransform;

 

 

    我們假設有3種狀態,這裡可以隨意增加,狀態列舉如下:

typedef enum {
  state_1=1,
  state_2,
  state_3}
State;

 

    我們假設有5個事件,也可以隨意增加,事件ID列舉如下:

typedef enum{
  event_1=1,
  event_2,
  event_3,
  event_4,
  event_5}EventID;

 

    將其封裝起來在StateMachine中:

typedef struct{
  State state;
  int transNum;
  StateTransform* transform;
}StateMachine;

 

 

    具體流程:當前狀態-有事件觸發-跳到下個狀態-具體表現,重構程式碼

StateTransform* findTranss(StateMachine* pSM,  const EventID evt){
  int i;
  for (i = 0; i < pSM->transNum; i++) {
    if ((pSM->transform[i].curState == pSM->state) && (pSM->transform[i].eventId == evt)) {
      return &pSM->transform[i];
    }
  }
  return NULL;
}

 

    狀態機實現如下:

    StateTransformm* pTrans;
    pTrans = findTrans(pSM, evt);
    if (pTrans == NULL)
    {
        xil_printf( "CurState= %s Do not process enent: %s\r\n", pSM->state,evt);
        return;
    }
    pSM->state = pTrans->nextState;
    Action act = pTrans->action;
    if (act == NULL) {
        xil_printf( "change state to %s. No action\r\n",pSM->state);
        return;
    }
    act(&evt);

 

    最後我模擬一些隨機事件,我們只需要弄清楚事件ID,狀態切換,具體表現就可以了,在程式碼中就是填寫 stateTran[] 這個表,一旦有增減事件,狀態等等,也不需要再去使用switch/case,特費腦,其程式碼如下:

int run()
{
    StateMachine stateMachine;
    stateMachine.state = state_1;
    stateMachine.transNum = 7;
    StateTransform stateTran[] = {
        {state_1,event_3,state_2,f121},
        {state_1,event_4,state_2,NULL},
        {state_2,event_1,state_3,f231},
        {state_2,event_4,state_2,f221},
        {state_3,event_2,state_1,f311},
        {state_3,event_3,state_2,f321},
        {state_3,event_5,state_3,f331}
    };
    stateMachine.transform = stateTran;

    EventID inputEvent[15] = { event_1, event_2, event_3, event_4, event_5,
        event_1, event_2, event_3, event_4, event_5,
        event_1, event_2, event_3, event_4, event_5 };

    int i;
    for (i = 0; i < 15; i++) {
        runStateMachine(&stateMachine, inputEvent[i]);
    }
    return 0;
}

 

    最後執行結果如下

 

 

總結:

    狀態機應用很廣泛,也可以鍛鍊我們寫程式碼的邏輯思維,看清問題的本質,寫的程式碼才能賞心悅目,希望大家能夠多多指點,找到程式設計的樂趣,欣賞到程式設計之美。

 

推薦閱讀

FPGA 高手養成記-Verliog語法基礎

FPGA 高手養成記-淺談狀態機

FPGA 高手養成記-Test bench檔案結構一覽無餘

FPGA 高手養成記-【很重要】Testbenth前模擬全過程

const 指標與指向const的指標

蛻變成蝶~Linux裝置驅動之字元裝置驅動

24小時學通Linux核心--核心探索工具類

機器學習理論提升方法AdaBoost演算法第一卷

 

關注公眾號【技術讓夢想更偉大】,獲取更多Linux/C/C++/Python/FPGA等原創技術文章。後臺免費獲取經典電子書籍和視訊資源,實時更新,原創不易,請多支援,謝謝!

&n