前言
功能業(yè)務(wù)代碼寫多了,回看代碼會發(fā)現(xiàn)用if-else if-else
用的越來越多,因為有很多場景需要區(qū)分,不同場景下的功能不一樣,因此需要通過if
做判斷,場景一旦多了起來起來,用if-else if-else
就會越來越多。
這里的
if-else if-else
通常是用來區(qū)分不同場景下的功能實現(xiàn),和上篇提到的表驅(qū)動編程還不太一樣。
為了避免這種情況,本編介紹一種開發(fā)方法-- 狀態(tài)機編程 。
為什么需要狀態(tài)機編程?相信很多人在實現(xiàn)
if
判斷的時候,偶爾會出現(xiàn)不小心漏掉或者寫錯一些觸發(fā)條件,導(dǎo)致功能出現(xiàn)異常的問題,特別是一些復(fù)雜的邏輯條件,一大堆的&&
/||
/()
等眼花繚亂的復(fù)雜邏輯,寫完之后恐怕自己都得檢查好一會,擔(dān)心是不是哪個條件不能正常觸發(fā)。狀態(tài)機通常采用switch-case
實現(xiàn)。那么狀態(tài)機的引入解決了哪些問題呢?
- 當(dāng)程序有多個狀態(tài)時,規(guī)范了程序的狀態(tài)轉(zhuǎn)換,避免了一些引入一些復(fù)雜的判斷邏輯。
- 規(guī)范了程序在不同狀態(tài)下的實現(xiàn)和所能提供的能力。
- 在能力上可以進行橫向擴展,提供新的狀態(tài)來完善現(xiàn)有邏輯
- 邏輯清楚,實現(xiàn)過程會多考慮一些情況,方便定位問題所在
介紹
什么是狀態(tài)機?
狀態(tài)機是有限狀態(tài)自動機(FSM)的簡稱,是現(xiàn)實事物運行規(guī)則抽象而成的一個 數(shù)學(xué)模型 。
簡單理解就是:現(xiàn)實事物是有不同狀態(tài)的,比如燈,就有“亮”和“滅”兩種狀態(tài),再復(fù)雜抽象一點,增加一個“損壞”狀態(tài),那這屬于異常情況了。
概念
- 現(xiàn)態(tài):當(dāng)前所處的狀態(tài),一個狀態(tài)機至少要包含兩個狀態(tài),某一時刻只能是一種狀態(tài),比如剛才說到的燈,就有“亮”和“滅”兩種狀態(tài)
- 條件:又稱事件,執(zhí)行某個操作的觸發(fā)條件或者口令,比如燈通過開關(guān)控制,操作開關(guān)就是一個事件
- 動作:事件發(fā)生以后要執(zhí)行動作,比如開關(guān)按下開關(guān),燈亮,松開則滅
- 次態(tài):條件滿足后要遷往的新狀態(tài),比如開關(guān)按下后燈從當(dāng)前滅的狀態(tài)變?yōu)榱恋臓顟B(tài)
用過"RTOS"的朋友應(yīng)該也知道任務(wù)的幾種狀態(tài),任務(wù)的四種狀態(tài)分別是: 就緒態(tài)、運行態(tài)、阻塞態(tài)、掛起態(tài) ,不會同時出現(xiàn)兩種及兩種以上的狀態(tài)存在,OS 根據(jù)當(dāng)前的狀態(tài),和任務(wù)優(yōu)先級、滴答時鐘、主動睡眠等條件進行任務(wù)的狀態(tài)切換。
狀態(tài)機的動作類型
- 進入動作:在進入狀態(tài)時進行
- 退出動作:在退出狀態(tài)時進行
- 輸入動作:依賴于當(dāng)前狀態(tài) 和 輸入條件進行
- 轉(zhuǎn)移動作:在進行特定轉(zhuǎn)移時進行
實現(xiàn)
首先,看一個簡單的例子,在不同場景下實現(xiàn)控制電機的功能:設(shè)備開機啟動三次電機、開關(guān)按下一次啟動一次、關(guān)機啟動三次電機。采用非狀態(tài)機的寫法,通過各種標(biāo)志位去判斷設(shè)備是否需要控制電機,什么條件下退出等。
只是簡單的實現(xiàn)一下,可能其中也有一些狀態(tài)機的思想吧(畢竟?fàn)顟B(tài)機編程思想已經(jīng)在腦海里,不可避免吧),不過我還是盡量還原我初次編程期間的實現(xiàn)這個功能的邏輯思想吧,勿怪。
/* 控制電機函數(shù) */
void MotorCtrlTask(void)
{
if (ctrlCnt)
{
MotorCtrl(ON);
delay(1);
MotorCtrl(OFF);
}
else
{
MotorCtrl(OFF);
}
}
int isPowerOn = true;
int isPowerOff = false;
int ctrlCnt = 0;
void main(void)
{
while (1)
{
if (isPowerOn)
{
isPowerOn = false;
ctrlCnt = 3;
}
if (keyPress)
{
keyPress = false;
ctrlCnt = 1;
}
if (...) // 關(guān)機條件
{
if (ctrlCnt == 0 && !isPowerOff && !isPowerOn)
{
isPowerOff = true;
ctrlCnt = 3;
}
}
MotorCtrlTask();
if (ctrlCnt > 0)
ctrlCnt--;
else
{
if (ctrlCnt == 0 && isPowerOff && !isPowerOn)
{
return;
}
}
}
}
通過采用狀態(tài)機編程的方式,首先考慮的就是有三種狀態(tài):開機、關(guān)機和工作狀態(tài),先理清楚三種狀態(tài)之間轉(zhuǎn)換的條件和當(dāng)前狀態(tài)需要執(zhí)行的相關(guān)功能,然而在實現(xiàn)過程中就會意識到還需要增加一種過渡狀態(tài):關(guān)機準(zhǔn)備中(關(guān)機過程中需要執(zhí)行的一系列操作)。
只有邏輯清晰了,才會下意識的察覺少了一些東西了,特別是一些臨界處理等。
int sysState = POWER_OFF; // 默認關(guān)機狀態(tài)
int ctrlCnt = 0;
/* 控制電機函數(shù) */
void MotorCtrlTask(void)
{
if (ctrlCnt)
{
MotorCtrl(ON);
delay(1);
MotorCtrl(OFF);
}
else
{
MotorCtrl(OFF);
}
}
void main(void)
{
while (1)
{
switch (sysState)
{
case POWER_OFF: // 關(guān)機狀態(tài)
sysState = POWER_ON; // 自動切換成開機狀態(tài)
ctrlCnt = 3;
break;
case POWER_ON: // 開機過程狀態(tài)
... // 開機過程中的其他功能
if (ctrlCnt == 0) // 控制結(jié)束自動切換工作狀態(tài)
{
sysState = WORKING;
break;
}
break;
case WORKING: // 工作狀態(tài)
if (...) // 關(guān)機條件
{
sysState = POWER_OFF_READY;
ctrlCnt = 3;
break;
}
if (keyPress)
{
keyPress = false;
ctrlCnt = 1;
}
break;
case POWER_OFF_READY: // 關(guān)機準(zhǔn)備中
... // 關(guān)機準(zhǔn)備中的其他功能
if (ctrlCnt == 0) // 控制結(jié)束自動退出
{
sysState = POWER_OFF;
return; // 退出程序
}
break;
default:
break;
}
MotorCtrlTask();
if (ctrlCnt > 0)
ctrlCnt--;
}
}
總結(jié):從上述兩份代碼看,你覺得哪一個邏輯更清晰呢?非狀態(tài)機實現(xiàn)方式還有一些異常處理沒有實現(xiàn),比如開機過程中在啟動三次調(diào)節(jié)電機過程中按下會有什么現(xiàn)象呢,為了避免這種情況又需要加入多少if
條件判斷呢?
適用場景
狀態(tài)機應(yīng)用范圍挺廣的,不止是在C語言中,其他都能使用,準(zhǔn)確來說這個屬于一種編程思想。特別是業(yè)務(wù)功能,狀態(tài)機是常用的。
比如常常使用的模塊也有狀態(tài)機的身影:比如按鍵的按下和松開,按下又包括按下瞬間、多次按下、持續(xù)按下、松開瞬間和持續(xù)松開等
通常狀態(tài)機和表驅(qū)動可以結(jié)合使用,狀態(tài)機的現(xiàn)態(tài)、條件、動作和次態(tài)作為數(shù)據(jù),執(zhí)行這些狀態(tài)切換的作為邏輯。完全可以根據(jù)實際情況靈活使用。
代碼參考:
菜單 :菜單控制,可以這樣理解:當(dāng)前菜單界面為現(xiàn)態(tài)、菜單進入和退出為條件、菜單切換時的函數(shù)執(zhí)行為動作、上下級菜單理解為次態(tài),其中菜單選項表就靈活使用了狀態(tài)機和表驅(qū)動的方式。
https://gitee.com/const-zpc/menu.git
ESP8266 :AT指令數(shù)據(jù)表,包含指令、期望響應(yīng)、時間和函數(shù)指針【后續(xù)的動作】等,可以這樣理解:數(shù)組索引當(dāng)前值為現(xiàn)態(tài)、收到響應(yīng)和超時為條件、收到響應(yīng)或超時執(zhí)行的函數(shù)為動作、數(shù)組索引的未來值為次態(tài)
https://gitee.com/const-zpc/esp8266
-
狀態(tài)機
+關(guān)注
關(guān)注
2文章
492瀏覽量
27673 -
fsm
+關(guān)注
關(guān)注
0文章
35瀏覽量
12847 -
數(shù)學(xué)模型
+關(guān)注
關(guān)注
0文章
83瀏覽量
11993
發(fā)布評論請先 登錄
相關(guān)推薦
嵌入式狀態(tài)機的幾種大牛才懂的操作
![<b class='flag-5'>嵌入式</b><b class='flag-5'>狀態(tài)機</b>的幾種大牛才懂的操作](https://file.elecfans.com/web2/M00/7B/63/poYBAGN1sVmAGELqAABXtigepLk573.png)
嵌入式軟件開發(fā)中常用的狀態(tài)機編程實現(xiàn)
為何要進行嵌入式軟件架構(gòu)設(shè)計?如何設(shè)計?
嵌入式之狀態(tài)機編程的概念是什么
嵌入式軟件架構(gòu)設(shè)計資料分享
狀態(tài)機在嵌入式系統(tǒng)中的應(yīng)用
![<b class='flag-5'>狀態(tài)機</b>在<b class='flag-5'>嵌入式</b>系統(tǒng)中的應(yīng)用](https://file1.elecfans.com//web2/M00/A5/E4/wKgZomUMOqaAH5aQAAAMFMmSVpg127.gif)
評論