RTX51tiny 复杂应用---时钟、温度显示、按键解析
keil工程下載地址:https://www.syjshare.com/res/3SXA00Q9
一、設(shè)計需求
在清翔51單片機開發(fā)板上,用帶RTOS的方式實現(xiàn)下面的功能:
1. 板上有8個段碼LED,左邊的4個做一個計時器,顯示“MSS.Z”,其中M為分鐘,SS為秒,Z為n×0.1秒;右側(cè)的4個,其中3個顯示從18B20采集的實時溫度,最后1個顯示鍵盤的按鍵值,按鍵延時顯示按鍵0.3秒(也就是松開按鍵之后還繼續(xù)顯示0.3秒);
2. 在板上的LCD1602上,構(gòu)建一個HH:MM:SS 時分秒的鐘表,同時將DS18B20的轉(zhuǎn)換結(jié)果也顯示在這里。
二、題目分析
面對一個復(fù)雜的題目,我們需要逐步實現(xiàn)需要的功能。先實現(xiàn)一個獨立的功能,然后再增加功能,慢慢就能達(dá)到完整的要求。在實現(xiàn)獨立功能的時候要考慮代碼的可綜合性,要盡量構(gòu)建易于被調(diào)用的函數(shù),在后期將會大大方面我們的綜合優(yōu)化。
編寫代碼的時候,盡量養(yǎng)成良好的習(xí)慣。對于一個龐大的工程,其必定可以分割為很多小模塊。本題中用到了數(shù)碼管、LCD1602、DS18B20、矩陣鍵盤這些外設(shè),我們可以單獨構(gòu)建這些模塊的驅(qū)動函數(shù),后面再加以綜合。
在創(chuàng)建好RTOS工程之后,在工程文件夾下再創(chuàng)建一個hardware文件夾,然后在這個文件夾里為每個外設(shè)都創(chuàng)建一個文件夾,如下圖所示。
每一個外設(shè)都對應(yīng)一個C文件和H文件,這兩個文件放置在各自的文件夾中。這里還需要注意在keil中需要將這幾個文件夾添加到編譯路徑中,要不然軟件找不到H文件。在option中的incliude path處添加需要的路徑。
做完這些準(zhǔn)備工作后,我們就可以來分模塊來構(gòu)建我們的代碼了。
三、模塊分析
3.1 數(shù)碼管
3.1.1 數(shù)碼管顯示函數(shù)
在使用的51開發(fā)板上,一共有8個數(shù)碼管。這里我們可以編寫一個函數(shù),讓其每一位可以單獨顯示任意數(shù)字。入口參數(shù)即為每一位要顯示的數(shù)字,后面需要用到顯示的地方,只需要傳入對應(yīng)的變量即可。比如構(gòu)造如下的函數(shù):
void play_SMG(unsigned char led7,led6,led5,led4,led3,led2,led1,led0);
在實際使用的時候,比如想讓數(shù)碼管顯示1234567,就只需這樣寫:play_SMG(1,2,3,4,5,6,7)。一般我們都是要求數(shù)碼管動態(tài)顯示的,這時我們把對應(yīng)的變量傳入相應(yīng)的位置即可。
函數(shù)內(nèi)部的詳細(xì)代碼比較簡單參見附錄。
3.1.2 數(shù)碼管閃爍問題
使用RTOS的時候,由于程序是輪循執(zhí)行的。假如在一個任務(wù)中讓數(shù)碼管顯示,就存在兩個問題。
- 輪循時間間隔太短,數(shù)碼管來不及全部顯示。
- 任務(wù)數(shù)量太多,下一次顯示間隔太長。
這里要搞清楚輪循的時間怎么計算。輪循時每個任務(wù)執(zhí)行的時間是固定的,這個時間具體可以通過INT_CLOCK和TIMESHARING相乘得到。默認(rèn)的INT_CLOCK = 10000,TIMESHARING = 5。也就是每個任務(wù)執(zhí)行時間為50ms。
不光是數(shù)碼管,每個任務(wù)在執(zhí)行的時候都存在這個問題。在調(diào)試數(shù)碼管的時候,會發(fā)現(xiàn)閃爍嚴(yán)重,就是上面兩個因素的影響。相比之下,后者影響更大。假如有4個任務(wù),按照默認(rèn)的時間片設(shè)定,每次顯示之后就要等待至少150ms。這個間隔內(nèi)數(shù)碼管是不顯示的,就會看到數(shù)碼管間歇性亮,或者很晃眼。
這時候可以通過更改時間片來獲得比較好的顯示效果。
3.2 時鐘
3.2.1 產(chǎn)生時間基準(zhǔn)
我們可以通過讓數(shù)碼管顯示一個時鐘來驗證我們的函數(shù)正確性,同時這也是題目要求的一部分。
對于時鐘的構(gòu)建,可以先設(shè)定一個最小的時間基準(zhǔn),比如100ms,每個100ms對應(yīng)的變量自加一次。10個100ms之后即1s的時候,代表秒的變量自加一次。當(dāng)秒個位加到9的時候,十位加一,當(dāng)59秒的時候再過1秒,秒清零同時分的個位加一……,這個過程寫起來比較繁瑣,需要細(xì)心不要搞錯了。
這里的主要問題是怎么產(chǎn)生100ms的時間間隔。我們可以在一個任務(wù)中產(chǎn)生我們的時鐘。100ms的間隔用延時函數(shù)來產(chǎn)生。
關(guān)于RTOS的延時,系統(tǒng)中給了os_wait2( ),這個函數(shù)有兩個輸入?yún)?shù),詳細(xì)可以看幫助文檔。這里需要注意一個tick代表多長時間,這個可以在Conf_tny.51文件中查看,通過INT_CLOCK的值來計算,默認(rèn)值為10000,如果使用12M的晶振,那么這里就是10ms,也就是說如果我們寫了os_wait2( K_TMO,1),就是代表延時10ms。我們會發(fā)現(xiàn)這里最小的延時單位只能是10ms。可以更改INT_CLOCK的值來減小延時單位長度。這里我將INT_CLOCK的值改為了1000,那么一個延時單位就是1ms。需要注意,os_wait2( )中的參數(shù)類型是unsigned char,意味著我們最大只能寫255,如果需要更長的延時,可以通過for循環(huán)來構(gòu)建。
3.2.2 時鐘顯示
定義用來表示時鐘的變量:uchar H1,H0,M1,M0,S1,S0,Z;
H、M、S分別為時分秒,Z為n×0.1秒。時鐘的產(chǎn)生比較簡單,就是需要的邏輯性比較強,詳細(xì)的代碼參見附錄。
然后如下編寫job0的代碼:
void job0 (void) _task_ 0{os_create_task (1);while (1){play_SMG(M0,S1,S0,Z,0,0,0,0);?????}}可以看到前四個數(shù)碼管顯示了計時器的值,后四個數(shù)碼管一直顯示0。這說明我們的代碼是沒有問題的。對于時鐘的產(chǎn)生,這里采用了比較直觀的方式去產(chǎn)生,易于理解,但是寫起來需要注意代碼的邏輯性。我們也可以嘗試使用更簡潔的方式去構(gòu)建。
通過這個過程我們也可以發(fā)現(xiàn)代碼的綜合其實并不難,就是一步一步添加功能的過程。
3.3 鍵盤顯示
3.3.1 矩陣鍵盤掃描
矩陣鍵盤掃描的函數(shù)有很多,它的原理網(wǎng)上也講的很多,不再贅述。
很多時候我們要做的并不是自己如何去寫一個器件的驅(qū)動程序,是如何把別人拿過來用。看懂別人的代碼,可以改善并融合到自己的程序中是最好的。
假如現(xiàn)在手頭有一個鍵盤掃描的代碼,當(dāng)檢測到某個按鍵按下的時候會返回一個值,不同的按鍵按下有不同的返回值。該怎么將其加入到我們的代碼中呢?
這里可以創(chuàng)建一個任務(wù)用來進行按鍵檢測,并創(chuàng)建一個變量key用來表示按鍵值。通過不同的按鍵返回值來給key進行賦值。截取部分代碼如下:
void keys (void) _task_ 3{while (1){switch( KeyScan() ){//第一行鍵值碼case 0xee: key = 0;?????? break;case 0xde: key = 1;?????? break;case 0xbe: key = 2;?????? break;case 0x7e: key = 3;?????? break;……}}}在任務(wù)三中key的值將根據(jù)按鍵的值改變。接下來我們只需要將按鍵的值送給數(shù)碼管顯示即可。play_SMG(M0,S1,S0,Z,0,0,0,key),將key放在數(shù)碼管顯示函數(shù)的最后一位。這樣我們又完成了一個功能的添加,也沒有改變程序原本運行的狀態(tài)。
3.3.2 清除按鍵顯示
在題目中要求按鍵延時顯示按鍵0.3秒(也就是松開按鍵之后還繼續(xù)顯示0.3秒);也就是在手松開0.3秒之后要清除顯示。
這里可以設(shè)置一個標(biāo)志位flag,當(dāng)按鍵按下的時候讓flag置1,并且延時300ms后將flag清0。這樣我們就可以通過flag的狀態(tài)判斷按鍵有沒有按下,并且flag是滯后于手松開300ms刷新的。具體的代碼如下:
unsigned char KeyScan() //帶返回值的子函數(shù){unsigned char i;unsigned char cord_l,cord_h;//聲明列線和行線的值的儲存變量P3 = 0x0f;//0000 1111if( (P3 & 0x0f) != 0x0f)//判斷是否有按鍵按下{os_wait2(K_TMO,5);//軟件消抖if( (P3 & 0x0f) != 0x0f)//判斷是否有按鍵按下{cord_h = P3 & 0x0f;// 儲存行線值P3 = cord_l | 0xf0;cord_l = P3 & 0xf0;// 儲存列線值flag = 1;return (cord_l + cord_h);//返回鍵值碼???????????????? ???}????}if(flag){for(i = 0; i < 3; i++){os_wait2(K_TMO,100);}flag = 0;}return 0;}為了得到題目要求的顯示效果,我們需要稍微更改之前的數(shù)碼管顯示代碼,在對數(shù)碼管最后一位進行顯示之前先判斷flag是否為1,是1則顯示否則就不顯示。
修改的部分如下:
if(flag){P0=wei[0]; ??wela=1;wela=0;??P0=table[led0]; ?dula=1;dula=0;os_wait2(K_TMO,1);}3.4 DS18B20
3.4.1 溫度采集實現(xiàn)
對于溫度采集函數(shù)的編寫,可以參考鍵盤掃描。我們要做的是如何把別人的代碼添加到自己的程序中而不影響原本的功能。
對于詳細(xì)的溫度采集代碼不再進行講解。這里只說明如何將代碼進行綜合。創(chuàng)建一個任務(wù)用來進行溫度采集。
這個任務(wù)不斷進行溫度采集,然后刷新A1,A2,A3的值。A1,A2,A3分別為溫度值的十位、個位以及小數(shù)點后一位。然后將A1,A2,A3的值添加到數(shù)碼管顯示函數(shù)的對應(yīng)位置即可。
void dsd18b20 (void) _task_ 2{char i;while (1){sendChangeCmd();?? //發(fā)送開始采集指令temp = tmp();?????? //得到溫度值A(chǔ)1=temp/100;A2t=temp%100;A2=A2t/10;A3=A2t%10;}}下載進去之會發(fā)現(xiàn)溫度值顯示經(jīng)常會出現(xiàn)亂碼。當(dāng)拔掉DS18B20之后發(fā)現(xiàn)顯示相同的亂碼。推測是因為溫度傳感器沒有正常通訊。
3.4.2 原子操作
DS18B20需要遵循嚴(yán)格的時序,輪循的方式會打亂正常的通訊。所以需要通過原子操作,對關(guān)鍵節(jié)點進行保護。即關(guān)掉總中斷,讓RTOS暫時停止工作,等關(guān)鍵代碼執(zhí)行完畢再打開中斷。在DS18B20進行復(fù)位,寫入數(shù)據(jù),發(fā)送數(shù)據(jù)的時候需要進行保護。
比如在進行復(fù)位操作的時候可以如下編寫:
void dsreset(void){uint i;EA = 0;?? //關(guān)閉總中斷DS=0;i=103;while(i>0)i--;DS=1;i=4;while(i>0)i--;EA = 1;? //打開總中斷}在寫入一個位和讀取一個位的地方也應(yīng)該按照上面的方式進行保護。
3.4.3 溫度采集頻率
溫度采集一次是需要時間的,即發(fā)送一次采集指令之后需要延時一會,等本次采集完成再進行下一次采集。如果不加延時會有兩個后果,一是溫度采集會出錯,二是這樣會進行頻繁的進行原子操作,對RTOS的正常運行有非常大的影響。所以采集一次之后要加入一個延時,比如1s采集一次。這樣不會影響正常的溫度顯示,同時又能保證系統(tǒng)的正常運作。
實測如果不進行采集時間控制,溫度值會經(jīng)常出錯,并且數(shù)碼管的閃爍很嚴(yán)重,甚至不能正常顯示。加入延時之后有了很大改善。
3.5 LCD1602
3.5.1 引腳沖突問題
在我們使用的開發(fā)板上,1602的數(shù)據(jù)口跟數(shù)碼管的數(shù)據(jù)口是共用的。在進行液晶顯示的時候需要將數(shù)碼管的鎖存器關(guān)閉,液晶顯示完畢之后再對數(shù)碼管進行操作。
但是還有一個問題,1602的部分控制引腳跟按鍵的端口是沖突的。在進行按鍵掃描的時候必定會使液晶顯示失常。由于液晶顯示跟按鍵掃描實在兩個任務(wù)中執(zhí)行的,所以這個沖突是必然存在的。在解決過程中嘗試在液晶顯示的時候?qū)存I掃描的任務(wù)delete,當(dāng)液晶顯示完畢之后再create。但是這樣導(dǎo)致數(shù)碼管異常閃爍。沒有找到解決方案。
所以嘗試將1602外接。這里將數(shù)據(jù)口接到P1,LCD_RS 接到P2^0; LCD_RW 接到 P2^1; LCD_EN接到P2^4;然后將程序中的引腳定義進行更改。
3.5.2 數(shù)據(jù)顯示
1602的驅(qū)動函數(shù)也有很多,這里只需要將其綜合到我們的程序中。當(dāng)然代碼的質(zhì)量需要我們判斷一下,有時候還需要我們自己進行完善修改。
要顯示的值在之前都已經(jīng)得到了,這里只需要編寫一個函數(shù)將其顯示出來即可,截取部分代碼如下:
void play_LCD(){lcd_pos(0x00);? //光標(biāo)定位在第一行LCD1602_Write_Dat(H1 + 0x30);?? //顯示時LCD1602_Write_Dat(H0 + 0x30);LCD1602_Write_Dat(0x3a);……}在進行液晶初始化的時候,由于有較長的延時,也需要用原子操作保護一下。如果不加保護,屏幕不能正常顯示。
四、總體方案設(shè)計
本次所做的設(shè)計綜合難度是比較大的。在平時的學(xué)習(xí)中可能更多的掌握了單個模塊的用法,但是像這種比較復(fù)雜的系統(tǒng)鍛煉比較少。在面對這種問題的時候我們不要慌,先大概有個框架,然后一步一步來,逐步添加需要的代碼進去。比如上面模塊分析的過程就是在逐步完善代碼。
一定要具有大局觀,在進行模塊代碼編寫的時候要便于我們調(diào)用,也就是代碼的可綜合性。在程序編寫過程中,先對單個模塊進行測試,勤看現(xiàn)象,測試通過之后再逐步添加功能上去。
五、實驗現(xiàn)象
全部完成之后可以看到數(shù)碼管跟LCD1602在同步進行計時,還有溫度的顯示,當(dāng)按下按鍵,會看到數(shù)碼管最后一位顯示按鍵的數(shù)值并保持一會后清除。至此,完成了所有的設(shè)計需求。
六、總結(jié)
通過本次實踐,對RTOS有了初步的認(rèn)識,同時也鍛煉了自己編寫復(fù)雜代碼的能力,收獲很大。在以后的學(xué)習(xí)實踐中,也要勤動手,勤思考,提升自己的能力。
總結(jié)
以上是生活随笔為你收集整理的RTX51tiny 复杂应用---时钟、温度显示、按键解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 疫情下的通信人:你我的岁月静好,是谁在负
- 下一篇: 塌陷