《電子技術應用》
您所在的位置:首頁 > 可編程邏輯 > 業(yè)界動態(tài) > 教學:單片機狀態(tài)機編程詳解

教學:單片機狀態(tài)機編程詳解

2022-08-31
來源:單片機與嵌入式
關鍵詞: 單片機 狀態(tài)機

  玩單片機還可以,,各個外設也都會驅動,,但是如果讓你完整的寫一套代碼時,卻無邏輯與框架可言,。這說明編程還處于比較低的水平,,你需要學會一種好的編程框架或者一種編程思想!比如模塊化編程,、狀態(tài)機編程,、分層思想等,相關推薦:分享兩種單片機編程思想,。

  本文來說一下狀態(tài)機編程,。

  什么是狀態(tài)機?

  狀態(tài)機(state machine)有5個要素:

  狀態(tài)(state)

  遷移(transiTIon)

  事件(event)

  動作(acTIon)

  條件(guard)

  狀態(tài):一個系統(tǒng)在某一時刻所存在的穩(wěn)定的工作情況,,系統(tǒng)在整個工作周期中可能有多個狀態(tài),。例如一部電動機共有正轉、反轉,、停轉這 3 種狀態(tài),。

  一個狀態(tài)機需要在狀態(tài)集合中選取一個狀態(tài)作為初始狀態(tài),。

  遷移:系統(tǒng)從一個狀態(tài)轉移到另一個狀態(tài)的過程稱作遷移,遷移不是自動發(fā)生的,,需要外界對系統(tǒng)施加影響,。停轉的電動機自己不會轉起來,讓它轉起來必須上電,。

  事件:某一時刻發(fā)生的對系統(tǒng)有意義的事情,,狀態(tài)機之所以發(fā)生狀態(tài)遷移,就是因為出現(xiàn)了事件,。對電動機來講,,加正電壓、加負電壓,、斷電就是事件,。

  動作:在狀態(tài)機的遷移過程中,狀態(tài)機會做出一些其它的行為,,這些行為就是動作,,動作是狀態(tài)機對事件的響應。給停轉的電動機加正電壓,,電動機由停轉狀態(tài)遷移到正轉狀態(tài),,同時會啟動電機,這個啟動過程可以看做是動作,,也就是對上電事件的響應,。

  條件:狀態(tài)機對事件并不是有求必應的,有了事件,,狀態(tài)機還要滿足一定的條件才能發(fā)生狀態(tài)遷移,。還是以停轉狀態(tài)的電動機為例,雖然合閘上電了,,但是如果供電線路有問題的話,,電動機還是不能轉起來。

  舉個例子

  要解決的問題

  電路如下圖:

  器件包括單片機MCU,、一按鍵K0,、LED燈L1和L2。

  實現(xiàn)功能描述:

  L1L2狀態(tài)轉換順序OFF/OFF--->ON/OFF--->ON/ON--->OFF/ON--->OFF/OFF

  通過按鍵控制L1L2的狀態(tài),,每次狀態(tài)轉換需連續(xù)按鍵5次

  L1L2的初始狀態(tài)OFF/OFF

 

3a579106-26f8-11ed-ba43-dac502259ad0.png

  狀態(tài)轉換圖

  在狀態(tài)機編程中,,正確的順序應該是先有狀態(tài)轉換圖,后有程序,,程序應該是根據設計好的狀態(tài)圖寫出來的,。

  下面這張按鍵控制流水燈狀態(tài)轉換圖,是用UML(統(tǒng)一建模語言)的語法元素畫出來的,語法不是很標準,,但拿來解釋問題足夠了,。

