大家好,我叫吳堅(jiān)鴻,從事單片機(jī)項(xiàng)目開發(fā)已經(jīng)有快十年了,目前跟小伙伴們在深圳開了個(gè)方案公司,專門承接項(xiàng)目開發(fā)。這些年我做的項(xiàng)目不計(jì)其數(shù),現(xiàn)在借電子信息網(wǎng)這個(gè)平臺把我認(rèn)為最有價(jià)值的東西分享給大家,我這個(gè)技術(shù)貼每個(gè)星期更新一兩篇,直到我江郎才盡為止。
第二節(jié):delay()延時(shí)實(shí)現(xiàn)LED燈的閃爍。
開場白:
上一節(jié)鴻哥列出了初學(xué)者七大誤區(qū),到底什么才是初學(xué)者關(guān)注的核心?那就是裸機(jī)奔跑的程序結(jié)構(gòu)。一個(gè)好的程序結(jié)構(gòu),本身就是一個(gè)微型的多任務(wù)操作系統(tǒng)。鴻哥教給大家的就是如何編寫這個(gè)簡單的操作系統(tǒng)。在main函數(shù)循環(huán)中用switch語句實(shí)現(xiàn)多任務(wù)并行處理的任務(wù)切換,再外加一個(gè)定時(shí)器中斷,這兩者的結(jié)合就是鴻哥多年來所有實(shí)戰(zhàn)項(xiàng)目的核心。鴻哥的程序結(jié)構(gòu)看似簡單,實(shí)際上就是那么簡單。大家不用著急,本篇連載文章現(xiàn)在才正式開始,這一節(jié)我要教會大家兩個(gè)知識點(diǎn):
第一點(diǎn):鴻哥首次提出的“三區(qū)一線”理論。此理論把程序代碼分成三個(gè)區(qū),一個(gè)延時(shí)分割線。
第二點(diǎn):delay()延時(shí)的用途。
(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。
(2)實(shí)現(xiàn)功能:讓一個(gè)LED閃爍。
(3)源代碼講解如下:
#include "REG52.H" void initial_myself(); void initial_peripheral(); void delay_short(unsigned int uiDelayshort); void delay_long(unsigned int uiDelaylong); void led_flicker(); /* 注釋一: * 吳堅(jiān)鴻個(gè)人的命名風(fēng)格:凡是輸出后綴都是_dr,凡是輸入后綴都是_sr。 * dr代表drive驅(qū)動,sr代表sensor感應(yīng)器 */ sbit led_dr=P3^5; void main() //學(xué)習(xí)要點(diǎn):深刻理解鴻哥首次提出的三區(qū)一線理論 { /* 注釋二: * initial_myself()函數(shù)屬于鴻哥三區(qū)一線理論的第一區(qū), * 專門用來初始化單片機(jī)自己的寄存器以及個(gè)別外圍要求響應(yīng)速度快的輸出設(shè)備, * 防止剛上電之后,由于輸出IO口電平狀態(tài)不確定而導(dǎo)致外圍設(shè)備誤動作, * 比如繼電器的誤動作等等。 */ initial_myself(); /* 注釋三: * 此處的delay_long()延時(shí)函數(shù)屬于第一區(qū)與第二區(qū)的分割線, * 延時(shí)時(shí)間一般是0.3秒到2秒之間,等待外圍芯片和模塊上電穩(wěn)定。 * 比如液晶模塊,AT24C02存儲芯片,DS1302時(shí)鐘芯片, * 這類芯片有個(gè)特點(diǎn),一般都是跟單片機(jī)進(jìn)行串口或并口通訊的, * 并且不要求上電立即處理的。 */ delay_long(100); /* 注釋四: * initial_peripheral()函數(shù)屬于鴻哥三區(qū)一線理論的第二區(qū), * 專門用來初始化不要求上電立即處理的外圍芯片和模塊. * 比如液晶模塊,AT24C02存儲芯片,DS1302時(shí)鐘芯片。 * 本程序基于朱兆祺51單片機(jī)學(xué)習(xí)板。 */ initial_peripheral(); /* 注釋五: * while(1){}主函數(shù)循環(huán)區(qū)屬于鴻哥三區(qū)一線理論的第三區(qū), * 專門用來編寫被循環(huán)掃描到的非中斷應(yīng)用程序 */ while(1) { led_flicker(); //LED閃爍應(yīng)用程序 } } void led_flicker() //LED閃爍應(yīng)用程序 { led_dr=1; //LED亮 delay_short(50000); //延時(shí)50000個(gè)空指令的時(shí)間 /* 注釋六: * delay_long(100)延時(shí)50000個(gè)空指令的時(shí)間,因?yàn)閮?nèi)嵌了一個(gè)500次的for循環(huán) */ led_dr=0; //LED滅 delay_long(100); //延時(shí)50000個(gè)空指令的時(shí)間 } /* 注釋七: * delay_short(unsigned int uiDelayShort)是小延時(shí)函數(shù), * 專門用在時(shí)序驅(qū)動的小延時(shí),一般uiDelayShort的數(shù)值取10左右, * 最大一般也不超過100.本例為了解釋此函數(shù)的特點(diǎn),取值范圍超過100。 * 此函數(shù)的特點(diǎn)是時(shí)間的細(xì)分度高,延時(shí)時(shí)間不宜過長。uiDelayShort數(shù)值 * 的大小就代表里面執(zhí)行了多少條空指令的時(shí)間。數(shù)值越大,延時(shí)越長。 * 時(shí)間精度不要刻意去計(jì)算,感覺差不多就行。 */ void delay_short(unsigned int uiDelayShort) { unsigned int i; for(i=0;i
總結(jié)陳詞:
鴻哥首次提出的“三區(qū)一線”理論概況了各種項(xiàng)目程序的基本分區(qū)。我后續(xù)的程序就按此分區(qū)編寫。Delay()函數(shù)的長延時(shí)適用在上電初始化。Delay()函數(shù)的短延時(shí)適用在驅(qū)動時(shí)序的脈沖延時(shí),此時(shí)的時(shí)間不能太長,本例中暫時(shí)沒有列出這方面的例子,在后面的章節(jié)中會提到。在本例源代碼中,在led_flicker()閃爍應(yīng)用程序里用到的兩個(gè)延時(shí)delay,它們的延時(shí)時(shí)間都太長了,在實(shí)戰(zhàn)項(xiàng)目中肯定不能用這種延時(shí),因?yàn)橄牡臅r(shí)間太長了,其它任務(wù)根本沒有機(jī)會執(zhí)行。那怎么辦呢?我們應(yīng)該如何改善?欲知詳情,請聽下回分解-----累計(jì)主循環(huán)次數(shù)使LED燈閃爍。
(未完待續(xù),下節(jié)更精彩,不要走開哦)
第一節(jié):吳堅(jiān)鴻談初學(xué)單片機(jī)的誤區(qū)。
(1)很難記住繁雜的寄存器?寄存器不用死記硬背,鴻哥我行走江湖多年,連一個(gè)寄存器都記不住。
需要配置寄存器的時(shí)候,直接在網(wǎng)上或者書本上參考別人現(xiàn)成的配置程序是上策,查找芯片數(shù)據(jù)手冊是中策,死記硬背寄存器是最最下策。
(2)很難記住繁雜的匯編語言指令?除非是在校學(xué)生要應(yīng)付考試或者少數(shù)工作中繞不開匯編,否則學(xué)匯編就是浪費(fèi)時(shí)間。鴻哥我行走江湖多年,從來就沒有用匯編幫客戶做過一個(gè)項(xiàng)目。
(3)C語言很難學(xué)?你不用學(xué)指針,你不用學(xué)帶形參的函數(shù),你不用學(xué)結(jié)構(gòu)體,你不用學(xué)宏定義,你不用學(xué)文件操作,你也不用死記繁瑣的數(shù)據(jù)類型。你只要會:
5條指令語句switch語句,if else語句,while語句,for語句,=賦值語句。
7個(gè)運(yùn)算符+,-,*,/,|,&,!。
4個(gè)邏輯關(guān)系符||,&&,!=,==.
3個(gè)數(shù)據(jù)類型unsigned char, unsigned int, unsigned long。
3個(gè)進(jìn)制相互轉(zhuǎn)化,二進(jìn)制,十六進(jìn)制,十進(jìn)制。
1個(gè)void函數(shù)。
1個(gè)一維數(shù)組code(或const) unsigned char array[]。
那么世界上任何一種邏輯功能的單片機(jī)軟件你都能做出來。
鴻哥我當(dāng)年剛畢業(yè)出來工作的時(shí)候才知道可以用C語言開發(fā)單片機(jī),一開始只用if語句就把項(xiàng)目做出來了,沒有用指針,沒有用帶形參的函數(shù)等復(fù)雜的功能。再到后來才慢慢開始用C語言其他的高級功能,但是我發(fā)現(xiàn)C語言其他的高級功能,本質(zhì)上都是用我前面列舉出來的最基本功能集合而成,只是書寫更加簡單方便了一點(diǎn),編譯后的機(jī)器碼都大同小異。
所以不會指針等高級功能你不用自卑,恰恰相反,當(dāng)你會最簡單的幾個(gè)語句,就把這些高級功能的程序都做出來了,你才發(fā)現(xiàn)你對底層了解得更加透切,再學(xué)那些高級功能輕而易舉。當(dāng)你裸機(jī)跑的程序都能夠協(xié)調(diào)得很好的時(shí)候,你才發(fā)現(xiàn)所謂高深的操作系統(tǒng)也不過如此,只要給你時(shí)間和金錢你也可以寫個(gè)操作系統(tǒng)來玩玩。
(4)很難記住精確時(shí)間的計(jì)算公式?經(jīng)常看到時(shí)間公式等于晶振,時(shí)鐘周期,執(zhí)行指令次數(shù)他們之間的乘除關(guān)系式。鴻哥我認(rèn)為這些都是浮云,不用糾結(jié)也不用去記,大概了解一下就可以了。不管你對公式掌握得有多精確,你都不可能做出非常精確的時(shí)間。想用單片機(jī)做一個(gè)非常精確的時(shí)間這種想法一開始就是錯(cuò)的,不可能的。真想做一個(gè)比較精確的時(shí)間,應(yīng)該用外圍時(shí)鐘芯片或者FPGA和CPLD,而不是單片機(jī)。
(5)很難記住繁雜的各種通信協(xié)議?什么IIC,SPI,232串口通訊,CAN,USB等等。這些都是浮云,你不用記那么多,你只要理解兩種通訊方式就夠了,那就是串行通訊方式和并行通訊方式。不管世界上有多少種通訊協(xié)議,物理世界上只有這兩種通訊方式,其他各種名稱的通訊協(xié)議都基于此兩種方式演變而來。
(6)很難寫短小精悍的程序?初學(xué)者不要糾結(jié)于此。做項(xiàng)目開發(fā),程序容量不是刻意追求的目標(biāo),程序多一點(diǎn)少一點(diǎn)沒關(guān)系,現(xiàn)在大容量的單片機(jī)品種非常多,容量不會是寸土寸金的事情,我們更加要關(guān)注程序的運(yùn)行效率,可讀性和可修改性。
既然鴻哥列出了那么多誤區(qū),那么什么才是初學(xué)者關(guān)注的核心?預(yù)知詳情,請聽下回分解----delay()延時(shí)實(shí)現(xiàn)LED燈的閃爍。(未完待續(xù),下節(jié)更精彩,不要走開哦)
1
第一節(jié)的前面沒基礎(chǔ)的嗎? 比如看什么書,之類
從入門開始行嗎
第三節(jié):累計(jì)主循環(huán)次數(shù)使LED燈閃爍。 開場白: 上一節(jié)鴻哥提到delay()延時(shí)函數(shù)消耗的時(shí)間太長了,其它任務(wù)根本沒有機(jī)會執(zhí)行,我們該怎么改善?本節(jié)教大家利用累計(jì)主循環(huán)次數(shù)的方法來解決這個(gè)問題。這一節(jié)要教會大家兩個(gè)知識點(diǎn): 第一點(diǎn):利用累計(jì)主循環(huán)次數(shù)的方法實(shí)現(xiàn)時(shí)間延時(shí)。 第二點(diǎn):switch核心語句之初體驗(yàn)。 鴻哥所有的實(shí)戰(zhàn)項(xiàng)目都是基于switch語句實(shí)現(xiàn)多任務(wù)并行處理。 (1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。 (2)實(shí)現(xiàn)功能:讓一個(gè)LED閃爍。 (3)源代碼講解如下:
#include "REG52.H" /* 注釋一: * const_time_level是統(tǒng)計(jì)循環(huán)次數(shù)的設(shè)定上限,數(shù)值越大,LED延時(shí)的時(shí)間越久 */ #define const_time_level 10000 void initial_myself(); void initial_peripheral(); void delay_long(unsigned int uiDelaylong); void led_flicker(); sbit led_dr=P3^5; /* 注釋二: * 吳堅(jiān)鴻個(gè)人的命名風(fēng)格:凡是switch語句里面的步驟變量后綴都是Step. * 前綴帶uc,ui,ul分別表示此變量是unsigned char,unsigned int,unsigned long. */ unsigned char ucLedStep=0; //步驟變量 unsigned int uiTimeCnt=0; //統(tǒng)計(jì)循環(huán)次數(shù)的延時(shí)計(jì)數(shù)器 void main() { initial_myself(); delay_long(100); initial_peripheral(); while(1) { led_flicker(); } } void led_flicker() ////第三區(qū) LED閃爍應(yīng)用程序 { switch(ucLedStep) { case 0: /* 注釋三: * uiTimeCnt累加循環(huán)次數(shù),只有當(dāng)它的次數(shù)大于或等于設(shè)定上限const_time_level時(shí), * 才會去改變LED燈的狀態(tài),否則CPU退出led_flicker()任務(wù),繼續(xù)快速掃描其他的任務(wù), * 這樣的程序結(jié)構(gòu)就可以達(dá)到多任務(wù)并行處理的目的。 * 本程序基于朱兆祺51單片機(jī)學(xué)習(xí)板 */ uiTimeCnt++; //累加循環(huán)次數(shù), if(uiTimeCnt>=const_time_level) //時(shí)間到 { uiTimeCnt=0; //時(shí)間計(jì)數(shù)器清零 led_dr=1; //讓LED亮 ucLedStep=1; //切換到下一個(gè)步驟 } break; case 1: uiTimeCnt++; //累加循環(huán)次數(shù), if(uiTimeCnt>=const_time_level) //時(shí)間到 { uiTimeCnt=0; //時(shí)間計(jì)數(shù)器清零 led_dr=0; //讓LED滅 ucLedStep=0; //返回到上一個(gè)步驟 } break; } } void delay_long(unsigned int uiDelayLong) { unsigned int i; unsigned int j; for(i=0;i |
第四節(jié):累計(jì)定時(shí)中斷次數(shù)使LED燈閃爍。
開場白:
上一節(jié)提到在累計(jì)主循環(huán)次數(shù)來實(shí)現(xiàn)計(jì)時(shí),隨著主函數(shù)里任務(wù)量的增加,為了保證延時(shí)時(shí)間的準(zhǔn)確性,要不斷修正設(shè)定上限閥值const_time_level 。我們該怎么解決這個(gè)問題呢?本節(jié)教大家利用累計(jì)定時(shí)中斷次數(shù)的方法來解決這個(gè)問題。這一節(jié)要教會大家四個(gè)知識點(diǎn):
第一點(diǎn):利用累計(jì)定時(shí)中斷次數(shù)的方法實(shí)現(xiàn)時(shí)間延時(shí)。
第二點(diǎn):展現(xiàn)鴻哥最完整的實(shí)戰(zhàn)程序框架。在主函數(shù)循環(huán)里用switch語句實(shí)現(xiàn)狀態(tài)機(jī)的切換,在定時(shí)中斷里累計(jì)中斷次數(shù),這兩個(gè)的結(jié)合就是我寫代碼最本質(zhì)的框架思想。
第三點(diǎn):提醒大家C語言中的int ,long變量是由幾個(gè)字節(jié)構(gòu)成的數(shù)據(jù),凡是在main函數(shù)和中斷函數(shù)里有可能同時(shí)改變的變量,這個(gè)變量應(yīng)該在主函數(shù)中被更改之前,先關(guān)閉相應(yīng)的中斷,更改完了此變量,再打開中斷,否則會留下不宜察覺的漏洞。當(dāng)然在大部分的項(xiàng)目中可以不用這么操作,但是在一些要求非常高的項(xiàng)目中,有一些核心變量必須這么做。
第四點(diǎn):定時(shí)中斷的初始值該怎么設(shè)置。不用嚴(yán)格按公式來計(jì)算時(shí)間,一般取個(gè)經(jīng)驗(yàn)值是最大初始值減去1000就可以了。具體內(nèi)容,請看源代碼講解。
(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。
(2)實(shí)現(xiàn)功能:讓一個(gè)LED閃爍。
(3)源代碼講解如下:
#include "REG52.H" #define const_time_level 200 void initial_myself(); void initial_peripheral(); void delay_long(unsigned int uiDelaylong); void led_flicker(); void T0_time(); //定時(shí)中斷函數(shù) sbit led_dr=P3^5; unsigned char ucLedStep=0; //步驟變量 unsigned int uiTimeCnt=0; //統(tǒng)計(jì)定時(shí)中斷次數(shù)的延時(shí)計(jì)數(shù)器 void main() { initial_myself(); delay_long(100); initial_peripheral(); while(1) { led_flicker(); } } void led_flicker() ////第三區(qū) LED閃爍應(yīng)用程序 { switch(ucLedStep) { case 0: /* 注釋一: * uiTimeCnt累加定時(shí)中斷的次數(shù),每一次定時(shí)中斷它都會在中斷函數(shù)里自加一。 * 只有當(dāng)它的次數(shù)大于或等于設(shè)定上限const_time_level時(shí), * 才會去改變LED燈的狀態(tài),否則CPU退出led_flicker()任務(wù),繼續(xù)快速掃描其他的任務(wù), * 這樣的程序結(jié)構(gòu)就可以達(dá)到多任務(wù)并行處理的目的。這就是鴻哥在所有開發(fā)項(xiàng)目中的核心框架。 */ if(uiTimeCnt>=const_time_level) //時(shí)間到 { /* 注釋二: * ET0=0;uiTimeCnt=0;ET0=1;----在清零uiTimeCnt之前,為什么要先禁止定時(shí)中斷? * 因?yàn)閡iTimeCnt是unsigned int類型,本質(zhì)上是由兩個(gè)字節(jié)組成。 * 在C語言中uiTimeCnt=0看似一條指令,實(shí)際上經(jīng)過編譯之后它不只一條匯編指令。 * 由于定時(shí)中斷函數(shù)里也對這個(gè)變量進(jìn)行累加操作,如果不禁止定時(shí)中斷, * 那么uiTimeCnt這個(gè)變量在main()函數(shù)中還沒被完全清零的時(shí)候,如果這個(gè)時(shí)候 * 突然來一個(gè)定時(shí)中斷,并且在中斷里又更改了此變量,這種情況在某些要求高的 * 項(xiàng)目上會是一個(gè)不容易察覺的漏洞,為項(xiàng)目帶來隱患。當(dāng)然,大部分的普通項(xiàng)目, * 都可以不用那么嚴(yán)格,可以不用禁止定時(shí)中斷。在這里只是提醒各位初學(xué)者有這種情況。 */ ET0=0; //禁止定時(shí)中斷 uiTimeCnt=0; //時(shí)間計(jì)數(shù)器清零 ET0=1; //開啟定時(shí)中斷 led_dr=1; //讓LED亮 ucLedStep=1; //切換到下一個(gè)步驟 } break; case 1: if(uiTimeCnt>=const_time_level) //時(shí)間到 { ET0=0; //禁止定時(shí)中斷 uiTimeCnt=0; //時(shí)間計(jì)數(shù)器清零 ET0=1; //開啟定時(shí)中斷 led_dr=0; //讓LED滅 ucLedStep=0; //返回到上一個(gè)步驟 } break; } } /* 注釋三: * C51的中斷函數(shù)格式如下: * void 函數(shù)名() interrupt 中斷號 * { * 中斷程序內(nèi)容 * } * 函數(shù)名可以隨便取,只要不是編譯器已經(jīng)征用的關(guān)鍵字。 * 這里最關(guān)鍵的是中斷號,不同的中斷號代表不同類型的中斷。 * 定時(shí)中斷的中斷號是 1.至于其它中斷的中斷號,大家可以查找 * 相關(guān)書籍和資料。大家進(jìn)入中斷時(shí),必須先清除中斷標(biāo)志,并且 * 關(guān)閉中斷,然后再寫代碼,最后出來時(shí),記得重裝初始值,并且 * 打開中斷。 */ void T0_time() interrupt 1 { TF0=0; //清除中斷標(biāo)志 TR0=0; //關(guān)中斷 if(uiTimeCnt<0xffff) //設(shè)定這個(gè)條件,防止uiTimeCnt超范圍。 { uiTimeCnt++; //累加定時(shí)中斷的次數(shù), } TH0=0xf8; //重裝初始值(65535-2000)=63535=0xf82f TL0=0x2f; TR0=1; //開中斷 } void delay_long(unsigned int uiDelayLong) { unsigned int i; unsigned int j; for(i=0;i總結(jié)陳詞:
本節(jié)程序麻雀雖小五臟俱全。在本節(jié)中已經(jīng)展示了我最完整的實(shí)戰(zhàn)程序框架。本節(jié)程序只有一個(gè)LED燈閃爍的單任務(wù),如果要多增加一個(gè)任務(wù)來并行處理,該怎么辦?欲知詳情,請聽下回分解-----蜂鳴器的驅(qū)動程序。
(未完待續(xù),下節(jié)更精彩,不要走開哦)
呵呵,從FSY一路追到這里,走到哪里都是焦點(diǎn),崇拜中
第五節(jié):蜂鳴器的驅(qū)動程序。
開場白:
上一節(jié)講了利用累計(jì)定時(shí)中斷次數(shù)實(shí)現(xiàn)LED燈閃爍,這個(gè)例子同時(shí)也第一次展示了我最完整的實(shí)戰(zhàn)程序框架:用switch語句實(shí)現(xiàn)狀態(tài)機(jī),外加定時(shí)中斷。這個(gè)框架看似簡單,實(shí)際上就是那么簡單。我做的所有開發(fā)項(xiàng)目都是基于這個(gè)簡單框架,但是非常好用。上一節(jié)只有一個(gè)單任務(wù)的LED燈在閃爍,這節(jié)開始,我們多增加一個(gè)蜂鳴器報(bào)警的任務(wù),要教會大家四個(gè)知識點(diǎn):
第一點(diǎn):蜂鳴器的驅(qū)動程序框架編寫。
第二點(diǎn):多任務(wù)處理的程序框架。
第三點(diǎn):如何控制蜂鳴器聲音的長叫和短叫。
第四點(diǎn):如何知道1秒鐘需要多少個(gè)定時(shí)中斷,也就是如何按比例修正時(shí)間精度。具體內(nèi)容,請看源代碼講解。
(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。
(2)實(shí)現(xiàn)功能:同時(shí)跑兩個(gè)任務(wù),第一個(gè)任務(wù)讓一個(gè)LED燈1秒鐘閃爍一次。第二個(gè)任務(wù)讓蜂鳴器在前面3秒發(fā)生一次短叫報(bào)警,在后面6秒發(fā)生一次長叫報(bào)警,反復(fù)循環(huán)。
(3)源代碼講解如下:
#include "REG52.H" /* 注釋一: * 如何知道1秒鐘需要多少個(gè)定時(shí)中斷? * 這個(gè)需要編寫一段小程序測試,得到測試的結(jié)果后再按比例修正。 * 步驟: * 第一步:在程序代碼上先寫入1秒鐘大概需要200個(gè)定時(shí)中斷。 * 第二步:基于以上1秒鐘的基準(zhǔn),編寫一個(gè)60秒的簡單測試程序(如果編寫超過 * 60秒的時(shí)間,這個(gè)精度還會更高)。比如,編寫一個(gè)用蜂鳴器的聲音來識別計(jì)時(shí)的 * 起始和終止的測試程序。 * 第三步:把程序燒錄進(jìn)單片機(jī)后,上電開始測試,手上同步打開手機(jī)里的秒表。 * 如果單片機(jī)僅僅跑了27秒。 * 第四步:那么最終得出1秒鐘需要的定時(shí)中斷次數(shù)是:const_time_1s=(200*60)/27=444 */ #define const_time_05s 222 //0.5秒鐘的時(shí)間需要的定時(shí)中斷次數(shù) #define const_time_1s 444 //1秒鐘的時(shí)間需要的定時(shí)中斷次數(shù) #define const_time_3s 1332 //3秒鐘的時(shí)間需要的定時(shí)中斷次數(shù) #define const_time_6s 2664 //6秒鐘的時(shí)間需要的定時(shí)中斷次數(shù) #define const_voice_short 40 //蜂鳴器短叫的持續(xù)時(shí)間 #define const_voice_long 200 //蜂鳴器長叫的持續(xù)時(shí)間 void initial_myself(); void initial_peripheral(); void delay_long(unsigned int uiDelaylong); void led_flicker(); void alarm_run(); void T0_time(); //定時(shí)中斷函數(shù) sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口 sbit led_dr=P3^5; //LED燈的驅(qū)動IO口 unsigned char ucLedStep=0; //LED燈的步驟變量 unsigned int uiTimeLedCnt=0; //LED燈統(tǒng)計(jì)定時(shí)中斷次數(shù)的延時(shí)計(jì)數(shù)器 unsigned char ucAlarmStep=0; //報(bào)警的步驟變量 unsigned int uiTimeAlarmCnt=0; //報(bào)警統(tǒng)計(jì)定時(shí)中斷次數(shù)的延時(shí)計(jì)數(shù)器 unsigned int uiVoiceCnt=0; //蜂鳴器鳴叫的持續(xù)時(shí)間計(jì)數(shù)器 void main() { initial_myself(); delay_long(100); initial_peripheral(); while(1) { led_flicker(); //第一個(gè)任務(wù)LED燈閃爍 alarm_run(); //第二個(gè)任務(wù)報(bào)警器定時(shí)報(bào)警 } } void led_flicker() //第三區(qū) LED閃爍應(yīng)用程序 { switch(ucLedStep) { case 0: if(uiTimeLedCnt>=const_time_05s) //時(shí)間到 { uiTimeLedCnt=0; //時(shí)間計(jì)數(shù)器清零 led_dr=1; //讓LED亮 ucLedStep=1; //切換到下一個(gè)步驟 } break; case 1: if(uiTimeLedCnt>=const_time_05s) //時(shí)間到 { uiTimeLedCnt=0; //時(shí)間計(jì)數(shù)器清零 led_dr=0; //讓LED滅 ucLedStep=0; //返回到上一個(gè)步驟 } break; } } void alarm_run() //第三區(qū) 報(bào)警器的應(yīng)用程序 { switch(ucAlarmStep) { case 0: if(uiTimeAlarmCnt>=const_time_3s) //時(shí)間到 { uiTimeAlarmCnt=0; //時(shí)間計(jì)數(shù)器清零 /* 注釋二: * 只要變量uiVoiceCnt不為0,蜂鳴器就會在定時(shí)中斷函數(shù)里啟動鳴叫,并且自減uiVoiceCnt * 直到uiVoiceCnt為0時(shí)才停止鳴叫。因此控制uiVoiceCnt變量的大小就是控制聲音的長短。 */ uiVoiceCnt=const_voice_short; //蜂鳴器短叫 ucAlarmStep=1; //切換到下一個(gè)步驟 } break; case 1: if(uiTimeAlarmCnt>=const_time_6s) //時(shí)間到 { uiTimeAlarmCnt=0; //時(shí)間計(jì)數(shù)器清零 uiVoiceCnt=const_voice_long; //蜂鳴器長叫 ucAlarmStep=0; //返回到上一個(gè)步驟 } break; } } void T0_time() interrupt 1 { TF0=0; //清除中斷標(biāo)志 TR0=0; //關(guān)中斷 if(uiTimeLedCnt<0xffff) //設(shè)定這個(gè)條件,防止uiTimeLedCnt超范圍。 { uiTimeLedCnt++; //LED燈的時(shí)間計(jì)數(shù)器,累加定時(shí)中斷的次數(shù), } if(uiTimeAlarmCnt<0xffff) //設(shè)定這個(gè)條件,防止uiTimeAlarmCnt超范圍。 { uiTimeAlarmCnt++; //報(bào)警的時(shí)間計(jì)數(shù)器,累加定時(shí)中斷的次數(shù), } /* 注釋三: * 為什么不把驅(qū)動蜂鳴器這段代碼放到main函數(shù)的循環(huán)里去? * 因?yàn)榉旁诙〞r(shí)中斷里,能保證蜂鳴器的聲音長度是一致的, * 如果放在main循環(huán)里,聲音的長度就有可能受到某些必須 * 一氣呵成的任務(wù)干擾,得不到及時(shí)響應(yīng),影響聲音長度的一致性。 */ if(uiVoiceCnt!=0) { uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫 beep_dr=0; //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。 } else { ; //此處多加一個(gè)空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。 beep_dr=1; //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。 } TH0=0xf8; //重裝初始值(65535-2000)=63535=0xf82f TL0=0x2f; TR0=1; //開中斷 } void delay_long(unsigned int uiDelayLong) { unsigned int i; unsigned int j; for(i=0;i
總結(jié)陳詞:
本節(jié)程序已經(jīng)展示了一個(gè)多任務(wù)處理的基本思路,假如要實(shí)現(xiàn)一個(gè)獨(dú)立按鍵檢測,能不能也按照這種思路來處理呢?欲知詳情,請聽下回分解-----在主函數(shù)中利用累計(jì)主循環(huán)次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。
(未完待續(xù),下節(jié)更精彩,不要走開哦)
第六節(jié):在主函數(shù)中利用累計(jì)主循環(huán)次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。
開場白:
上一節(jié)講了多任務(wù)中蜂鳴器驅(qū)動程序的框架,這節(jié)繼續(xù)利用多任務(wù)處理的方式,在主函數(shù)中利用累計(jì)主循環(huán)次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。要教會大家四個(gè)知識點(diǎn):
第一點(diǎn):獨(dú)立按鍵的驅(qū)動程序框架。
第二點(diǎn):用累計(jì)主循環(huán)次數(shù)來實(shí)現(xiàn)去抖動的延時(shí)。
第三點(diǎn):靈活運(yùn)用防止按鍵不松手后一直觸發(fā)的按鍵自鎖標(biāo)志。
第四點(diǎn):在按鍵去抖動延時(shí)計(jì)時(shí)中,添加一個(gè)抗干擾的軟件監(jiān)控判斷。一旦發(fā)現(xiàn)瞬間雜波干擾,馬上把延時(shí)計(jì)數(shù)器清零。這種方法是我在復(fù)雜的工控項(xiàng)目中總結(jié)出來的。以后凡是用到開關(guān)感應(yīng)器的地方,都可以用類似的方法實(shí)現(xiàn)軟件上的抗干擾處理。具體內(nèi)容,請看源代碼講解。
(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨(dú)立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨(dú)立按鍵的觸發(fā)地GND。
(2)實(shí)現(xiàn)功能:有兩個(gè)獨(dú)立按鍵,每按一個(gè)獨(dú)立按鍵,蜂鳴器發(fā)出“滴”的一聲后就停。
(3)源代碼講解如下:
#include "REG52.H" #define const_voice_short 40 //蜂鳴器短叫的持續(xù)時(shí)間 /* 注釋一: * 調(diào)整抖動時(shí)間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。 * 去抖動的時(shí)間本質(zhì)上等于累計(jì)主循環(huán)次數(shù)的時(shí)間。 */ #define const_key_time1 500 //按鍵去抖動延時(shí)的時(shí)間 #define const_key_time2 500 //按鍵去抖動延時(shí)的時(shí)間 void initial_myself(); void initial_peripheral(); void delay_long(unsigned int uiDelaylong); void T0_time(); //定時(shí)中斷函數(shù) void key_service(); //按鍵服務(wù)的應(yīng)用程序 void key_scan(); //按鍵掃描函數(shù) sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵 sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵 sbit key_gnd_dr=P0^4; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平 sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口 unsigned char ucKeySec=0; //被觸發(fā)的按鍵編號 unsigned int uiKeyTimeCnt1=0; //按鍵去抖動延時(shí)計(jì)數(shù)器 unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志 unsigned int uiKeyTimeCnt2=0; //按鍵去抖動延時(shí)計(jì)數(shù)器 unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志 unsigned int uiVoiceCnt=0; //蜂鳴器鳴叫的持續(xù)時(shí)間計(jì)數(shù)器 void main() { initial_myself(); delay_long(100); initial_peripheral(); while(1) { key_scan(); //按鍵掃描函數(shù) key_service(); //按鍵服務(wù)的應(yīng)用程序 } } void key_scan()//按鍵掃描函數(shù) { /* 注釋二: * 獨(dú)立按鍵掃描的詳細(xì)過程: * 第一步:平時(shí)沒有按鍵被觸發(fā)時(shí),按鍵的自鎖標(biāo)志和去抖動延時(shí)計(jì)數(shù)器一直被清零。 * 第二步:一旦有按鍵被按下,去抖動延時(shí)計(jì)數(shù)器開始累加,在還沒累加到 * 閥值const_key_time1時(shí),如果在這期間由于受外界干擾或者按鍵抖動,而使 * IO口突然瞬間觸發(fā)成高電平,這個(gè)時(shí)候馬上又把延時(shí)計(jì)數(shù)器uiKeyTimeCnt1 * 清零了,這個(gè)過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實(shí)戰(zhàn)中摸索出來的。 * 以后凡是用到開關(guān)感應(yīng)器的時(shí)候,都可以用類似這樣的方法去干擾。 * 第三步:如果按鍵按下的時(shí)間超過了閥值const_key_time1,則觸發(fā)按鍵,把編號ucKeySec賦值。 * 同時(shí),馬上把自鎖標(biāo)志ucKeyLock1置位,防止按住按鍵不松手后一直觸發(fā)。 * 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時(shí)清零,為下一次自鎖做準(zhǔn)備。 * 第五步:以上整個(gè)過程,就是識別按鍵IO口下降沿觸發(fā)的過程。 */ if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位 { ucKeyLock1=0; //按鍵自鎖標(biāo)志清零 uiKeyTimeCnt1=0;//按鍵去抖動延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來的。 } else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下 { ++uiKeyTimeCnt1; //延時(shí)計(jì)數(shù)器 if(uiKeyTimeCnt1>const_key_time1) { uiKeyTimeCnt1=0; ucKeyLock1=1; //自鎖按鍵置位,避免一直觸發(fā) ucKeySec=1; //觸發(fā)1號鍵 } } if(key_sr2==1) { ucKeyLock2=0; uiKeyTimeCnt2=0; } else if(ucKeyLock2==0) { ++uiKeyTimeCnt2; if(uiKeyTimeCnt2>const_key_time2) { uiKeyTimeCnt2=0; ucKeyLock2=1; ucKeySec=2; //觸發(fā)2號鍵 } } } void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序 { switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換 { case 1:// 1號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。 ucKeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā) break; case 2:// 2號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。 ucKeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā) break; } } void T0_time() interrupt 1 { TF0=0; //清除中斷標(biāo)志 TR0=0; //關(guān)中斷 if(uiVoiceCnt!=0) { uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫 beep_dr=0; //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。 } else { ; //此處多加一個(gè)空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。 beep_dr=1; //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。 } TH0=0xf8; //重裝初始值(65535-2000)=63535=0xf82f TL0=0x2f; TR0=1; //開中斷 } void delay_long(unsigned int uiDelayLong) { unsigned int i; unsigned int j; for(i=0;i總結(jié)陳詞:
本節(jié)程序已經(jīng)展示了在主函數(shù)中,利用累計(jì)主循環(huán)次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。這種方法我經(jīng)常在實(shí)戰(zhàn)用應(yīng)用,但是它也有一個(gè)小小的不足,隨著在主函數(shù)循環(huán)中任務(wù)量的增加,為了保證去抖動延時(shí)的時(shí)間一致性,要適當(dāng)調(diào)整一下去抖動的閥值const_key_time1。如何解決這個(gè)問題呢?欲知詳情,請聽下回分解-----在主函數(shù)中利用累計(jì)定時(shí)中斷的次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。
(未完待續(xù),下節(jié)更精彩,不要走開哦)
第七節(jié):在主函數(shù)中利用累計(jì)定時(shí)中斷的次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。
開場白:
上一節(jié)講了在主函數(shù)中利用累計(jì)主循環(huán)次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測,但是它也有一個(gè)小小的不足,隨著在主函數(shù)中任務(wù)量的增加,為了保證去抖動延時(shí)的時(shí)間一致性,要適當(dāng)調(diào)整一下去抖動的時(shí)間閥值const_key_time1。如何解決這個(gè)問題呢?這一節(jié)教大家在主函數(shù)中利用累計(jì)定時(shí)中斷的次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測,可以有效地避免這個(gè)問題。要教會大家一個(gè)知識點(diǎn):如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以在主函數(shù)中,利用累計(jì)定時(shí)中斷的次數(shù)來實(shí)現(xiàn)去抖動的延時(shí)。
具體內(nèi)容,請看源代碼講解。
(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨(dú)立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨(dú)立按鍵的觸發(fā)地GND。
(2)實(shí)現(xiàn)功能:有兩個(gè)獨(dú)立按鍵,每按一個(gè)獨(dú)立按鍵,蜂鳴器發(fā)出“滴”的一聲后就停。
(3)源代碼講解如下:
#include "REG52.H" #define const_voice_short 40 //蜂鳴器短叫的持續(xù)時(shí)間 /* 注釋一: * 調(diào)整抖動時(shí)間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。 * 去抖動的時(shí)間本質(zhì)上等于累計(jì)定時(shí)中斷次數(shù)的時(shí)間。 */ #define const_key_time1 30 //按鍵去抖動延時(shí)的時(shí)間 #define const_key_time2 30 //按鍵去抖動延時(shí)的時(shí)間 void initial_myself(); void initial_peripheral(); void delay_long(unsigned int uiDelaylong); void T0_time(); //定時(shí)中斷函數(shù) void key_service(); //按鍵服務(wù)的應(yīng)用程序 void key_scan(); //按鍵掃描函數(shù) sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵 sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵 sbit key_gnd_dr=P0^4; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平 sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口 unsigned char ucKeySec=0; //被觸發(fā)的按鍵編號 unsigned char ucKeyStartFlag1=0; //啟動定時(shí)中斷計(jì)數(shù)的開關(guān) unsigned int uiKeyTimeCnt1=0; //按鍵去抖動延時(shí)計(jì)數(shù)器 unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志 unsigned char ucKeyStartFlag2=0; //啟動定時(shí)中斷計(jì)數(shù)的開關(guān) unsigned int uiKeyTimeCnt2=0; //按鍵去抖動延時(shí)計(jì)數(shù)器 unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志 unsigned int uiVoiceCnt=0; //蜂鳴器鳴叫的持續(xù)時(shí)間計(jì)數(shù)器 void main() { initial_myself(); delay_long(100); initial_peripheral(); while(1) { key_scan(); //按鍵掃描函數(shù) key_service(); //按鍵服務(wù)的應(yīng)用程序 } } void key_scan()//按鍵掃描函數(shù) { /* 注釋二: * 獨(dú)立按鍵掃描的詳細(xì)過程: * 第一步:平時(shí)沒有按鍵被觸發(fā)時(shí),按鍵的自鎖標(biāo)志,計(jì)時(shí)器開關(guān)和去抖動延時(shí)計(jì)數(shù)器一直被清零。 * 第二步:一旦有按鍵被按下,啟動計(jì)時(shí)器,去抖動延時(shí)計(jì)數(shù)器開始在定時(shí)中斷函數(shù)里累加,在還沒累加到 * 閥值const_key_time1時(shí),如果在這期間由于受外界干擾或者按鍵抖動,而使 * IO口突然瞬間觸發(fā)成高電平,這個(gè)時(shí)候馬上停止計(jì)時(shí),并且把延時(shí)計(jì)數(shù)器uiKeyTimeCnt1 * 清零了,這個(gè)過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實(shí)戰(zhàn)中摸索出來的。 * 以后凡是用到開關(guān)感應(yīng)器的時(shí)候,都可以用類似這樣的方法去干擾。 * 第三步:如果按鍵按下的時(shí)間超過了閥值const_key_time1,則觸發(fā)按鍵,把編號ucKeySec賦值。 * 同時(shí),馬上把自鎖標(biāo)志ucKeyLock1置位,防止按住按鍵不松手后一直觸發(fā)。 * 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時(shí)清零,為下一次自鎖做準(zhǔn)備。 * 第五步:以上整個(gè)過程,就是識別按鍵IO口下降沿觸發(fā)的過程。 */ if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位 { ucKeyLock1=0; //按鍵自鎖標(biāo)志清零 ucKeyStartFlag1=0; //停止計(jì)數(shù)器 uiKeyTimeCnt1=0;//按鍵去抖動延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來的。 } else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下 { ucKeyStartFlag1=1; //啟動計(jì)數(shù)器 if(uiKeyTimeCnt1>const_key_time1) { ucKeyStartFlag1=0; //停止計(jì)數(shù)器 uiKeyTimeCnt1=0; ucKeyLock1=1; //自鎖按鍵置位,避免一直觸發(fā) ucKeySec=1; //觸發(fā)1號鍵 } } if(key_sr2==1) { ucKeyLock2=0; ucKeyStartFlag2=0; //停止計(jì)數(shù)器 uiKeyTimeCnt2=0; } else if(ucKeyLock2==0) { ucKeyStartFlag2=1; //啟動計(jì)數(shù)器 if(uiKeyTimeCnt2>const_key_time2) { ucKeyStartFlag2=0; //停止計(jì)數(shù)器 uiKeyTimeCnt2=0; ucKeyLock2=1; ucKeySec=2; //觸發(fā)2號鍵 } } } void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序 { switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換 { case 1:// 1號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。 ucKeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā) break; case 2:// 2號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。 ucKeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā) break; } } void T0_time() interrupt 1 { TF0=0; //清除中斷標(biāo)志 TR0=0; //關(guān)中斷 if(ucKeyStartFlag1==1)//啟動計(jì)數(shù)器 { if(uiKeyTimeCnt1<0xffff) //防止計(jì)數(shù)器超范圍 { uiKeyTimeCnt1++; } } if(ucKeyStartFlag2==1)//啟動計(jì)數(shù)器 { if(uiKeyTimeCnt2<0xffff) //防止計(jì)數(shù)器超范圍 { uiKeyTimeCnt2++; } } if(uiVoiceCnt!=0) { uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫 beep_dr=0; //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。 } else { ; //此處多加一個(gè)空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。 beep_dr=1; //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。 } TH0=0xf8; //重裝初始值(65535-2000)=63535=0xf82f TL0=0x2f; TR0=1; //開中斷 } void delay_long(unsigned int uiDelayLong) { unsigned int i; unsigned int j; for(i=0;i
總結(jié)陳詞:
本節(jié)程序已經(jīng)展示了在主函數(shù)中,利用累計(jì)定時(shí)中斷次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測。這種方法我也經(jīng)常在實(shí)戰(zhàn)用應(yīng)用,但是如果在某些項(xiàng)目中,需要在主函數(shù)里間歇性地執(zhí)行一些一氣呵成的耗時(shí)任務(wù),這種方法就不是很實(shí)用,因?yàn)楫?dāng)主函數(shù)正在處理一氣呵成的耗時(shí)任務(wù)時(shí),這個(gè)時(shí)候如果有按鍵按下來,就有可能沒有及時(shí)被響應(yīng)到而遺漏了。那有什么方法可以解決這類項(xiàng)目中遇到的問題?欲知詳情,請聽下回分解-----在定時(shí)中斷函數(shù)里執(zhí)行獨(dú)立按鍵的掃描程序。
(未完待續(xù),下節(jié)更精彩,不要走開哦)
第八節(jié):在定時(shí)中斷函數(shù)里執(zhí)行獨(dú)立按鍵的掃描程序。
開場白:
上一節(jié)講了在主函數(shù)中利用累計(jì)定時(shí)中斷的次數(shù)來實(shí)現(xiàn)獨(dú)立按鍵的檢測,但是如果在某些項(xiàng)目中,需要在主函數(shù)里間歇性地執(zhí)行一些一氣呵成的耗時(shí)任務(wù),當(dāng)主函數(shù)正在處理一氣呵成的耗時(shí)任務(wù)時(shí)(前提是沒有關(guān)閉定時(shí)器中斷),這個(gè)時(shí)候如果有按鍵按下來,就有可能沒有及時(shí)被響應(yīng)到而遺漏了。在定時(shí)中斷函數(shù)里處理獨(dú)立按鍵的掃描程序,可以避免這個(gè)問題。要教會大家一個(gè)知識點(diǎn):如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以在定時(shí)中斷函數(shù)里處理獨(dú)立按鍵的掃描程序。具體內(nèi)容,請看源代碼講解。
(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨(dú)立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨(dú)立按鍵的觸發(fā)地GND。
(2)實(shí)現(xiàn)功能:有兩個(gè)獨(dú)立按鍵,每按一個(gè)獨(dú)立按鍵,蜂鳴器發(fā)出“滴”的一聲后就停。
(3)源代碼講解如下:
#include "REG52.H" #define const_voice_short 40 //蜂鳴器短叫的持續(xù)時(shí)間 /* 注釋一: * 調(diào)整抖動時(shí)間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。 * 去抖動的時(shí)間本質(zhì)上等于累計(jì)定時(shí)中斷次數(shù)的時(shí)間。 */ #define const_key_time1 20 //按鍵去抖動延時(shí)的時(shí)間 #define const_key_time2 20 //按鍵去抖動延時(shí)的時(shí)間 void initial_myself(); void initial_peripheral(); void delay_long(unsigned int uiDelaylong); void T0_time(); //定時(shí)中斷函數(shù) void key_service(); //按鍵服務(wù)的應(yīng)用程序 void key_scan(); //按鍵掃描函數(shù) 放在定時(shí)中斷里 sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵 sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵 sbit key_gnd_dr=P0^4; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平 sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口 unsigned char ucKeySec=0; //被觸發(fā)的按鍵編號 unsigned int uiKeyTimeCnt1=0; //按鍵去抖動延時(shí)計(jì)數(shù)器 unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志 unsigned int uiKeyTimeCnt2=0; //按鍵去抖動延時(shí)計(jì)數(shù)器 unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志 unsigned int uiVoiceCnt=0; //蜂鳴器鳴叫的持續(xù)時(shí)間計(jì)數(shù)器 void main() { initial_myself(); delay_long(100); initial_peripheral(); while(1) { key_service(); //按鍵服務(wù)的應(yīng)用程序 } } void key_scan()//按鍵掃描函數(shù) 放在定時(shí)中斷里 { /* 注釋二: * 獨(dú)立按鍵掃描的詳細(xì)過程: * 第一步:平時(shí)沒有按鍵被觸發(fā)時(shí),按鍵的自鎖標(biāo)志,去抖動延時(shí)計(jì)數(shù)器一直被清零。 * 第二步:一旦有按鍵被按下,去抖動延時(shí)計(jì)數(shù)器開始在定時(shí)中斷函數(shù)里累加,在還沒累加到 * 閥值const_key_time1時(shí),如果在這期間由于受外界干擾或者按鍵抖動,而使 * IO口突然瞬間觸發(fā)成高電平,這個(gè)時(shí)候馬上把延時(shí)計(jì)數(shù)器uiKeyTimeCnt1 * 清零了,這個(gè)過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實(shí)戰(zhàn)中摸索出來的。 * 以后凡是用到開關(guān)感應(yīng)器的時(shí)候,都可以用類似這樣的方法去干擾。 * 第三步:如果按鍵按下的時(shí)間超過了閥值const_key_time1,則觸發(fā)按鍵,把編號ucKeySec賦值。 * 同時(shí),馬上把自鎖標(biāo)志ucKeyLock1置位,防止按住按鍵不松手后一直觸發(fā)。 * 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時(shí)清零,為下一次自鎖做準(zhǔn)備。 * 第五步:以上整個(gè)過程,就是識別按鍵IO口下降沿觸發(fā)的過程。 */ if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位 { ucKeyLock1=0; //按鍵自鎖標(biāo)志清零 uiKeyTimeCnt1=0;//按鍵去抖動延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來的。 } else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下 { uiKeyTimeCnt1++; //累加定時(shí)中斷次數(shù) if(uiKeyTimeCnt1>const_key_time1) { uiKeyTimeCnt1=0; ucKeyLock1=1; //自鎖按鍵置位,避免一直觸發(fā) ucKeySec=1; //觸發(fā)1號鍵 } } if(key_sr2==1) { ucKeyLock2=0; uiKeyTimeCnt2=0; } else if(ucKeyLock2==0) { uiKeyTimeCnt2++; //累加定時(shí)中斷次數(shù) if(uiKeyTimeCnt2>const_key_time2) { uiKeyTimeCnt2=0; ucKeyLock2=1; ucKeySec=2; //觸發(fā)2號鍵 } } } void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序 { switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換 { case 1:// 1號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。 ucKeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā) break; case 2:// 2號鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。 ucKeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā) break; } } void T0_time() interrupt 1 { TF0=0; //清除中斷標(biāo)志 TR0=0; //關(guān)中斷 key_scan(); //按鍵掃描函數(shù) if(uiVoiceCnt!=0) { uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫 beep_dr=0; //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。 } else { ; //此處多加一個(gè)空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。 beep_dr=1; //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。 } TH0=0xf8; //重裝初始值(65535-2000)=63535=0xf82f TL0=0x2f; TR0=1; //開中斷 } void delay_long(unsigned int uiDelayLong) { unsigned int i; unsigned int j; for(i=0;i
總結(jié)陳詞:
本節(jié)程序已經(jīng)展示了在定時(shí)中斷函數(shù)里執(zhí)行獨(dú)立按鍵的掃描程序。這節(jié)和前面兩節(jié)所講的掃描方式,我都在項(xiàng)目上用過,具體跟項(xiàng)目的側(cè)重點(diǎn)不同來選擇不同的方式,我本人用得最多的就是當(dāng)前這種方式。假如要獨(dú)立按鍵實(shí)現(xiàn)類似鼠標(biāo)的雙擊功能,我們改怎么寫程序?欲知詳情,請聽下回分解-----獨(dú)立按鍵的雙擊按鍵觸發(fā)。
(未完待續(xù),下節(jié)更精彩,不要走開哦)
第九節(jié):獨(dú)立按鍵的雙擊按鍵觸發(fā)。
開場白:
上一節(jié)講了在定時(shí)中斷函數(shù)里處理獨(dú)立按鍵的掃描程序,這種結(jié)構(gòu)的程序我用在了很多項(xiàng)目上。這一節(jié)教大家如何實(shí)現(xiàn)按鍵雙擊觸發(fā)的功能,這種功能類似鼠標(biāo)的雙擊。要教會大家一個(gè)知識點(diǎn):如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以實(shí)現(xiàn)按鍵的雙擊功能。
具體內(nèi)容,請看源代碼講解。
(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨(dú)立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨(dú)立按鍵的觸發(fā)地GND。
(2)實(shí)現(xiàn)功能:有兩個(gè)獨(dú)立按鍵,每雙擊一個(gè)獨(dú)立按鍵,蜂鳴器發(fā)出“滴”的一聲后就停。
(3)源代碼講解如下:
#include "REG52.H" #define const_voice_short 40 //蜂鳴器短叫的持續(xù)時(shí)間 /* 注釋一: * 調(diào)整抖動時(shí)間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。 * 去抖動的時(shí)間本質(zhì)上等于累計(jì)定時(shí)中斷次數(shù)的時(shí)間。 */ #define const_key_time1 20 //按鍵去抖動延時(shí)的時(shí)間 #define const_key_time2 20 //按鍵去抖動延時(shí)的時(shí)間 /* 注釋二: * 有效時(shí)間差,是指連續(xù)兩次按鍵觸發(fā)的最大有效間隔時(shí)間。 * 如果雙擊的兩個(gè)按鍵按下的時(shí)間間隔太長,則視為無效雙擊。 */ #define const_interval_time1 200 //連續(xù)兩次按鍵之間的有效時(shí)間差 #define const_interval_time2 200 //連續(xù)兩次按鍵之間的有效時(shí)間差 void initial_myself(); void initial_peripheral(); void delay_long(unsigned int uiDelaylong); void T0_time(); //定時(shí)中斷函數(shù) void key_service(); //按鍵服務(wù)的應(yīng)用程序 void key_scan(); //按鍵掃描函數(shù) 放在定時(shí)中斷里 sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵 sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵 sbit key_gnd_dr=P0^4; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平 sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口 unsigned char ucKeySec=0; //被觸發(fā)的按鍵編號 unsigned int uiKeyTimeCnt1=0; //按鍵去抖動延時(shí)計(jì)數(shù)器 unsigned char ucKeyLock1=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志 unsigned char ucKeyTouchCnt1=0; //按鍵按下的次數(shù)記錄 unsigned int uiKeyIntervalCnt1=0; //按鍵間隔的時(shí)間計(jì)數(shù)器 unsigned int uiKeyTimeCnt2=0; //按鍵去抖動延時(shí)計(jì)數(shù)器 unsigned char ucKeyLock2=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志 unsigned char ucKeyTouchCnt2=0; //按鍵按下的次數(shù)記錄 unsigned int uiKeyIntervalCnt2=0; //按鍵間隔的時(shí)間計(jì)數(shù)器 unsigned int uiVoiceCnt=0; //蜂鳴器鳴叫的持續(xù)時(shí)間計(jì)數(shù)器 void main() { initial_myself(); delay_long(100); initial_peripheral(); while(1) { key_service(); //按鍵服務(wù)的應(yīng)用程序 } } void key_scan()//按鍵掃描函數(shù) 放在定時(shí)中斷里 { /* 注釋三: * 獨(dú)立雙擊按鍵掃描的詳細(xì)過程: * 第一步:平時(shí)沒有按鍵被觸發(fā)時(shí),按鍵的自鎖標(biāo)志,去抖動延時(shí)計(jì)數(shù)器一直被清零。 * 如果之前已經(jīng)有按鍵觸發(fā)過一次,那么啟動時(shí)間間隔計(jì)數(shù)器uiKeyIntervalCnt1, * 在這個(gè)允許的時(shí)間差范圍內(nèi),如果一直沒有第二次按鍵觸發(fā),則把累加按鍵觸發(fā)的 * 次數(shù)ucKeyTouchCnt1也清零。 * 第二步:一旦有按鍵被按下,去抖動延時(shí)計(jì)數(shù)器開始在定時(shí)中斷函數(shù)里累加,在還沒累加到 * 閥值const_key_time1時(shí),如果在這期間由于受外界干擾或者按鍵抖動,而使 * IO口突然瞬間觸發(fā)成高電平,這個(gè)時(shí)候馬上把延時(shí)計(jì)數(shù)器uiKeyTimeCnt1 * 清零了,這個(gè)過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實(shí)戰(zhàn)中摸索出來的。 * 以后凡是用到開關(guān)感應(yīng)器的時(shí)候,都可以用類似這樣的方法去干擾。 * 第三步:如果按鍵按下的時(shí)間超過了閥值const_key_time1,馬上把自鎖標(biāo)志ucKeyLock1置位, * 防止按住按鍵不松手后一直觸發(fā)。與此同時(shí),累加一次按鍵次數(shù),如果按鍵次數(shù)累加有兩次以上, * 則認(rèn)為觸發(fā)雙擊按鍵,并把編號ucKeySec賦值。 * 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock1及時(shí)清零,為下一次自鎖做準(zhǔn)備。并且累加間隔時(shí)間, * 防止兩次按鍵的間隔時(shí)間太長。 * 第五步:以上整個(gè)過程,就是識別按鍵IO口下降沿觸發(fā)的過程。 */ if(key_sr1==1)//IO是高電平,說明按鍵沒有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位 { ucKeyLock1=0; //按鍵自鎖標(biāo)志清零 uiKeyTimeCnt1=0;//按鍵去抖動延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來的。 if(ucKeyTouchCnt1>0) //之前已經(jīng)有按鍵觸發(fā)過一次,再來一次就構(gòu)成雙擊 { uiKeyIntervalCnt1++; //按鍵間隔的時(shí)間計(jì)數(shù)器累加 if(uiKeyIntervalCnt1>const_interval_time1) //超過最大允許的間隔時(shí)間 { uiKeyIntervalCnt1=0; //時(shí)間計(jì)數(shù)器清零 ucKeyTouchCnt1=0; //清零按鍵的按下的次數(shù) } } } else if(ucKeyLock1==0)//有按鍵按下,且是第一次被按下 { uiKeyTimeCnt1++; //累加定時(shí)中斷次數(shù) if(uiKeyTimeCnt1>const_key_time1) { uiKeyTimeCnt1=0; ucKeyLock1=1; //自鎖按鍵置位,避免一直觸發(fā) uiKeyIntervalCnt1=0; //按鍵有效間隔的時(shí)間計(jì)數(shù)器清零 ucKeyTouchCnt1++; if(ucKeyTouchCnt1>1) //連續(xù)被按了兩次以上 { ucKeyTouchCnt1=0; //統(tǒng)計(jì)按鍵次數(shù)清零 ucKeySec=1; //觸發(fā)1號鍵 } } } if(key_sr2==1) { ucKeyLock2=0; uiKeyTimeCnt2=0; if(ucKeyTouchCnt2>0) { uiKeyIntervalCnt2++; //按鍵間隔的時(shí)間計(jì)數(shù)器累加 if(uiKeyIntervalCnt2>const_interval_time2) //超過最大允許的間隔時(shí)間 { uiKeyIntervalCnt2=0; //時(shí)間計(jì)數(shù)器清零 ucKeyTouchCnt2=0; //清零按鍵的按下的次數(shù) } } } else if(ucKeyLock2==0) { uiKeyTimeCnt2++; //累加定時(shí)中斷次數(shù) if(uiKeyTimeCnt2>const_key_time2) { uiKeyTimeCnt2=0; ucKeyLock2=1; uiKeyIntervalCnt2=0; //按鍵有效間隔的時(shí)間計(jì)數(shù)器清零 ucKeyTouchCnt2++; if(ucKeyTouchCnt2>1) //連續(xù)被按了兩次以上 { ucKeyTouchCnt2=0; //統(tǒng)計(jì)按鍵次數(shù)清零 ucKeySec=2; //觸發(fā)2號鍵 } } } } void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序 { switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換 { case 1:// 1號鍵 雙擊 對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。 ucKeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā) break; case 2:// 2號鍵 雙擊 對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。 ucKeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā) break; } } void T0_time() interrupt 1 { TF0=0; //清除中斷標(biāo)志 TR0=0; //關(guān)中斷 key_scan(); //按鍵掃描函數(shù) if(uiVoiceCnt!=0) { uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫 beep_dr=0; //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。 } else { ; //此處多加一個(gè)空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。 beep_dr=1; //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。 } TH0=0xf8; //重裝初始值(65535-2000)=63535=0xf82f TL0=0x2f; TR0=1; //開中斷 } void delay_long(unsigned int uiDelayLong) { unsigned int i; unsigned int j; for(i=0;i
總結(jié)陳詞:
假如要兩個(gè)獨(dú)立按鍵實(shí)現(xiàn)組合按鍵的功能,我們該怎么寫程序?欲知詳情,請聽下回分解-----獨(dú)立按鍵的組合按鍵觸發(fā)。
(未完待續(xù),下節(jié)更精彩,不要走開哦)
第十節(jié):兩個(gè)獨(dú)立按鍵的組合按鍵觸發(fā)。
開場白:
上一節(jié)講了按鍵雙擊觸發(fā)功能的程序,這一節(jié)講類似電腦鍵盤組合按鍵觸發(fā)的功能,要教會大家一個(gè)知識點(diǎn):如何在上一節(jié)的基礎(chǔ)上,略作修改,就可以實(shí)現(xiàn)兩個(gè)獨(dú)立按鍵的組合按鍵觸發(fā)功能。具體內(nèi)容,請看源代碼講解。
(1)硬件平臺:基于朱兆祺51單片機(jī)學(xué)習(xí)板。用矩陣鍵盤中的S1和S5號鍵作為獨(dú)立按鍵,記得把輸出線P0.4一直輸出低電平,模擬獨(dú)立按鍵的觸發(fā)地GND。
(2)實(shí)現(xiàn)功能:有兩個(gè)獨(dú)立按鍵,當(dāng)把兩個(gè)獨(dú)立按鍵都按下后,蜂鳴器發(fā)出“滴”的一聲后就停。直到松開任一個(gè)按鍵后,才能重新進(jìn)行下一次的組合按鍵觸發(fā)。
(3)源代碼講解如下:
#include "REG52.H" #define const_voice_short 40 //蜂鳴器短叫的持續(xù)時(shí)間 /* 注釋一: * 調(diào)整抖動時(shí)間閥值的大小,可以更改按鍵的觸發(fā)靈敏度。 * 去抖動的時(shí)間本質(zhì)上等于累計(jì)定時(shí)中斷次數(shù)的時(shí)間。 */ #define const_key_time12 20 //按鍵去抖動延時(shí)的時(shí)間 void initial_myself(); void initial_peripheral(); void delay_long(unsigned int uiDelaylong); void T0_time(); //定時(shí)中斷函數(shù) void key_service(); //按鍵服務(wù)的應(yīng)用程序 void key_scan(); //按鍵掃描函數(shù) 放在定時(shí)中斷里 sbit key_sr1=P0^0; //對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵 sbit key_sr2=P0^1; //對應(yīng)朱兆祺學(xué)習(xí)板的S5鍵 sbit key_gnd_dr=P0^4; //模擬獨(dú)立按鍵的地GND,因此必須一直輸出低電平 sbit beep_dr=P2^7; //蜂鳴器的驅(qū)動IO口 unsigned char ucKeySec=0; //被觸發(fā)的按鍵編號 unsigned int uiKeyTimeCnt12=0; //按鍵去抖動延時(shí)計(jì)數(shù)器 unsigned char ucKeyLock12=0; //按鍵觸發(fā)后自鎖的變量標(biāo)志 unsigned int uiVoiceCnt=0; //蜂鳴器鳴叫的持續(xù)時(shí)間計(jì)數(shù)器 void main() { initial_myself(); delay_long(100); initial_peripheral(); while(1) { key_service(); //按鍵服務(wù)的應(yīng)用程序 } } void key_scan()//按鍵掃描函數(shù) 放在定時(shí)中斷里 { /* 注釋二: * 獨(dú)立組合按鍵掃描的詳細(xì)過程: * 第一步:平時(shí)只要兩個(gè)按鍵中有一個(gè)沒有被按下時(shí),按鍵的自鎖標(biāo)志,去抖動延時(shí)計(jì)數(shù)器一直被清零。 * 第二步:一旦兩個(gè)按鍵都被按下,去抖動延時(shí)計(jì)數(shù)器開始在定時(shí)中斷函數(shù)里累加,在還沒累加到 * 閥值const_key_time12時(shí),如果在這期間由于受外界干擾或者按鍵抖動,而使 * IO口突然瞬間觸發(fā)成高電平,這個(gè)時(shí)候馬上把延時(shí)計(jì)數(shù)器uiKeyTimeCnt12 * 清零了,這個(gè)過程非常巧妙,非常有效地去除瞬間的雜波干擾。這是我實(shí)戰(zhàn)中摸索出來的。 * 以后凡是用到開關(guān)感應(yīng)器的時(shí)候,都可以用類似這樣的方法去干擾。 * 第三步:如果按鍵按下的時(shí)間超過了閥值const_key_time12,馬上把自鎖標(biāo)志ucKeyLock12置位, * 防止按住按鍵不松手后一直觸發(fā)。并把編號ucKeySec賦值。 組合按鍵觸發(fā) * 第四步:等按鍵松開后,自鎖標(biāo)志ucKeyLock12及時(shí)清零,為下一次自鎖做準(zhǔn)備。 * 第五步:以上整個(gè)過程,就是識別按鍵IO口下降沿觸發(fā)的過程。 */ if(key_sr1==1||key_sr2==1)//IO是高電平,說明兩個(gè)按鍵沒有全部被按下,這時(shí)要及時(shí)清零一些標(biāo)志位 { ucKeyLock12=0; //按鍵自鎖標(biāo)志清零 uiKeyTimeCnt12=0;//按鍵去抖動延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是我實(shí)戰(zhàn)中摸索出來的。 } else if(ucKeyLock12==0)//有按鍵按下,且是第一次被按下 { uiKeyTimeCnt12++; //累加定時(shí)中斷次數(shù) if(uiKeyTimeCnt12>const_key_time12) { uiKeyTimeCnt12=0; ucKeyLock12=1; //自鎖按鍵置位,避免一直觸發(fā) ucKeySec=1; //觸發(fā)1號鍵 } } } void key_service() //第三區(qū) 按鍵服務(wù)的應(yīng)用程序 { switch(ucKeySec) //按鍵服務(wù)狀態(tài)切換 { case 1:// 1號鍵 組合按鍵 對應(yīng)朱兆祺學(xué)習(xí)板的S1鍵和S5鍵 uiVoiceCnt=const_voice_short; //按鍵聲音觸發(fā),滴一聲就停。 ucKeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號清零,避免一致觸發(fā) break; } } void T0_time() interrupt 1 { TF0=0; //清除中斷標(biāo)志 TR0=0; //關(guān)中斷 key_scan(); //按鍵掃描函數(shù) if(uiVoiceCnt!=0) { uiVoiceCnt--; //每次進(jìn)入定時(shí)中斷都自減1,直到等于零為止。才停止鳴叫 beep_dr=0; //蜂鳴器是PNP三極管控制,低電平就開始鳴叫。 } else { ; //此處多加一個(gè)空指令,想維持跟if括號語句的數(shù)量對稱,都是兩條指令。不加也可以。 beep_dr=1; //蜂鳴器是PNP三極管控制,高電平就停止鳴叫。 } TH0=0xf8; //重裝初始值(65535-2000)=63535=0xf82f TL0=0x2f; TR0=1; //開中斷 } void delay_long(unsigned int uiDelayLong) { unsigned int i; unsigned int j; for(i=0;i
總結(jié)陳詞:
以前尋呼機(jī)流行的時(shí)候,尋呼機(jī)往往只有一個(gè)設(shè)置按鍵,它要求用一個(gè)按鍵來設(shè)置不同的參數(shù),這個(gè)時(shí)候就要用到同一個(gè)按鍵來實(shí)現(xiàn)短按和長按的區(qū)別觸發(fā)功能。要現(xiàn)實(shí)這種功能,我們該怎么寫程序?欲知詳情,請聽下回分解-----同一個(gè)按鍵短按與長按的區(qū)別觸發(fā)。
(未完待續(xù),下節(jié)更精彩,不要走開哦)