天天看點

狀态機思路在程式設計中的應用

狀态機思路在單片機程式設計中的應用

狀态機的概念

狀态機是軟體程式設計中的一個重要概念。比這個概念更重要的是對它的靈活應用。在一個思路清晰而且高效的程式中,必然有狀态機的身影浮現。

比如說一個按鍵指令解析程式,就可以被看做狀态機:本來在A狀态下,觸發一個按鍵後切換到了B狀态;再觸發另一個鍵後切換到C狀态,或者傳回到A狀态。這就是最簡單的按鍵狀态機例子。實際的按鍵解析程式會比這更複雜些,但這不影響我們對狀态機的認識。

進一步看,擊鍵動作本身也可以看做一個狀态機。一個細小的擊鍵動作包含了:釋放、抖動、閉合、抖動和重新釋放等狀态。

同樣,一個串行通信的時序(不管它是遵循何種協定,标準序列槽也好、I2C也好;也不管它是有線的、還是紅外的、無線的)也都可以看做由一系列有限的狀态構成。

顯示掃描程式也是狀态機;通信指令解析程式也是狀态機;甚至連繼電器的吸合/釋放控制、發光管(LED)的亮/滅控制又何嘗不是個狀态機。

當我們打開思路,把狀态機作為一種思想導入到程式中去時,就會找到解決問題的一條有效的捷徑。有時候用狀态機的思維去思考程式該幹什麼,比用控制流程的思維去思考,可能會更有效。這樣一來狀态機便有了更實際的功用。

程式其實就是狀态機。

也許你還不了解上面這句話。請想想看,計算機的大廈不就是建立在“0”和“1”兩個基本狀态的地基之上麼?

狀态機的要素

狀态機可歸納為4個要素,即現态、條件、動作、次态。這樣的歸納,主要是出于對狀态機的内在因果關系的考慮。“現态”和“條件”是因,“動作”和“次态”是果。詳解如下:

①現态:是指目前所處的狀态。

②條件:又稱為“事件”。當一個條件被滿足,将會觸發一個動作,或者執行一次狀态的遷移。

③動作:條件滿足後執行的動作。動作執行完畢後,可以遷移到新的狀态,也可以仍舊保持原狀态。動作不是必需的,當條件滿足後,也可以不執行任何動作,直接遷移到新狀态。

④次态:條件滿足後要遷往的新狀态。“次态”是相對于“現态”而言的,“次态”一旦被激活,就轉變成新的“現态”了。

如果我們進一步歸納,把“現态”和“次态”統一起來,而把“動作”忽略(降格處理),則隻剩下兩個最關鍵的要素,即:狀态、遷移條件。

狀态機的表示方法有許多種,我們可以用文字、圖形或表格的形式來表示一個狀态機。

純粹用文字描述是很低效的,是以就不介紹了。接下來先介紹圖形的方式。

狀态遷移圖(STD)

狀态遷移圖(STD),是一種描述系統的狀态、以及互相轉化關系的圖形方式。狀态遷移圖的畫法有許多種,不過一般都大同小異。我們結合一個例子來說明一下它的畫法,如圖1所示。

狀态機思路在程式設計中的應用

圖1 狀态遷移圖

①狀态框:用方框表示狀态,包括所謂的“現态”和“次态”。

②條件及遷移箭頭:用箭頭表示狀态遷移的方向,并在該箭頭上标注觸發條件。

③節點圓圈:當多個箭頭指向一個狀态時,可以用節點符号(小圓圈)連接配接彙總。

④動作框:用橢圓框表示。

⑤附加條件判斷框:用六角菱形框表示。

狀态遷移圖和我們常見的流程圖相比有着本質的差別,具體展現為:在流程圖中,箭頭代表了程式PC指針的跳轉;而在狀态遷移圖中,箭頭代表的是狀态的改變。

我們會發現,這種狀态遷移圖比普通程式流程圖更簡練、直覺、易懂。這正是我們需要達到的目的。

狀态遷移表

除了狀态遷移圖,我們還可以用表格的形式來表示狀态之間的關系。這種表一般稱為狀态遷移表。

表1就是前面介紹的那張狀态遷移圖的另一種描述形式。

表1 狀态遷移表

狀态機思路在程式設計中的應用

①采用表格方式來描述狀态機,優點是可容納更多的文字資訊。例如,我們不但可以在狀态遷移表中描述狀态的遷移關系,還可以把每個狀态的特征描述也包含在内。

②如果表格内容較多,過于臃腫不利于閱讀,我們也可以将狀态遷移表進行拆分。經過拆分後的表格根據其具體内容,表格名稱也有所變化。