3a677ad0-26f8-11ed-ba43-dac502259ad0.png

  上圖中,圓角矩形代表狀態(tài)機的各個狀態(tài),,里面標注著狀態(tài)的名稱,。

  帶箭頭的直線或弧線代表狀態(tài)遷移,起于初態(tài),,止于次態(tài)。

  圖中的文字內容是對遷移的說明,,格式是:事件[條件]/動作列表(后兩項可選),。

  “事件[條件]/動作列表”要說明的意思是:如果在某個狀態(tài)下發(fā)生了“事件”,并且狀態(tài)機

  滿足“[條件]”,,那么就要執(zhí)行此次狀態(tài)轉移,,同時要產生一系列“動作”,以響應事件,。在這個例子里,,我用“KEY”表示擊鍵事件。

  圖中有一個黑色實心圓點,,表示狀態(tài)機在工作之前所處的一種不可知的狀態(tài),,在運行之前狀態(tài)機必須強制地由這個狀態(tài)遷移到初始狀態(tài),這個遷移可以有動作列表(如圖1所示),,但不需要事件觸發(fā),。

  圖中還有一個包含黑色實心圓點的圓圈,表示狀態(tài)機生命周期的結束,,這個例子中的狀態(tài)機生生不息,,所以沒有狀態(tài)指向該圓圈。

  程序代碼

  下面是根據上述狀態(tài)轉換圖寫成的代碼:

  void main(void){ sys_init(),; led_off(LED1),; led_off(LED2); g_stFSM.u8LedStat = LS_OFFOFF; g_stFSM.u8KeyCnt = 0;while(1) {if(test_key()==TRUE)  {   fsm_acTIve(),;  }else  {   ; /*idle code*/  } }}void fsm_acTIve(void){if(g_stFSM.u8KeyCnt > 3) /*擊鍵是否滿 5 次*/ {switch(g_stFSM.u8LedStat)  {case LS_OFFOFF:    led_on(LED1),; /*輸出動作*/    g_stFSM.u8KeyCnt = 0;    g_stFSM.u8LedStat = LS_ONOFF; /*狀態(tài)遷移*/break;case LS_ONOFF:    led_on(LED2); /*輸出動作*/    g_stFSM.u8KeyCnt = 0;    g_stFSM.u8LedStat = LS_ONON; /*狀態(tài)遷移*/break;case LS_ONON:    led_off(LED1),; /*輸出動作*/    g_stFSM.u8KeyCnt = 0;    g_stFSM.u8LedStat = LS_OFFON; /*狀態(tài)遷移*/break;case LS_OFFON:    led_off(LED2),; /*輸出動作*/    g_stFSM.u8KeyCnt = 0;    g_stFSM.u8LedStat = LS_OFFOFF; /*狀態(tài)遷移*/break;default: /*非法狀態(tài)*/    led_off(LED1);    led_off(LED2),;    g_stFSM.u8KeyCnt = 0;    g_stFSM.u8LedStat = LS_OFFOFF; /*恢復初始狀態(tài)*/break;  } }else {  g_stFSM.u8KeyCnt++; /*狀態(tài)不遷移,,僅記錄擊鍵次數*/ }}

  先看一下fsm_active()這個函數,g_stFSM.u8KeyCnt = 0;這個語句在switch—case里共出現(xiàn)了 5 次,,前 4 次是作為各個狀態(tài)遷移的動作出現(xiàn)的,。從代碼簡化提高效率的角度來看,,我們完全可以把這 5 次合并為 1 次放在 switch—case 語句之前,兩者的效果是完全一樣的,,代碼里之所以這樣啰嗦,,是為了清晰地表明每次狀態(tài)遷移中所有的動作細節(jié),這種方式和上面狀態(tài)轉換圖所要表達的意圖是完全一致的,。

  再看一下g_stFSM這個狀態(tài)機結構體變量,,它有兩個成員:u8LedStat和 u8KeyCnt。用這個結構體來做狀態(tài)機好像有點兒啰嗦,,我們能不能只用一個像 u8LedStat 這樣的整型變量來做狀態(tài)機呢,?

  當然可以!我們把上圖中的這 4 個狀態(tài)各自拆分成 5 個小狀態(tài),,這樣用 20 個狀態(tài)同樣能實現(xiàn)這個狀態(tài)機,,而且只需要一個 unsigned char 型的變量就足夠了,每次擊鍵都會引發(fā)狀態(tài)遷移,, 每遷移 5 次就能改變一次 LED 燈的狀態(tài),,從外面看兩種方法的效果完全一樣。

  假設我把功能要求改一下,,把連續(xù)擊鍵5次改變L1L2的狀態(tài)改為連續(xù)擊鍵100次才能改變L1L2的狀態(tài),。這樣的話第二種方法需要4X100=400個狀態(tài)!而且函數fsm_active()中的switch—case語句里要有400個case,,這樣的程序還有法兒寫么,?!

  同樣的功能改動,,如果用g_stFSM這個結構體來實現(xiàn)狀態(tài)機的話,,函數fsm_active()只需要將if(g_stFSM.u8KeyCnt>3)改為if(g_stFSM.u8KeyCnt > 98)就可以了!

  g_stFSM結構體的兩個成員中,,u8LedStat可以看作是質變因子,,相當于主變量;u8KeyCnt可以看作是量變因子,,相當于輔助變量,。量變因子的逐步積累會引發(fā)質變因子的變化。

  像g_stFSM這樣的狀態(tài)機被稱作Extended State Machine,。



更多信息可以來這里獲取==>>電子技術應用-AET<< 

mmexport1621241704608.jpg

本站內容除特別聲明的原創(chuàng)文章之外,,轉載內容只為傳遞更多信息,并不代表本網站贊同其觀點,。轉載的所有的文章,、圖片、音/視頻文件等資料的版權歸版權所有權人所有。本站采用的非本站原創(chuàng)文章及圖片等內容無法一一聯(lián)系確認版權者,。如涉及作品內容,、版權和其它問題,請及時通過電子郵件或電話通知我們,,以便迅速采取適當措施,,避免給雙方造成不必要的經濟損失。聯(lián)系電話:010-82306118,;郵箱:[email protected],。