《電子技術(shù)應(yīng)用》
您所在的位置:首頁(yè) > 可編程邏輯 > 業(yè)界動(dòng)態(tài) > 教學(xué):?jiǎn)纹瑱C(jī)狀態(tài)機(jī)編程詳解

教學(xué):?jiǎn)纹瑱C(jī)狀態(tài)機(jī)編程詳解

2022-08-31
來(lái)源:?jiǎn)纹瑱C(jī)與嵌入式
關(guān)鍵詞: 單片機(jī) 狀態(tài)機(jī)

  玩單片機(jī)還可以,各個(gè)外設(shè)也都會(huì)驅(qū)動(dòng),但是如果讓你完整的寫(xiě)一套代碼時(shí),卻無(wú)邏輯與框架可言。這說(shuō)明編程還處于比較低的水平,你需要學(xué)會(huì)一種好的編程框架或者一種編程思想!比如模塊化編程、狀態(tài)機(jī)編程、分層思想等,相關(guān)推薦:分享兩種單片機(jī)編程思想。

  本文來(lái)說(shuō)一下?tīng)顟B(tài)機(jī)編程。

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

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

  狀態(tài)(state)

  遷移(transiTIon)

  事件(event)

  動(dòng)作(acTIon)

  條件(guard)

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

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

  遷移:系統(tǒng)從一個(gè)狀態(tài)轉(zhuǎn)移到另一個(gè)狀態(tài)的過(guò)程稱作遷移,遷移不是自動(dòng)發(fā)生的,需要外界對(duì)系統(tǒng)施加影響。停轉(zhuǎn)的電動(dòng)機(jī)自己不會(huì)轉(zhuǎn)起來(lái),讓它轉(zhuǎn)起來(lái)必須上電。

  事件:某一時(shí)刻發(fā)生的對(duì)系統(tǒng)有意義的事情,狀態(tài)機(jī)之所以發(fā)生狀態(tài)遷移,就是因?yàn)槌霈F(xiàn)了事件。對(duì)電動(dòng)機(jī)來(lái)講,加正電壓、加負(fù)電壓、斷電就是事件。

  動(dòng)作:在狀態(tài)機(jī)的遷移過(guò)程中,狀態(tài)機(jī)會(huì)做出一些其它的行為,這些行為就是動(dòng)作,動(dòng)作是狀態(tài)機(jī)對(duì)事件的響應(yīng)。給停轉(zhuǎn)的電動(dòng)機(jī)加正電壓,電動(dòng)機(jī)由停轉(zhuǎn)狀態(tài)遷移到正轉(zhuǎn)狀態(tài),同時(shí)會(huì)啟動(dòng)電機(jī),這個(gè)啟動(dòng)過(guò)程可以看做是動(dòng)作,也就是對(duì)上電事件的響應(yīng)。

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

  舉個(gè)例子

  要解決的問(wèn)題

  電路如下圖:

  器件包括單片機(jī)MCU、一按鍵K0、LED燈L1和L2。

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

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

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

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

 

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

  狀態(tài)轉(zhuǎn)換圖

  在狀態(tài)機(jī)編程中,正確的順序應(yīng)該是先有狀態(tài)轉(zhuǎn)換圖,后有程序,程序應(yīng)該是根據(jù)設(shè)計(jì)好的狀態(tài)圖寫(xiě)出來(lái)的。

  下面這張按鍵控制流水燈狀態(tài)轉(zhuǎn)換圖,是用UML(統(tǒng)一建模語(yǔ)言)的語(yǔ)法元素畫(huà)出來(lái)的,語(yǔ)法不是很標(biāo)準(zhǔn),但拿來(lái)解釋問(wèn)題足夠了。

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

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

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

  圖中的文字內(nèi)容是對(duì)遷移的說(shuō)明,格式是:事件[條件]/動(dòng)作列表(后兩項(xiàng)可選)。

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

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

  圖中有一個(gè)黑色實(shí)心圓點(diǎn),表示狀態(tài)機(jī)在工作之前所處的一種不可知的狀態(tài),在運(yùn)行之前狀態(tài)機(jī)必須強(qiáng)制地由這個(gè)狀態(tài)遷移到初始狀態(tài),這個(gè)遷移可以有動(dòng)作列表(如圖1所示),但不需要事件觸發(fā)。

  圖中還有一個(gè)包含黑色實(shí)心圓點(diǎn)的圓圈,表示狀態(tài)機(jī)生命周期的結(jié)束,這個(gè)例子中的狀態(tài)機(jī)生生不息,所以沒(méi)有狀態(tài)指向該圓圈。

  程序代碼

  下面是根據(jù)上述狀態(tài)轉(zhuǎn)換圖寫(xiě)成的代碼:

  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); /*輸出動(dòng)作*/    g_stFSM.u8KeyCnt = 0;    g_stFSM.u8LedStat = LS_ONOFF; /*狀態(tài)遷移*/break;case LS_ONOFF:    led_on(LED2); /*輸出動(dòng)作*/    g_stFSM.u8KeyCnt = 0;    g_stFSM.u8LedStat = LS_ONON; /*狀態(tài)遷移*/break;case LS_ONON:    led_off(LED1); /*輸出動(dòng)作*/    g_stFSM.u8KeyCnt = 0;    g_stFSM.u8LedStat = LS_OFFON; /*狀態(tài)遷移*/break;case LS_OFFON:    led_off(LED2); /*輸出動(dòng)作*/    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; /*恢復(fù)初始狀態(tài)*/break;  } }else {  g_stFSM.u8KeyCnt++; /*狀態(tài)不遷移,僅記錄擊鍵次數(shù)*/ }}

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

  再看一下g_stFSM這個(gè)狀態(tài)機(jī)結(jié)構(gòu)體變量,它有兩個(gè)成員:u8LedStat和 u8KeyCnt。用這個(gè)結(jié)構(gòu)體來(lái)做狀態(tài)機(jī)好像有點(diǎn)兒?jiǎn)拢覀兡懿荒苤挥靡粋€(gè)像 u8LedStat 這樣的整型變量來(lái)做狀態(tài)機(jī)呢?

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

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

  同樣的功能改動(dòng),如果用g_stFSM這個(gè)結(jié)構(gòu)體來(lái)實(shí)現(xiàn)狀態(tài)機(jī)的話,函數(shù)fsm_active()只需要將if(g_stFSM.u8KeyCnt>3)改為if(g_stFSM.u8KeyCnt > 98)就可以了!

  g_stFSM結(jié)構(gòu)體的兩個(gè)成員中,u8LedStat可以看作是質(zhì)變因子,相當(dāng)于主變量;u8KeyCnt可以看作是量變因子,相當(dāng)于輔助變量。量變因子的逐步積累會(huì)引發(fā)質(zhì)變因子的變化。

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



更多信息可以來(lái)這里獲取==>>電子技術(shù)應(yīng)用-AET<< 

mmexport1621241704608.jpg

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