③比如,我們可以把狀态特征和遷移關系分開清單。被單獨拆分出來的描述狀态特征的表格,也可以稱為“狀态真值表”。這其中比較常見的就是把每個狀态的顯示内容單獨清單。這種描述每個狀态顯示内容的表稱之為“顯示真值表”。同樣,我們把單獨表述基于按鍵的狀态遷移表稱為“按鍵功能真值表”。另外,如果每一個狀态包含的資訊量過多,我們也可以把每個狀态單獨清單。

④由此可見,狀态遷移表作為狀态遷移圖的有益補充,它的表現形式是靈活的。

⑤狀态遷移表優點是資訊涵蓋面大,缺點是視覺上不夠直覺,是以它并不能取代狀态遷移圖。比較理想的是将圖形和表格結合應用。用圖形展現宏觀,用表格說明細節。二者互為參照,相得益彰。

用狀态機思路實作一個時鐘程式

接下來,我将就狀态機的應用,結合流程圖、狀态遷移圖和狀态遷移,舉一個實際例子。下面這張圖是一個時鐘程式的狀态遷移圖,如圖2所示。

狀态機思路在程式設計中的應用

圖2 時鐘程式狀态遷移圖

把這張圖稍做歸納,就可以得到它的另一種表現形式——狀态遷移表,如表2所示。

表2 時鐘程式狀态遷移表

狀态機思路在程式設計中的應用

狀态機應用的注意事項

基于狀态機的程式排程機制,其應用的難點并不在于對狀态機概念的了解,而在于對系統工作狀态的合理劃分。

初學者往往會把某個“程式動作”當作是一種“狀态”來處理。我稱之為“僞态”。那麼如何區分“動作”和“狀态”。本匠人的心得是看二者的本質:“動作”是不穩定的,即使沒有條件的觸發,“動作”一旦執行完畢就結束了;而“狀态”是相對穩定的,如果沒有外部條件的觸發,一個狀态會一直持續下去。

初學者的另一種比較緻命的錯誤,就是在狀态劃分時漏掉一些狀态。我稱之為“漏态”。

“僞态”和“漏态”這兩種錯誤的存在,将會導緻程式結構的渙散。是以要特别小心避免。

更複雜的狀态機

前面介紹的是一種簡單的狀态結構。它隻有一級,并且隻有一維,如圖3所示。

狀态機思路在程式設計中的應用

圖3  線性狀态機結構

如果有必要,我們可以建立更複雜的狀态機模型。

1 多級狀态結構

狀态機可以是多級的。在分層的多級狀态機系統裡面,一個“父狀态”下可以劃分多個“子狀态”,這些子狀态共同擁有上級父狀态的某些共性,同時又各自擁有自己的一些個性。

在某些狀态下,還可以進一步劃分子狀态。比如,我們可以把前面的時鐘例子修改如下:

把所有和時鐘功能有關的狀态,合并成1個一級狀态。在這個狀态下,又可以劃分出3個二級子狀态,分别為顯示時間、設定小時、設定分鐘;

同樣,我們也可以把所有和鬧鐘功能有關的狀态,合并成1個一級狀态。在這個狀态下,再劃分出4個二級子狀态,分别為顯示鬧鐘、設定“時”、設定“分”、設定鳴叫時間。

我們需要用另一個狀态變量(寄存器)來表示這些子狀态。

子狀态下面當然還可以有更低一級的孫狀态(子子孫孫無窮盡也),進而将整個狀态體系變成了樹狀多級狀态結構,如圖4所示。

狀态機思路在程式設計中的應用

圖4 樹狀多級狀态結構

2 多元狀态結構

狀态結構也可以是多元的。從不同的角度對系統進行狀态的劃分,這些狀态的某些特性是交叉的。比如,在按照按鍵和顯示劃分狀态的同時,又按照系統的工作程序做出另一種狀态劃分。這兩種狀态劃分同時存在,互相交叉,進而構成了二維的狀态結構空間。

舉一個這方面的例子,如:空調遙控器,如圖5所示。

狀态機思路在程式設計中的應用

圖5 多元狀态機結構

同樣,我們也可以建構三維、四維甚至更多元的狀态結構。每一維的狀态都需要用一個狀态變量(寄存器)來表示。

無論多級狀态結構和多元狀态結構看上去多麼迷人,匠人的忠告是:我們依然要盡可能地簡化狀态結構,能用單級、單維的結構,就不要給自己找事,去玩那噩夢般的複雜結構。

簡單的才是最有效的。

結束語

