我是嵌入式系統(tǒng)的講師。我繼承了一段運(yùn)行良好的代碼,但是由于缺少設(shè)計(jì)圖,并且花了很多條件語(yǔ)句和標(biāo)志,使我花了一些時(shí)間來理解。
該代碼的目的是檢測(cè)連接到微控制器端口的幾個(gè)按鈕之一何時(shí)被激活并記錄事實(shí)。這些按鈕為高電平有效,這意味著按下按鈕時(shí)會(huì)在相應(yīng)的引腳上產(chǎn)生高電壓。開關(guān)彈跳的問題也已在固件中解決,因此同一引腳必須在預(yù)定的時(shí)間內(nèi)保持高電平,然后才能被接受為有效引腳。
該代碼每10毫秒調(diào)用一次,如果同一引腳為高電平,則計(jì)數(shù)器遞增。當(dāng)計(jì)數(shù)器達(dá)到預(yù)定義的值(在這種情況下為10)時(shí),按鈕按下被認(rèn)為是有效的。因此,在這種情況下,在認(rèn)為有效之前,引腳電壓必須穩(wěn)定在100mS的高電平。
為了更好地說明設(shè)計(jì),并作為學(xué)生的狀態(tài)機(jī)設(shè)計(jì)的另一個(gè)示例,我著手使用狀態(tài)機(jī)設(shè)計(jì)方法重新設(shè)計(jì)系統(tǒng)。
狀態(tài)機(jī)
狀態(tài)機(jī)圖如下圖1所示。所述Button_PORT是定義為任何端口的按鈕都連接到宏。這允許將按鈕輕松移動(dòng)到另一個(gè)端口。
#define Button_PORT PORTA
聲明了一個(gè)聯(lián)合,該聯(lián)合將允許將按鈕作為一個(gè)整體或單獨(dú)進(jìn)行訪問。
typedef union { unsigned char Full; 結(jié)構(gòu){ 無符號(hào)字符B0:1; 未簽名的字符B1:1; 未簽名的字符B2:1; 未簽名的字符B3:1; 未簽名的字符B4:1; 未簽名的字符B5:1; 未簽名的字符B6:1; 未簽名的字符B7:1; }; } Button_Type;
使用此類型定義了兩個(gè)變量Button_Press和Temp_Press。Button_Press在反跳后保留按鈕的最終值,而Temp_Press在反跳過程中保留按鈕的中間值。
在應(yīng)用程序代碼中,設(shè)置了一個(gè)計(jì)時(shí)器,每10毫秒產(chǎn)生一個(gè)中斷,然后評(píng)估狀態(tài)機(jī)。狀態(tài)機(jī)圖將此時(shí)間表示為TICK事件(TICK_E)的發(fā)生。
有以下四種狀態(tài):
等待中:等待端口上的任何按鈕被激活。
檢測(cè)到:按鈕已激活,因此進(jìn)入此狀態(tài),并使用Temp_Press記錄按鈕的端口值。每10毫秒,將再次檢查按鈕端口,并且-在其值仍然相同的情況下-計(jì)數(shù)器將遞增。用狀態(tài)機(jī)的話來說,該變量稱為“擴(kuò)展?fàn)顟B(tài)變量”。
WaitForRelease:如果計(jì)數(shù)器達(dá)到預(yù)定義的最小值'MIN_BUTTON_COUNT',則Temp_press現(xiàn)在被視為有效,并且進(jìn)入WaitForRelease狀態(tài)以等待按鈕釋放,直到變量Button_Press保留了最終的按鈕值。
更新:按鈕已釋放,因此最終值Button_Press已用去抖動(dòng)的臨時(shí)值'Temp_Press'更新。
圖1.按鈕反跳狀態(tài)機(jī)(來源:Thomas Gartlan)
該狀態(tài)機(jī)繪制在www.draw.io上,并根據(jù)Miro Samek的書《C / C ++中的實(shí)用UML狀態(tài)圖:嵌入式系統(tǒng)的事件驅(qū)動(dòng)編程》中的內(nèi)容使用表示法來表示狀態(tài)。
從教學(xué)的角度來看,此狀態(tài)機(jī)是狀態(tài),事件,警戒條件,Do操作,OnEntry操作和擴(kuò)展?fàn)顟B(tài)變量的一個(gè)很好的示例。
正如我們前面提到的,有四個(gè)狀態(tài)。唯一的事件是10mS TICK_E。從“等待”到“檢測(cè)到”的過渡中,TICK_E上有一個(gè)保護(hù)狀態(tài),[Button_PORT> 0],在這種情況下,這意味著某些按鈕已被激活。“已檢測(cè)”狀態(tài)下的“ OnEntry”操作會(huì)重置計(jì)數(shù)器,而“已檢測(cè)”狀態(tài)下的“執(zhí)行”操作會(huì)在計(jì)數(shù)器中遞增。計(jì)數(shù)器本身是擴(kuò)展?fàn)顟B(tài)變量。
與原始的以條件標(biāo)記為中心的代碼相反,此狀態(tài)機(jī)圖提供了非常清晰的設(shè)計(jì)視圖,因此為學(xué)生提供了一個(gè)很好的示例。
實(shí)施
該設(shè)計(jì)是使用MPLABX IDE和XC8編譯器實(shí)現(xiàn)的。目標(biāo)器件是Microchip的8位PIC18F4520微控制器。該設(shè)計(jì)以易于重復(fù)使用的方式實(shí)現(xiàn)。如前所述,端口是使用宏定義的,因此可以輕松地對(duì)其進(jìn)行更改。而且,該代碼打包在一個(gè)庫(kù)中并發(fā)布到GitHub,這使得它可以輕松地維護(hù)和在任何項(xiàng)目中使用。
庫(kù)頭文件包含按鈕結(jié)構(gòu)和端口信息。庫(kù)C文件包含狀態(tài)機(jī)功能。代碼中使用的名稱與狀態(tài)機(jī)圖相匹配,從而更易于理解和調(diào)試設(shè)計(jì)。函數(shù)指針并不是真正需要的,也沒有使用,因?yàn)樗鼈儠?huì)使學(xué)生在此階段對(duì)設(shè)計(jì)的理解更加復(fù)雜。狀態(tài)機(jī)功能的代碼如下所示。
typedef枚舉{Waiting,Detected,WaitForRelease,Update}狀態(tài); 無效Find_Button_Press(void) { 靜態(tài)狀態(tài)Button_State =正在等待; 靜態(tài)無符號(hào)字符Button_Count = 0; 靜態(tài)Button_Type Temp_Press; 開關(guān)(按鈕狀態(tài)){ 案例(等待): 如果(Button_PORT) { Button_State =已檢測(cè)到; Button_Count = 0; Temp_Press.Full =按鈕_端口; } 休息; 案例(檢測(cè)): 如果(Temp_Press.Full == Button_PORT) { ++ Button_Count; 如果(Button_Count> MIN_BUTTON_COUNT) { Button_State = WaitForRelease; } } 別的 { Button_State =等待中; } 休息; 案例(WaitForRelease): 如果(!Button_PORT) { Button_State =更新; } 休息; 案例(更新): { Button_Press = Temp_Press; Button_State =等待中; Button_Count = 0; Temp_Press.Full = 0; } 休息; 默認(rèn): { Button_State =等待中; Button_Count = 0; Temp_Press.Full = 0; Button_Press.Full = 0; } } }
應(yīng)用代碼和測(cè)試
開發(fā)了一個(gè)簡(jiǎn)單的應(yīng)用程序來說明和測(cè)試該設(shè)計(jì)。該應(yīng)用程序測(cè)試代碼的一部分如下所示。包含頭文件,并定義了Button_Press變量。對(duì)頭文件的唯一修改是定義用于按鈕的端口。
/ ***************************************************** *** 包括圖書館 ****************************************************** *** / #include#include #include“ Buttons_Debounce.h” Button_Type Button_Press; //創(chuàng)建Button變量 / **************************************************** 功能原型 ****************************************************** / void Initial(void); void delay_s(unsigned char secs); / ***************************************************** 鐘 ****************************************************** / #define _XTAL_FREQ 19660800 unsigned char count_test = 0; void __interrupt myIsr(void) { //定時(shí)器每10毫秒溢出一次 if(INTCONbits.TMR0IE && INTCONbits.TMR0IF){ Find_Button_Press(); //每10毫秒調(diào)用一次 WriteTimer0(40960); INTCONbits.TMR0IF = 0; //清除此中斷條件 } } void main(無效) { Button_Press.Full = 0x00; 最初的(); 而(1) { if(Got_Button_E)//如果已按下某個(gè)按鈕 { if(Button_Press.B0)//如果其按鈕為0 PORTCbits.RC0 =?PORTCbits.RC0; if(Button_Press.B1)//如果其按鈕為0 PORTCbits.RC1 =?PORTCbits.RC1; if(Button_Press.B2)//如果其按鈕為0 PORTCbits.RC2 =?PORTCbits.RC2; if(Button_Press.B3)//如果其按鈕為0 PORTCbits.RC3 =?PORTCbits.RC3; Button_Press.Full = 0x00; //清除所有按鈕事件 } } }
將創(chuàng)建一個(gè)定時(shí)器中斷,每10毫秒發(fā)生一次,并調(diào)用狀態(tài)機(jī)功能。在此應(yīng)用中,按鈕連接到PORTB,而LED連接到PORTC。如果按下任何按鈕,則相應(yīng)的LED會(huì)切換而不會(huì)出現(xiàn)任何延遲或彈跳問題。處理按鈕值后,將清除整個(gè)變量。
總體而言,該項(xiàng)目被認(rèn)為是在設(shè)計(jì)階段如何使用狀態(tài)機(jī)方法,從而導(dǎo)致更清晰,更不易出錯(cuò)的實(shí)現(xiàn)的一個(gè)很好的例子。
編輯:hfy
-
微控制器
+關(guān)注
關(guān)注
48文章
7953瀏覽量
155084 -
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3683瀏覽量
131401 -
計(jì)數(shù)器
+關(guān)注
關(guān)注
32文章
2291瀏覽量
96422 -
狀態(tài)機(jī)
+關(guān)注
關(guān)注
2文章
493瀏覽量
28250
發(fā)布評(píng)論請(qǐng)先 登錄
嵌入式狀態(tài)機(jī)的幾種大牛才懂的操作

嵌入式軟件開發(fā)中常用的狀態(tài)機(jī)編程實(shí)現(xiàn)
嵌入式系統(tǒng)中串口通信幀的同步方法
嵌入式系統(tǒng)的描述與設(shè)計(jì)
高速環(huán)境下FPGA或CPLD中的狀態(tài)機(jī)設(shè)計(jì)
狀態(tài)機(jī)在嵌入式系統(tǒng)中的應(yīng)用

嵌入式軟件中狀態(tài)機(jī)的抽象與實(shí)現(xiàn)
有限狀態(tài)機(jī)在嵌入式系統(tǒng)中的實(shí)現(xiàn)及應(yīng)用
有限狀態(tài)機(jī)在嵌入式軟件中的應(yīng)用
基于有限狀態(tài)機(jī)的嵌入式系統(tǒng)串口通信幀同步方法設(shè)計(jì)

嵌入式中狀態(tài)機(jī)的設(shè)置
嵌入式狀態(tài)機(jī)的編程優(yōu)點(diǎn)分析
嵌入式狀態(tài)機(jī)的設(shè)計(jì)與實(shí)現(xiàn)
LSM6DSOX嵌入式有限狀態(tài)機(jī)的使用和配置

評(píng)論