玩單片機(jī)還可以,各個外設(shè)也都會驅(qū)動,但是如果讓你完整的寫一套代碼時,,卻無邏輯與框架可言,。這說明編程還處于比較低的水平,你需要學(xué)會一種好的編程框架或者一種編程思想,!比如模塊化編程,、狀態(tài)機(jī)編程、分層思想等,,相關(guān)推薦:分享兩種單片機(jī)編程思想,。
本文來說一下狀態(tài)機(jī)編程。
什么是狀態(tài)機(jī),?
狀態(tài)機(jī)(state machine)有5個要素:
狀態(tài)(state)
遷移(transiTIon)
事件(event)
動作(acTIon)
條件(guard)
狀態(tài):一個系統(tǒng)在某一時刻所存在的穩(wěn)定的工作情況,,系統(tǒng)在整個工作周期中可能有多個狀態(tài)。例如一部電動機(jī)共有正轉(zhuǎn),、反轉(zhuǎn),、停轉(zhuǎn)這 3 種狀態(tài)。
一個狀態(tài)機(jī)需要在狀態(tài)集合中選取一個狀態(tài)作為初始狀態(tài),。
遷移:系統(tǒng)從一個狀態(tài)轉(zhuǎn)移到另一個狀態(tài)的過程稱作遷移,,遷移不是自動發(fā)生的,需要外界對系統(tǒng)施加影響,。停轉(zhuǎn)的電動機(jī)自己不會轉(zhuǎn)起來,,讓它轉(zhuǎn)起來必須上電。
事件:某一時刻發(fā)生的對系統(tǒng)有意義的事情,,狀態(tài)機(jī)之所以發(fā)生狀態(tài)遷移,,就是因為出現(xiàn)了事件。對電動機(jī)來講,,加正電壓,、加負(fù)電壓、斷電就是事件。
動作:在狀態(tài)機(jī)的遷移過程中,,狀態(tài)機(jī)會做出一些其它的行為,,這些行為就是動作,動作是狀態(tài)機(jī)對事件的響應(yīng),。給停轉(zhuǎn)的電動機(jī)加正電壓,,電動機(jī)由停轉(zhuǎn)狀態(tài)遷移到正轉(zhuǎn)狀態(tài),同時會啟動電機(jī),,這個啟動過程可以看做是動作,,也就是對上電事件的響應(yīng)。
條件:狀態(tài)機(jī)對事件并不是有求必應(yīng)的,,有了事件,,狀態(tài)機(jī)還要滿足一定的條件才能發(fā)生狀態(tài)遷移。還是以停轉(zhuǎn)狀態(tài)的電動機(jī)為例,,雖然合閘上電了,,但是如果供電線路有問題的話,電動機(jī)還是不能轉(zhuǎn)起來,。
舉個例子
要解決的問題
電路如下圖:
器件包括單片機(jī)MCU,、一按鍵K0、LED燈L1和L2,。
實現(xiàn)功能描述:
L1L2狀態(tài)轉(zhuǎn)換順序OFF/OFF--->ON/OFF--->ON/ON--->OFF/ON--->OFF/OFF
通過按鍵控制L1L2的狀態(tài),,每次狀態(tài)轉(zhuǎn)換需連續(xù)按鍵5次
L1L2的初始狀態(tài)OFF/OFF
狀態(tài)轉(zhuǎn)換圖
在狀態(tài)機(jī)編程中,正確的順序應(yīng)該是先有狀態(tài)轉(zhuǎn)換圖,,后有程序,,程序應(yīng)該是根據(jù)設(shè)計好的狀態(tài)圖寫出來的。
下面這張按鍵控制流水燈狀態(tài)轉(zhuǎn)換圖,,是用UML(統(tǒng)一建模語言)的語法元素畫出來的,,語法不是很標(biāo)準(zhǔn),但拿來解釋問題足夠了,。
上圖中,,圓角矩形代表狀態(tài)機(jī)的各個狀態(tài),里面標(biāo)注著狀態(tài)的名稱,。
帶箭頭的直線或弧線代表狀態(tài)遷移,,起于初態(tài),止于次態(tài),。
圖中的文字內(nèi)容是對遷移的說明,,格式是:事件[條件]/動作列表(后兩項可選)。
“事件[條件]/動作列表”要說明的意思是:如果在某個狀態(tài)下發(fā)生了“事件”,,并且狀態(tài)機(jī)
滿足“[條件]”,,那么就要執(zhí)行此次狀態(tài)轉(zhuǎn)移,,同時要產(chǎn)生一系列“動作”,以響應(yīng)事件,。在這個例子里,,我用“KEY”表示擊鍵事件。
圖中有一個黑色實心圓點,,表示狀態(tài)機(jī)在工作之前所處的一種不可知的狀態(tài),,在運行之前狀態(tài)機(jī)必須強(qiáng)制地由這個狀態(tài)遷移到初始狀態(tài),這個遷移可以有動作列表(如圖1所示),,但不需要事件觸發(fā),。
圖中還有一個包含黑色實心圓點的圓圈,表示狀態(tài)機(jī)生命周期的結(jié)束,,這個例子中的狀態(tài)機(jī)生生不息,,所以沒有狀態(tài)指向該圓圈。
程序代碼
下面是根據(jù)上述狀態(tài)轉(zhuǎn)換圖寫成的代碼:
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; /*恢復(fù)初始狀態(tài)*/break; } }else { g_stFSM.u8KeyCnt++; /*狀態(tài)不遷移,僅記錄擊鍵次數(shù)*/ }}
先看一下fsm_active()這個函數(shù),,g_stFSM.u8KeyCnt = 0;這個語句在switch—case里共出現(xiàn)了 5 次,,前 4 次是作為各個狀態(tài)遷移的動作出現(xiàn)的。從代碼簡化提高效率的角度來看,,我們完全可以把這 5 次合并為 1 次放在 switch—case 語句之前,,兩者的效果是完全一樣的,代碼里之所以這樣啰嗦,,是為了清晰地表明每次狀態(tài)遷移中所有的動作細(xì)節(jié),,這種方式和上面狀態(tài)轉(zhuǎn)換圖所要表達(dá)的意圖是完全一致的。
再看一下g_stFSM這個狀態(tài)機(jī)結(jié)構(gòu)體變量,,它有兩個成員:u8LedStat和 u8KeyCnt,。用這個結(jié)構(gòu)體來做狀態(tài)機(jī)好像有點兒啰嗦,我們能不能只用一個像 u8LedStat 這樣的整型變量來做狀態(tài)機(jī)呢,?
當(dāng)然可以,!我們把上圖中的這 4 個狀態(tài)各自拆分成 5 個小狀態(tài),,這樣用 20 個狀態(tài)同樣能實現(xiàn)這個狀態(tài)機(jī),而且只需要一個 unsigned char 型的變量就足夠了,,每次擊鍵都會引發(fā)狀態(tài)遷移,, 每遷移 5 次就能改變一次 LED 燈的狀態(tài),從外面看兩種方法的效果完全一樣,。
假設(shè)我把功能要求改一下,,把連續(xù)擊鍵5次改變L1L2的狀態(tài)改為連續(xù)擊鍵100次才能改變L1L2的狀態(tài)。這樣的話第二種方法需要4X100=400個狀態(tài),!而且函數(shù)fsm_active()中的switch—case語句里要有400個case,,這樣的程序還有法兒寫么?,!
同樣的功能改動,,如果用g_stFSM這個結(jié)構(gòu)體來實現(xiàn)狀態(tài)機(jī)的話,函數(shù)fsm_active()只需要將if(g_stFSM.u8KeyCnt>3)改為if(g_stFSM.u8KeyCnt > 98)就可以了,!
g_stFSM結(jié)構(gòu)體的兩個成員中,,u8LedStat可以看作是質(zhì)變因子,相當(dāng)于主變量,;u8KeyCnt可以看作是量變因子,,相當(dāng)于輔助變量。量變因子的逐步積累會引發(fā)質(zhì)變因子的變化,。
像g_stFSM這樣的狀態(tài)機(jī)被稱作Extended State Machine,。
更多信息可以來這里獲取==>>電子技術(shù)應(yīng)用-AET<<