對狀态機的了解需要一個由淺入深的過程。這個過程應該是與實踐應用和具體案例思考相結合的。當一種良好的思路成為設計的習慣,它就能給設計者帶來回報。願這篇手記裡介紹的基于狀态機的程式設計思路能給新手們帶來一些啟迪,幫助大家找到“程式設計”的感覺。

狀态機思路在單片機程式設計中的應用

狀态機的概念

狀态機是軟體程式設計中的一個重要概念。比這個概念更重要的是對它的靈活應用。在一個思路清晰而且高效的程式中,必然有狀态機的身影浮現。

比如說一個按鍵指令解析程式,就可以被看做狀态機:本來在A狀态下,觸發一個按鍵後切換到了B狀态;再觸發另一個鍵後切換到C狀态,或者傳回到A狀态。這就是最簡單的按鍵狀态機例子。實際的按鍵解析程式會比這更複雜些,但這不影響我們對狀态機的認識。

進一步看,擊鍵動作本身也可以看做一個狀态機。一個細小的擊鍵動作包含了:釋放、抖動、閉合、抖動和重新釋放等狀态。

同樣,一個串行通信的時序(不管它是遵循何種協定,标準序列槽也好、I2C也好;也不管它是有線的、還是紅外的、無線的)也都可以看做由一系列有限的狀态構成。

顯示掃描程式也是狀态機;通信指令解析程式也是狀态機;甚至連繼電器的吸合/釋放控制、發光管(LED)的亮/滅控制又何嘗不是個狀态機。

當我們打開思路,把狀态機作為一種思想導入到程式中去時,就會找到解決問題的一條有效的捷徑。有時候用狀态機的思維去思考程式該幹什麼,比用控制流程的思維去思考,可能會更有效。這樣一來狀态機便有了更實際的功用。

程式其實就是狀态機。

也許你還不了解上面這句話。請想想看,計算機的大廈不就是建立在“0”和“1”兩個基本狀态的地基之上麼?

狀态機的要素

狀态機可歸納為4個要素,即現态、條件、動作、次态。這樣的歸納,主要是出于對狀态機的内在因果關系的考慮。“現态”和“條件”是因,“動作”和“次态”是果。詳解如下:

①現态:是指目前所處的狀态。

②條件:又稱為“事件”。當一個條件被滿足,将會觸發一個動作,或者執行一次狀态的遷移。

③動作:條件滿足後執行的動作。動作執行完畢後,可以遷移到新的狀态,也可以仍舊保持原狀态。動作不是必需的,當條件滿足後,也可以不執行任何動作,直接遷移到新狀态。

④次态:條件滿足後要遷往的新狀态。“次态”是相對于“現态”而言的,“次态”一旦被激活,就轉變成新的“現态”了。

如果我們進一步歸納,把“現态”和“次态”統一起來,而把“動作”忽略(降格處理),則隻剩下兩個最關鍵的要素,即:狀态、遷移條件。

狀态機的表示方法有許多種,我們可以用文字、圖形或表格的形式來表示一個狀态機。

純粹用文字描述是很低效的,是以就不介紹了。接下來先介紹圖形的方式。

狀态遷移圖(STD)

狀态遷移圖(STD),是一種描述系統的狀态、以及互相轉化關系的圖形方式。狀态遷移圖的畫法有許多種,不過一般都大同小異。我們結合一個例子來說明一下它的畫法,如圖1所示。

狀态機思路在程式設計中的應用

圖1 狀态遷移圖

①狀态框:用方框表示狀态,包括所謂的“現态”和“次态”。

②條件及遷移箭頭:用箭頭表示狀态遷移的方向,并在該箭頭上标注觸發條件。

③節點圓圈:當多個箭頭指向一個狀态時,可以用節點符号(小圓圈)連接配接彙總。

④動作框:用橢圓框表示。

⑤附加條件判斷框:用六角菱形框表示。

狀态遷移圖和我們常見的流程圖相比有着本質的差別,具體展現為:在流程圖中,箭頭代表了程式PC指針的跳轉;而在狀态遷移圖中,箭頭代表的是狀态的改變。

我們會發現,這種狀态遷移圖比普通程式流程圖更簡練、直覺、易懂。這正是我們需要達到的目的。

狀态遷移表

除了狀态遷移圖,我們還可以用表格的形式來表示狀态之間的關系。這種表一般稱為狀态遷移表。

表1就是前面介紹的那張狀态遷移圖的另一種描述形式。

表1 狀态遷移表

狀态機思路在程式設計中的應用

