|
|
- - 前言 -
- 時(shí)至今日,依舊看到很多小伙伴們放著單片機(jī)里的定時(shí)器不用,動(dòng)輒delay1s(); delay500ms();雖然簡(jiǎn)單粗暴,但是其實(shí)是很不妥當(dāng)?shù)摹?/font>
- 還有很多需要按鍵的程序,動(dòng)不動(dòng)就“while(k1==0);”的等按鍵松開(kāi),這樣的代碼只是“為了滿足某個(gè)功能“而設(shè)計(jì)的代碼,而非”為了保證產(chǎn)品的質(zhì)量“而設(shè)計(jì)的代碼。
- 我們應(yīng)該時(shí)刻考慮“如果這傻逼用戶不按我的標(biāo)準(zhǔn)操作來(lái)搞我的產(chǎn)品,我該怎么寫(xiě)程序?”,而不是”只要客戶乖乖聽(tīng)我的,這樣做是沒(méi)問(wèn)題的,就行了;出了問(wèn)題都是他亂搞,跟我有個(gè)毛關(guān)系”,后面的思維是典型的只顧自己爽的思維,我們應(yīng)當(dāng)想方設(shè)法的提高產(chǎn)品的可靠度。
- OK,扯遠(yuǎn)了,現(xiàn)在只是想提出一些設(shè)計(jì)的思路。
- part1 . 系統(tǒng)的時(shí)基 & 依托于時(shí)基去設(shè)計(jì)函數(shù)
- 系統(tǒng)的時(shí)鐘基線 -- 關(guān)鍵詞,是“時(shí)鐘基線”。
- 我們需要設(shè)置一個(gè)定時(shí)器,這個(gè)定時(shí)器會(huì)告訴單片機(jī):“好的,現(xiàn)在已經(jīng)過(guò)了1ms了(或者100ms?這都沒(méi)關(guān)系)”單片機(jī)一拍腦袋:“啊,已經(jīng)到了1ms了么,那我就把某一件事情做一下吧”。
- 大致像這樣:
-
51hei.png (16.78 KB, 下載次數(shù): 189)
下載附件
主函數(shù)與定時(shí)器
2024-4-1 20:31 上傳
- 這個(gè)程序的意思就是,讓單片機(jī)每隔1ms就do_sth()一次(定時(shí)器的初始化函數(shù)省略了沒(méi)畫(huà));
- 這樣寫(xiě)還可以讓中斷函數(shù)非常的簡(jiǎn)潔,幾乎一進(jìn)去就出來(lái),基本不會(huì)影響到被中斷打斷的函數(shù)的工作;
- 以傳遞關(guān)鍵參數(shù)的形式,大循環(huán)里只要查一查這個(gè)flag1ms值,知道什么時(shí)間要做什么事情就對(duì)了。
- 按鍵函數(shù)舉例 -- 上圖中這個(gè)do_sth()可以是任意函數(shù),以按鍵函數(shù)keyPress()為例,可以這樣寫(xiě):
- void keyPress(){
- static unsigned int key_press_time = 0; // ……請(qǐng)記得標(biāo)為靜態(tài)變量
- if(K1==0){
- if(++key_press_time <=0 ) --key_press_time;//計(jì)量按鍵時(shí)間,并避免數(shù)據(jù)溢出
- if(key_press_time==3000){
- //在此寫(xiě)下按鍵長(zhǎng)按3s時(shí)要做的事情;
- }
- }else{
- if(20<=key_press_time && key_press_time < 3000){
- //大于20ms小于3s,視為短按,在此寫(xiě)下寫(xiě)短按的處理代碼
- }
- key_press_time=0;
- }
- }
復(fù)制代碼
- 這里有很多可以研究的地方:
- ①“if(++key_press_time <=0 ) --key_press_time;”這一句,看起來(lái)用“++key_press_time”就能搞定,但是,誰(shuí)也不能保證某些用戶真的不會(huì)按按鍵超過(guò)65秒的啊;萬(wàn)一他真的按了65576ms;單片機(jī)還就真的以為用戶“短按”了一次呢(65576-65536=40ms,屬于短按范疇),這下那個(gè)短按程序段也會(huì)被執(zhí)行一次;現(xiàn)在這樣寫(xiě),哪怕你按100年也沒(méi)關(guān)系了,反正我的單片機(jī)就每隔1ms進(jìn)來(lái)看一次,K1這個(gè)按鈕你想按多久就按多久,掉在我的范圍內(nèi)我就處理你,超出我的范圍我就無(wú)視你。
- ②“if(key_press_time==3000){ ; }”這里的3000只是隨便設(shè)置的的一個(gè)時(shí)間(3s),如果你需要做按鍵長(zhǎng)短按功能,這里就是你的長(zhǎng)按程序所放的位置;你也可以不用3000;用2000,50000都沒(méi)事,別超過(guò)65534就行(提問(wèn):為什么這里又是65534呢?);
- ③“if(20<=key_press_time && key_press_time < 3000)”這里,前面的>=20是個(gè)消抖的設(shè)計(jì),客戶再?gòu)?qiáng)也是人類(lèi),再?gòu)?qiáng)的人類(lèi)也不可能1秒按一個(gè)按鍵超過(guò)20次;也就是不可能短于50ms的時(shí)間;這里用了20ms相當(dāng)于兼容 男上加男的快男 也來(lái)按按鍵了。后面<3000是不能和長(zhǎng)按的時(shí)間沖突,因?yàn)?s我們已經(jīng)人為的設(shè)置成長(zhǎng)按時(shí)間節(jié)點(diǎn)了。
- 好,現(xiàn)在知道相關(guān)的程序該怎么寫(xiě)了,我們回到剛剛討論的時(shí)鐘基線處。
- 多個(gè)時(shí)鐘基線的方案 -- 剛剛的例子中我們只展示了1ms的時(shí)基,對(duì)于單片機(jī)來(lái)說(shuō),1ms已經(jīng)很慢了,但對(duì)于人和某些設(shè)備來(lái)說(shuō)來(lái)說(shuō)還是快的不行,比如一些本就需要10ms間隔的通信設(shè)置,需要隔100ms才能刷一次的顯示,需要50ms才能重新采的某些傳感器等等,這些東西放進(jìn)1ms的do_sth()里面肯定是不行的(其實(shí)也行,但不方便管理來(lái)著)——怎么辦呢?
- ——好辦,放進(jìn)10ms 或50ms 或100ms 的do_sth() 里就OK了。
- ——但是我們只有1個(gè)定時(shí)器啊,怎么弄幾個(gè)時(shí)基呢?
- ——1個(gè)定時(shí)器就夠了,其他的時(shí)基都基于那個(gè)1ms的時(shí)基來(lái)就好。我們已經(jīng)有了flag1ms,當(dāng)然可以弄幾個(gè)flag10ms乃至flag1000ms,只要你需要,就能設(shè)置。
- 如下圖,我們通過(guò)參數(shù)控制參數(shù)的形式,將flag1ms擴(kuò)展為多個(gè)時(shí)基,使得程序漸漸的趨于模塊化,精準(zhǔn)化。
-
51hei1.png (33.52 KB, 下載次數(shù): 214)
下載附件
增大時(shí)基的設(shè)置
2024-4-1 20:31 上傳
- 這里有個(gè)要注意的地方,就是函數(shù)從宏觀上來(lái)看,確實(shí)是“每隔1ms就do_sth();每隔10ms就do_sth1();每隔100ms就do_sth2()"。
- 但是,從微觀上看,單片機(jī)是沒(méi)法在同一時(shí)刻做2件事情的!所以,每到10ms的時(shí)候,單片機(jī)會(huì)”先把1ms的事情做完再做10ms的事“;每到100ms的時(shí)候,單片機(jī)會(huì)”先把1ms的事情做完再做10ms的事,再做100ms的事“。
- 現(xiàn)在,我們的系統(tǒng)已經(jīng)趨于成型了,不過(guò)要注意一點(diǎn),這些做事情的子函數(shù)里萬(wàn)萬(wàn)不得有delay函數(shù)了(放幾個(gè)nop倒是無(wú)傷大雅),好不容易劃分好了時(shí)鐘基線,你在1ms的時(shí)基里塞上幾個(gè)delay2ms是要鬧怎樣。
- 還有一個(gè)問(wèn)題,那就是子函數(shù)不要拖泥帶水,寫(xiě)得越簡(jiǎn)潔越好,趕緊把事情做完,把控制權(quán)還給單片機(jī),由它來(lái)決定接下來(lái)要做什么。
- 函數(shù)應(yīng)該放在哪個(gè)時(shí)基里? -- 這也是關(guān)鍵的地方,這要求我們要對(duì)自己的程序要有清楚的把握。以及一定的產(chǎn)品思維。
- 總的原則還是沒(méi)變:所有的函數(shù)都要寫(xiě)得簡(jiǎn)潔干凈,不要有任何模塊的delay()加起來(lái)超過(guò)200us!
- 一般來(lái)說(shuō),按鍵按一次不會(huì)超過(guò)50ms,放到flag10ms或者flag20ms的時(shí)間段里是沒(méi)什么關(guān)系的(注意,這時(shí)候函數(shù)里的3000表示的就不是3s了,而是30s或者60s!這是遵循乘法原則的!);
- LCD顯示之類(lèi)的,控制某個(gè)LED之類(lèi)的,100ms一次就行了,人眼視覺(jué)暫留之類(lèi)的看不出問(wèn)題的。
- 一般:檢測(cè),通信這類(lèi)的子程序都能放到flag10ms或者flag20ms里。輸出,顯示這類(lèi)的放flag100ms就OK了。
- flag1ms里面應(yīng)該放什么呢?我的意見(jiàn)是盡量什么也別放,空著都OK,因?yàn)槠鋵?shí)沒(méi)什么東西需要刷新得這么快的。
- 如果有特別需要關(guān)照的部分,比如說(shuō)步進(jìn)電機(jī)的驅(qū)動(dòng)啥的,請(qǐng)放到另一個(gè)定時(shí)器中斷里(單片機(jī)有倆定時(shí)器的,不用白不用),按你需要的來(lái)設(shè)置。
- 定時(shí)器的中斷觸發(fā)時(shí)間建議不要低于0.5ms,不然進(jìn)中斷就太頻繁了,誰(shuí)也不會(huì)希望自己正看著小電影的時(shí)候,爸媽過(guò)來(lái)拍你的房門(mén)吧?
- —— 2019年6月10日更新 ——
- Part2.將代碼模塊化,降低耦合度
- - 降低代碼間的耦合度 -
- 通俗點(diǎn)說(shuō),耦合度就是表示兩個(gè)東西“你中有我,我中有你”的程度,代碼間的耦合度越高,你修改起來(lái)就越費(fèi)勁,有時(shí)候看到代碼黏糊糊的像一大坨蜜糖,難免會(huì)讓人不知從何處下口。
- 還是用個(gè)例子來(lái)說(shuō)明一下吧——《電風(fēng)扇系統(tǒng)設(shè)計(jì)1》
- 假設(shè)我們要寫(xiě)個(gè)電風(fēng)扇的控制程序 —— 按鍵方面:風(fēng)扇有開(kāi)關(guān)鍵,強(qiáng)風(fēng)弱風(fēng)切換,擺頭控制3個(gè)按鍵(注意,這里的按鍵是那種大開(kāi)大合的開(kāi)關(guān)按鍵,不是那種輕觸按鍵); 顯示方面:每個(gè)按鍵旁邊各有1個(gè)小led燈,共3個(gè)燈; 輸出方面:有個(gè)控制風(fēng)扇擺頭的負(fù)載,有個(gè)控制風(fēng)扇轉(zhuǎn)不轉(zhuǎn)的負(fù)載,有個(gè)控制風(fēng)扇轉(zhuǎn)得快或者慢的負(fù)載。
- 或許有人三下兩下就畫(huà)了個(gè)這樣的流程圖(一般風(fēng)扇沒(méi)開(kāi)的時(shí)候是不能擺頭的,這里就先不管了):
-
51hei2.jpg (41.99 KB, 下載次數(shù): 193)
下載附件
2024-4-1 20:31 上傳
- 然后就隨手寫(xiě)出了這樣的代碼:
-
51hei3.jpg (19.83 KB, 下載次數(shù): 193)
下載附件
2024-4-1 20:33 上傳
- 老實(shí)說(shuō),這沒(méi)啥問(wèn)題,因?yàn)槲覀兊念}目要求是那么的簡(jiǎn)單,簡(jiǎn)單的要求那就簡(jiǎn)單的實(shí)現(xiàn)吧。雖然這里的按鍵、顯示、負(fù)載的程序代碼像蜜糖一樣黏糊糊的,但是代碼也才十幾行,誰(shuí)會(huì)在意呢?也大致畫(huà)一下如上代碼的結(jié)構(gòu)吧:
-
51hei4.jpg (8.39 KB, 下載次數(shù): 177)
下載附件
2024-4-1 20:33 上傳
- 但是,別忘了2點(diǎn)!①是客戶的需求隨時(shí)都會(huì)改動(dòng);②是這么簡(jiǎn)單的項(xiàng)目還用不到你做。所以,我們稍微,加點(diǎn)難度上來(lái)看看?
- 《電風(fēng)扇系統(tǒng)設(shè)計(jì)2》
- 在《風(fēng)扇1》的基礎(chǔ)上,①風(fēng)扇沒(méi)開(kāi)時(shí)不能擺頭;②風(fēng)扇沒(méi)開(kāi)時(shí),為了與沒(méi)電作區(qū)分,要讓3個(gè)LED燈同時(shí)以1s為周期閃爍;③開(kāi)風(fēng)扇后若是5小時(shí)內(nèi)按鍵都沒(méi)有變化過(guò),則自動(dòng)關(guān)掉風(fēng)扇(避免久開(kāi),當(dāng)然此后LED也要閃爍),若想重開(kāi)風(fēng)扇必須把“風(fēng)扇開(kāi)關(guān)按鍵”先關(guān)掉再打開(kāi)才行。
- 說(shuō)句實(shí)話,先前的那份代碼也到此為止了,如果有人還想在原來(lái)的代碼上改出滿足現(xiàn)在的需求的代碼,我只能祝他好運(yùn)。雖然也不是不能改,但是若是想用你那delay500ms來(lái)滿足我的閃爍要求;或者硬是嵌入一些繞來(lái)繞去的東西在里面,那肯定沒(méi)有從頭再來(lái)要省事。
- 那么——現(xiàn)在該怎么做呢?
- 首先,把粘連的模塊分開(kāi),按鍵歸按鍵,顯示歸顯示,輸出歸輸出。就像這樣子:
51hei5.jpg (9.87 KB, 下載次數(shù): 211)
下載附件
2024-4-1 20:35 上傳
- 這樣的話,大家各做各的事情,互不干擾,需要共享的信息則通過(guò)關(guān)鍵參數(shù)來(lái)傳遞。這里我們?cè)O(shè)置了3個(gè)全局變量:key_on_flag, key_strong_flag, key_sheak_flag; 在keyPress()函數(shù)里可以修改這3個(gè)變量的值,然后在display()和output()函數(shù)里查詢這3個(gè)變量的值來(lái)控制顯示和輸出。這時(shí)候的代碼結(jié)構(gòu)就是這種樣子的了——各個(gè)模塊分開(kāi),用關(guān)鍵參數(shù)傳遞信息。
51hei6.jpg (12.31 KB, 下載次數(shù): 192)
下載附件
2024-4-1 20:35 上傳
- 所謂的“模塊化,降低耦合度”大致就是這種感覺(jué),現(xiàn)在你需要在哪里改動(dòng)就單獨(dú)改動(dòng)哪個(gè)部分的,不至于瞻前顧后、束手束腳了。
- 還沒(méi)完呢,第二步,把我們的“時(shí)基”弄進(jìn)來(lái)(不然上一堂課就白講了),如圖所示:
51hei7.jpg (20.54 KB, 下載次數(shù): 194)
下載附件
2024-4-1 20:35 上傳
- 如此一來(lái),關(guān)于那些指定時(shí)間的、指定頻率的要求,我們也能輕松的應(yīng)對(duì)了。比如這時(shí)候的display()函數(shù),可以這樣寫(xiě)(這個(gè)其實(shí)還有缺陷,不過(guò)先湊合一下)——
-
51hei8.jpg (12.07 KB, 下載次數(shù): 198)
下載附件
2024-4-1 20:37 上傳
- 相信通過(guò)這樣一個(gè)例子,大家應(yīng)該能看到模塊化對(duì)你的軟件系統(tǒng)有多大的改善了。模塊間的耦合度降低之后,自有一種九陽(yáng)神功中的“他強(qiáng)由他強(qiáng),清風(fēng)拂山崗。它弱由他弱,明月照大江。”的感覺(jué)。你那邊愛(ài)怎么變就怎么變,我這邊有必要就改動(dòng),沒(méi)必要就不改動(dòng),將彼此間的影響減到最低。
- 時(shí)間有限,模塊化的解說(shuō)先到這里,后續(xù)有時(shí)間會(huì)將狀態(tài)機(jī)等知識(shí)盡可能的講解給大家。同時(shí)還得把這個(gè)需求變更后的《電風(fēng)扇系統(tǒng)設(shè)計(jì)2》的軟件給寫(xiě)完。
- ——2019年6月11日 ,今天沒(méi)空更貼,暫時(shí)先優(yōu)化一下排版——
- ——2019年6月14日,修復(fù)了教程中的keyPress()和display()函數(shù)中的兩個(gè)靜態(tài)變量忘了加static關(guān)鍵字的bug——
- ——2019年6月16日,更新,狀態(tài)機(jī)上篇 ——
- part3.使用狀態(tài)機(jī),幫助你管理系統(tǒng)狀態(tài)(上篇)
- (本章較為復(fù)雜,需要讀者有較好的數(shù)電和C語(yǔ)言功底,方能融匯貫通)
- - 狀態(tài)機(jī)入門(mén)(有基礎(chǔ)的可以跳過(guò)本小節(jié)) -
- 直白的說(shuō),狀態(tài)機(jī)就是若干個(gè)“當(dāng)前狀態(tài) + 觸發(fā)條件 = 新?tīng)顟B(tài)( + 附加動(dòng)作)”的公式。
- 狀態(tài)機(jī)可以畫(huà)成圖的形式(《狀態(tài)遷移圖》),也可以做成表格的形式(《狀態(tài)分析表》),如果大家還有數(shù)字電子技術(shù)的功底的話,應(yīng)該對(duì)下圖還不至于太陌生。
-
51hei9.jpg (40.4 KB, 下載次數(shù): 204)
下載附件
2024-4-1 20:37 上傳
- ↑此為某狀態(tài)遷移圖舉例(與下表同義)
51hei10.jpg (15.74 KB, 下載次數(shù): 199)
下載附件
2024-4-1 20:37 上傳
- ↑此為某狀態(tài)分析表舉例(與上圖同義)
- 如果上述圖表使用公式來(lái)表達(dá)的話那就是4個(gè)公式(取決于圖的箭頭數(shù),或者表的跳轉(zhuǎn)數(shù)),用公式的話,公式的數(shù)量不但多,看起來(lái)也很麻煩,所以我們一般用《圖》或者《表》來(lái)描述你的狀態(tài)機(jī)。
- 注意,一般的狀態(tài)機(jī)是包含“動(dòng)作”的,這里為了教學(xué)方便,略過(guò)了“動(dòng)作”(后面會(huì)加回來(lái)的)。
- 狀態(tài)機(jī)的使用有助于我們更直接,更便捷的管理我們的系統(tǒng)工作。尤其是在系統(tǒng)比較復(fù)雜的時(shí)候。
- - 用程序來(lái)表現(xiàn)狀態(tài)機(jī) -
- 狀態(tài)用枚舉量為佳,因?yàn)槲覀儜?yīng)當(dāng)保證系統(tǒng)的狀態(tài)處于可控范圍內(nèi),枚舉量是我們工程師自定義的一個(gè)量,可以使系統(tǒng)處處受我們控制。
- 觸發(fā)條件用bit變量即可,觸發(fā)了就是1,沒(méi)觸發(fā)就是0;當(dāng)然char之類(lèi)也可以,但沒(méi)必要。
- 有基礎(chǔ)的同學(xué)可以使用"位結(jié)構(gòu)體"來(lái)優(yōu)化這些觸發(fā)條件的內(nèi)存,這里不提,請(qǐng)自行百度。
- ok,那現(xiàn)在可以先定義我們的狀態(tài)機(jī)變量了(以上述圖表為例)。
- typedef enum{
- STATE1,
- STATE2,
- STATE3
- }ENUM_STATE; //定義ENUM_STATE枚舉類(lèi)型,表狀態(tài)
- ENUM_STATE system_state = STATE1; //定義上述枚舉類(lèi)型的枚舉變量system_state, 初始化為STATE1
- bit test_flag_a, test_flag_b, test_flag_c; //定義3個(gè)觸發(fā)條件的bit變量。
復(fù)制代碼
- 那么,狀態(tài)機(jī)的程序要怎么寫(xiě)呢?其實(shí)我們觀察《狀態(tài)分析表》的時(shí)候,有人會(huì)喜歡根據(jù)當(dāng)前狀態(tài),分析觸發(fā)條件,來(lái)決定下一刻的狀態(tài);有人會(huì)喜歡從觸發(fā)條件開(kāi)始,看看現(xiàn)在的狀態(tài)是否受這種觸發(fā)條件影響,而進(jìn)入新的狀態(tài)。
- ——好吧,有兩種觀察方法,就有2種寫(xiě)法,讀者可以自由選擇自己喜歡的寫(xiě)法。
- 寫(xiě)法1(根據(jù)當(dāng)前狀態(tài),看觸發(fā)條件是否有效):
- void systemStateCtrl(){
- switch(system_state){
- case STATE1:
- if(test_flag_b) system_state = STATE2;
- break;
- case STATE2:
- if(test_flag_a) system_state = STATE1;
- else if(test_flag_c) system_state = STATE3;//條件a比條件c優(yōu)先
- break;
- case STATE3:
- if(test_flag_a) system_state = STATE1;
- break;
- default:
- break;
- }
- }
復(fù)制代碼
- 寫(xiě)法2(根據(jù)觸發(fā)條件,看當(dāng)前狀態(tài)是否需要改變):
- void systemStateCtrl(){
- if(test_flag_a){
- if(system_state==STATE2 || system_state==STATE3)
- system_state = STATE1;
- }
- else if(test_flag_b){
- if(system_state==STATE1)
- system_state = STATE2;
- }
- else if(test_flag_c){
- if(system_state==STATE2)
- system_state = STATE3;
- }
- else{
- ;
- }
- }
復(fù)制代碼
- 這2種寫(xiě)法各有特點(diǎn),并沒(méi)有優(yōu)劣之分,各位可以自取所需。非要比較的話,我個(gè)人認(rèn)為:第一種寫(xiě)法"好讀一些",第二種寫(xiě)法"好寫(xiě)一些"。
- - 根據(jù)狀態(tài)的值,控制系統(tǒng)的工作流 -
- “系統(tǒng)的工作”很好理解,就是系統(tǒng)現(xiàn)在在干什么的意思,那么“系統(tǒng)的工作流”是什么意思?
- ——系統(tǒng)的工作流,表示系統(tǒng)在某段時(shí)間內(nèi)的工作流程。
- ——為什么要普及“工作流”這個(gè)東西呢?
- ——因?yàn)椋芏嗲闆r下,某個(gè)狀態(tài)的工作不是寫(xiě)死的,而是可變的。比如:冰箱在制冷時(shí),制冷器并不是一直開(kāi)著的,而是一段時(shí)間開(kāi),一段時(shí)間關(guān);洗衣機(jī)在洗衣服時(shí),滾筒不是一直開(kāi)著的,而是先等注水完成之后,正著轉(zhuǎn)一段時(shí)間,然后反著轉(zhuǎn)一段時(shí)間,然后又正著轉(zhuǎn)……就是有一系列的工作步驟,這些工作的步驟其實(shí)就是我們所說(shuō)的工作流。
- 如果你非要把洗衣機(jī)在洗衣服時(shí),滾筒正著轉(zhuǎn)和反著轉(zhuǎn)分成2種新的狀態(tài)的話……一路走好。
- 總之,為了給狀態(tài)下的動(dòng)作有一定的預(yù)留空間(因?yàn)?/font>天知道,需求會(huì)不會(huì)發(fā)生變化),我們需要給每個(gè)狀態(tài)都做一套關(guān)于此狀態(tài)下的動(dòng)作的設(shè)計(jì)。
- 這個(gè)程序?qū)懫饋?lái)也很簡(jiǎn)單,嵌入位置上,直接塞進(jìn)狀態(tài)機(jī)的屁股后面就行。如下:
- /* 狀態(tài)機(jī)程序 */
- void systemStateCtrl(){
- //你的狀態(tài)機(jī)程序
- systemStateWork();//把狀態(tài)工作程序放這里
- }
- /* 狀態(tài)工作程序 */
- void systemStateWork(){ //設(shè)計(jì)你各個(gè)狀態(tài)下的工作
- switch(system_state){
- case STATE1:
- do_sth1();break;
- case STATE2:
- do_sth2();break;
- case STATE3:
- do_sth3();break;
- default:
- break;
- }
- }
復(fù)制代碼
- - 例程:《電風(fēng)扇系統(tǒng)設(shè)計(jì)2》的狀態(tài)機(jī)初版 -
- 狀態(tài)機(jī)真的是一個(gè)很龐大的知識(shí)點(diǎn)啊,好不容易把理論說(shuō)完了,接下來(lái)諸位看看我的實(shí)例吧。
- 這個(gè)系統(tǒng)設(shè)計(jì)的需求我就不再重復(fù)了,各位往回看一看就能找到,關(guān)鍵在于,需求③我們還未處理【③開(kāi)風(fēng)扇后若是5小時(shí)內(nèi)按鍵都沒(méi)有變化過(guò),則自動(dòng)關(guān)掉風(fēng)扇(避免久開(kāi),當(dāng)然此后LED也要閃爍),若想重開(kāi)風(fēng)扇必須把“風(fēng)扇開(kāi)關(guān)按鍵”先關(guān)掉再打開(kāi)才行。】。
- 那么開(kāi)始吧,我們?cè)谝婚_(kāi)始,會(huì)將系統(tǒng)的狀態(tài)分成“風(fēng)扇開(kāi)”和“風(fēng)扇關(guān)”兩種,直接由風(fēng)扇的開(kāi)機(jī)鍵控制切換。但是,多了新的需求之后,開(kāi)機(jī)鍵就不好使了——因?yàn)橛蟹N“風(fēng)扇關(guān)”的狀態(tài),這時(shí)候的開(kāi)機(jī)鍵也是按下的!經(jīng)過(guò)一番思索,為了和真正的“風(fēng)扇關(guān)”作區(qū)別,我們可以再創(chuàng)造一種新的狀態(tài)——“停機(jī)”!也就是開(kāi)機(jī)太久了,需要停機(jī)休息。停機(jī)時(shí)候的搖頭鍵,強(qiáng)弱風(fēng)鍵都無(wú)效,只有開(kāi)機(jī)鍵松開(kāi),才能讓你退出“停機(jī)”,進(jìn)入"關(guān)機(jī)"。
- 根據(jù)我們的分析,可以畫(huà)出系統(tǒng)的狀態(tài)遷移圖:
-
51hei11.jpg (40.93 KB, 下載次數(shù): 192)
下載附件
2024-4-1 20:39 上傳
- 同樣的,狀態(tài)分析表。
-
51hei12.jpg (17.13 KB, 下載次數(shù): 195)
下載附件
2024-4-1 20:39 上傳
- 相關(guān)程序如下
typedef enum{
STATE_OFF,
STATE_ON,
STATE_STOP
}ENUM_STATE; //定義ENUM_STATE枚舉類(lèi)型
ENUM_STATE system_state = STATE_OFF; //定義枚舉變量system_state, 初始化為STATE_OFF
bit key_on_flag, key_off_flag, work_too_long_flag; //定義3個(gè)觸發(fā)條件的bit變量(其實(shí)用2個(gè)就行)
void systemStateCtrl(){
if(key_on_flag){
if(system_state==STATE_ON || system_state==STATE_STOP)
system_state = STATE_OFF;
}
else if(key_off_flag){
if(system_state==STATE_OFF)
system_state = STATE_ON;
}
else if(work_too_long_flag){
if(system_state==STATE_ON)
system_state = STATE_STOP;
}
else{
;
}
systemStateWork();//把狀態(tài)工作程序放這里
}
void systemStateWork(){ //設(shè)計(jì)你各個(gè)狀態(tài)下的工作
switch( system_state ){
case STATE_OFF:
do_sth1(); //關(guān)機(jī)時(shí)的工作
break;
case STATE_ON:
do_sth2(); //開(kāi)機(jī)時(shí)的工作
break;
case STATE_STOP:
do_sth3(); //超時(shí)停機(jī)時(shí)的工作
break;
default:
break;
}
}
- 現(xiàn)在,我們從《電風(fēng)扇系統(tǒng)設(shè)計(jì)2》的需求出發(fā),先解析系統(tǒng)的狀態(tài);再作圖表(其實(shí)圖和表做一個(gè)就行,這里為了解讀需要,都做了出來(lái)),將狀態(tài)機(jī)的大體結(jié)構(gòu)都描述出來(lái);這時(shí)候?qū)懜鱾(gè)狀態(tài)下的工作程序,就是水到渠成的事情了。
- - 狀態(tài)機(jī)小結(jié) -
- 狀態(tài)機(jī)確實(shí)是個(gè)比較長(zhǎng)的教程,所以我將之分成了2部分來(lái)解說(shuō),第一部分只強(qiáng)調(diào)“狀態(tài)+條件=新?tīng)顟B(tài)”這一部分,目的是讓大家先對(duì)狀態(tài)機(jī)有個(gè)初步的認(rèn)知;第二部分再來(lái)描述“狀態(tài)+條件=新?tīng)顟B(tài)+動(dòng)作”的完整的狀態(tài)機(jī)。
- 眼尖的貼友應(yīng)該也發(fā)現(xiàn)了,這里為了解說(shuō)方便,還沒(méi)有將“強(qiáng)弱風(fēng)”,“是否擺頭”等動(dòng)作做進(jìn)狀態(tài)機(jī)里。不過(guò)他們其實(shí)只是一些“動(dòng)作”而已,在開(kāi)始分析系統(tǒng)的狀態(tài)時(shí),我們要分清楚系統(tǒng)的“主要?jiǎng)幼鳌焙汀按我獎(jiǎng)幼鳌保瑢?duì)于電風(fēng)扇而言,開(kāi)和關(guān)是主要的動(dòng)作,擺頭和強(qiáng)弱風(fēng)只是一些次要的附加動(dòng)作罷了。
- 關(guān)于如何劃分系統(tǒng)的主要狀態(tài)?
- ——這也是一個(gè)值得拿來(lái)長(zhǎng)篇大論的話題,但我這里不想深入探討,也無(wú)力深入探討。因?yàn)椤皩⒅付ㄎ锲贩诸?lèi)”,或者“請(qǐng)給蘋(píng)果,梨,水果,蔬菜,白菜,豆腐,白板筆這幾個(gè)詞進(jìn)行排序”這類(lèi)問(wèn)題本就有無(wú)數(shù)解法,是很看重解答者的主觀意識(shí)的。
- 熟練掌握狀態(tài)機(jī)的用法,可以使你能夠更準(zhǔn)確的把握系統(tǒng)的狀態(tài)。
- 本章未完,此外,由于我的個(gè)人原因,狀態(tài)機(jī)的下篇只能保證在今年7月15日前更新,還請(qǐng)各位諒解。屆時(shí)會(huì)將本教程劃上一個(gè)完美的句號(hào),同時(shí)各種源碼等也會(huì)打包上傳。
|
評(píng)分
-
查看全部評(píng)分
|