①采用表格方式來描述狀态機,優點是可容納更多的文字資訊。例如,我們不但可以在狀态遷移表中描述狀态的遷移關系,還可以把每個狀态的特征描述也包含在内。

②如果表格内容較多,過于臃腫不利于閱讀,我們也可以将狀态遷移表進行拆分。經過拆分後的表格根據其具體内容,表格名稱也有所變化。

③比如,我們可以把狀态特征和遷移關系分開清單。被單獨拆分出來的描述狀态特征的表格,也可以稱為“狀态真值表”。這其中比較常見的就是把每個狀态的顯示内容單獨清單。這種描述每個狀态顯示内容的表稱之為“顯示真值表”。同樣,我們把單獨表述基于按鍵的狀态遷移表稱為“按鍵功能真值表”。另外,如果每一個狀态包含的資訊量過多,我們也可以把每個狀态單獨清單。

④由此可見,狀态遷移表作為狀态遷移圖的有益補充,它的表現形式是靈活的。

⑤狀态遷移表優點是資訊涵蓋面大,缺點是視覺上不夠直覺,是以它并不能取代狀态遷移圖。比較理想的是将圖形和表格結合應用。用圖形展現宏觀,用表格說明細節。二者互為參照,相得益彰。

用狀态機思路實作一個時鐘程式

接下來,我将就狀态機的應用,結合流程圖、狀态遷移圖和狀态遷移,舉一個實際例子。下面這張圖是一個時鐘程式的狀态遷移圖,如圖2所示。

狀态機思路在程式設計中的應用

圖2 時鐘程式狀态遷移圖

把這張圖稍做歸納,就可以得到它的另一種表現形式——狀态遷移表,如表2所示。

表2 時鐘程式狀态遷移表

狀态機思路在程式設計中的應用

狀态機應用的注意事項

基于狀态機的程式排程機制,其應用的難點并不在于對狀态機概念的了解,而在于對系統工作狀态的合理劃分。

初學者往往會把某個“程式動作”當作是一種“狀态”來處理。我稱之為“僞态”。那麼如何區分“動作”和“狀态”。本匠人的心得是看二者的本質:“動作”是不穩定的,即使沒有條件的觸發,“動作”一旦執行完畢就結束了;而“狀态”是相對穩定的,如果沒有外部條件的觸發,一個狀态會一直持續下去。

初學者的另一種比較緻命的錯誤,就是在狀态劃分時漏掉一些狀态。我稱之為“漏态”。

“僞态”和“漏态”這兩種錯誤的存在,将會導緻程式結構的渙散。是以要特别小心避免。

更複雜的狀态機

前面介紹的是一種簡單的狀态結構。它隻有一級,并且隻有一維,如圖3所示。

狀态機思路在程式設計中的應用

圖3  線性狀态機結構

如果有必要,我們可以建立更複雜的狀态機模型。

1 多級狀态結構

狀态機可以是多級的。在分層的多級狀态機系統裡面,一個“父狀态”下可以劃分多個“子狀态”,這些子狀态共同擁有上級父狀态的某些共性,同時又各自擁有自己的一些個性。

在某些狀态下,還可以進一步劃分子狀态。比如,我們可以把前面的時鐘例子修改如下:

把所有和時鐘功能有關的狀态,合并成1個一級狀态。在這個狀态下,又可以劃分出3個二級子狀态,分别為顯示時間、設定小時、設定分鐘;

同樣,我們也可以把所有和鬧鐘功能有關的狀态,合并成1個一級狀态。在這個狀态下,再劃分出4個二級子狀态,分别為顯示鬧鐘、設定“時”、設定“分”、設定鳴叫時間。

我們需要用另一個狀态變量(寄存器)來表示這些子狀态。

子狀态下面當然還可以有更低一級的孫狀态(子子孫孫無窮盡也),進而将整個狀态體系變成了樹狀多級狀态結構,如圖4所示。

狀态機思路在程式設計中的應用

圖4 樹狀多級狀态結構

2 多元狀态結構

狀态結構也可以是多元的。從不同的角度對系統進行狀态的劃分,這些狀态的某些特性是交叉的。比如,在按照按鍵和顯示劃分狀态的同時,又按照系統的工作程序做出另一種狀态劃分。這兩種狀态劃分同時存在,互相交叉,進而構成了二維的狀态結構空間。

舉一個這方面的例子,如:空調遙控器,如圖5所示。

狀态機思路在程式設計中的應用

圖5 多元狀态機結構

同樣,我們也可以建構三維、四維甚至更多元的狀态結構。每一維的狀态都需要用一個狀态變量(寄存器)來表示。

繼續閱讀