當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式云IOT技術(shù)圈
[導(dǎo)讀]鴻哥是我的一位單片機(jī)學(xué)習(xí)的啟蒙老師,在此分享來自于他的一篇文章。 【92.1? ?獨(dú)立按鍵的硬件電路簡介?!?? ?? ? ?? ?? ?? ?? ? 上圖92.1.1??獨(dú)立按鍵電路 ????按鍵有兩種驅(qū)動方式,一種是獨(dú)立按鍵,一種是矩陣按鍵。1個(gè)獨(dú)立按鍵要占用1個(gè)IO口,IO口不能共


鴻哥是我的一位單片機(jī)學(xué)習(xí)的啟蒙老師,在此分享來自于他的一篇文章。

【92.1   獨(dú)立按鍵的硬件電路簡介。】


                上圖92.1.1  獨(dú)立按鍵電路

    按鍵有兩種驅(qū)動方式,一種是獨(dú)立按鍵,一種是矩陣按鍵。1個(gè)獨(dú)立按鍵要占用1個(gè)IO口,IO口不能共用。而矩陣按鍵的IO口是分時(shí)片選復(fù)用的,用少量的IO口就可以驅(qū)動翻倍級別的按鍵數(shù)量。比如,用8個(gè)IO口只能驅(qū)動8個(gè)獨(dú)立按鍵,但是卻可以驅(qū)動16個(gè)矩陣按鍵(4x4)。因此,按鍵少的時(shí)候就用獨(dú)立按鍵,按鍵多的時(shí)候就用矩陣按鍵。這兩種按鍵的驅(qū)動本質(zhì)是一樣的,都是靠識別輸入信號的下降沿(或上升沿)來識別按鍵的觸發(fā)。
     獨(dú)立按鍵的硬件原理基礎(chǔ),如上圖,P2.2這個(gè)IO口,在按鍵K1沒有被按下的時(shí)候,P2.2口因?yàn)閱纹瑱C(jī)內(nèi)部自帶上拉電阻把電平拉高,此時(shí)P2.2口是高電平的輸入狀態(tài)。當(dāng)按鍵K1被按下的時(shí)候,按鍵K1左右像一根導(dǎo)線連接到電源的負(fù)極(GND),直接把原來P2.2口的電平拉低,此時(shí)P2.2口變成了低電平的輸入狀態(tài)。編寫按鍵驅(qū)動程序,就是要識別這個(gè)電平從高到低的過程,這個(gè)過程也叫下降沿。多說一句,51單片機(jī)的P1,P2,P3口是內(nèi)部自帶上拉電阻的,而P0口是內(nèi)部沒有上拉電阻的,需要外接上拉電阻。除此之外,很多單片機(jī)內(nèi)部其實(shí)都沒有上拉電阻的,因此,建議大家在做獨(dú)立按鍵電路的時(shí)候,養(yǎng)成一個(gè)習(xí)慣,凡是按鍵輸入狀態(tài)都外接上拉電阻。
     識別按鍵的下降沿觸發(fā)有四大要素:自鎖,消抖,非阻塞,清零式濾波。
“自鎖”,按鍵一旦進(jìn)入到低電平,就要“自鎖”起來,避免不斷觸發(fā)按鍵,只有當(dāng)按鍵被松開變成高電平的時(shí)候,才及時(shí)“解鎖”為下一次觸發(fā)做準(zhǔn)備。
“消抖”,按鍵是一個(gè)機(jī)械觸點(diǎn)器件,在接觸的瞬間必然存在微觀上的機(jī)械抖動,反饋到電平的瞬間就是“高,低,高,低...”這種不穩(wěn)定的電平狀態(tài)是一種干擾,但是,按鍵一旦按下去穩(wěn)定了之后,這種狀態(tài)就消失,電平就一直保持穩(wěn)定的低電平。消抖的本質(zhì)就是濾波,要把這種接觸的瞬間抖動過濾掉,避免按鍵的“一按多觸發(fā)”。
“非阻塞”,在處理消抖的時(shí)候,必須用到延時(shí),如果此時(shí)用阻塞的delay延時(shí)就會影響其它任務(wù)的運(yùn)行效率,因此,用非阻塞的定時(shí)延時(shí)更加有優(yōu)越性。
“清零式濾波”,在消抖的時(shí)候,有兩種境界,第一種境界是判斷兩次電平的狀態(tài),中間插入“固定的時(shí)間”延時(shí),這種方法前后一共判斷了兩次,第一次是識別到低電平就進(jìn)入延時(shí)的狀態(tài),第二次是延時(shí)后再確認(rèn)一次是否繼續(xù)是低電平的狀態(tài),這種方法的不足是,“固定的時(shí)間”全憑經(jīng)驗(yàn)值,但是不同的按鍵它們的抖動時(shí)間長度是不同的,除此之外,前后才判斷了兩次,在軟件的抗干擾能力上也弱了很多,“密碼等級”不夠高。第二種境界就是“清零式濾波”,“清零式濾波”非常巧妙,抗擾能力超強(qiáng),它能自動過濾不同按鍵的“抖動時(shí)間”,然后再進(jìn)入一個(gè)“穩(wěn)定時(shí)間”的“N次識別判斷”,更加巧妙的是,在“抖動時(shí)間”和“穩(wěn)定時(shí)間”兩者時(shí)間內(nèi),只要發(fā)現(xiàn)一次是高電平的干擾,就馬上自動清零計(jì)時(shí)器,重新開始計(jì)時(shí)。“穩(wěn)定時(shí)間”一般取20ms到30ms之間,而“抖動時(shí)間”是隱藏的,在代碼上并沒有直接描寫出來,但是卻無形地融入了代碼之中,只有慢慢體會才能發(fā)現(xiàn)它的存在。
     具體的代碼如下,實(shí)現(xiàn)的功能是按一次K1或者K2按鍵,就觸發(fā)一次蜂鳴器鳴叫。

 1#include "REG52.H"   2  3#define KEY_VOICE_TIME   50 //按鍵觸發(fā)后發(fā)出的聲音長度   4#define KEY_FILTER_TIME  25 //按鍵濾波的“穩(wěn)定時(shí)間”25ms  5  6void T0_time();  7void SystemInitial(void) ;  8void Delay(unsigned long u32DelayTime) ;  9void PeripheralInitial(void) ;  10  11void BeepOpen(void);  12void BeepClose(void);  13void VoiceScan(void);  14void KeyScan(void); //按鍵識別的驅(qū)動函數(shù),放在定時(shí)中斷里  15void KeyTask(void); //按鍵任務(wù)函數(shù),放在主函數(shù)內(nèi)  16  17sbit P3_4=P3^4;  18sbit KEY_INPUT1=P2^2; //K1按鍵識別的輸入口。  19sbit KEY_INPUT2=P2^1; //K2按鍵識別的輸入口。  20  21volatile unsigned char vGu8BeepTimerFlag=0;  22volatile unsigned int vGu16BeepTimerCnt=0;  23  24volatile unsigned char vGu8KeySec=0; //按鍵的觸發(fā)序號,全局變量意味著是其它函數(shù)的接口。  25  26void main()  27{  28SystemInitial();  29Delay(10000);  30PeripheralInitial();  31 while(1)  32{  33 KeyTask(); //按鍵任務(wù)函數(shù)  34 }  35}  36  37void T0_time() interrupt 1  38{  39VoiceScan();  40KeyScan(); //按鍵識別的驅(qū)動函數(shù)  41  42TH0=0xfc;  43TL0=0x66;  44}  45  46  47void SystemInitial(void)  48{  49TMOD=0x01;  50TH0=0xfc;  51TL0=0x66;  52EA=1;  53ET0=1;  54TR0=1;  55}  56  57void Delay(unsigned long u32DelayTime)  58{  59 for(;u32DelayTime>0;u32DelayTime--);  60}  61  62void PeripheralInitial(void)  63{  64  65}  66  67void BeepOpen(void)  68{  69P3_4=0;  70}  71  72void BeepClose(void)  73{  74P3_4=1;  75}  76  77void VoiceScan(void)  78{  79  80 static unsigned char Su8Lock=0;  81  82if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0)  83 {  84 if(0==Su8Lock)  85 {  86 Su8Lock=1;  87BeepOpen();  88 }  89 else  90{  91  92 vGu16BeepTimerCnt--;  93  94 if(0==vGu16BeepTimerCnt)  95 {  96 Su8Lock=0;  97BeepClose();  98 }  99 100} 101 } 102} 103 104/* 注釋一: 105* 獨(dú)立按鍵掃描的詳細(xì)過程,以按鍵K1為例,如下: 106* 第一步:平時(shí)沒有按鍵被觸發(fā)時(shí),按鍵的自鎖標(biāo)志,去抖動延時(shí)計(jì)數(shù)器一直被清零。 107* 第二步:一旦有按鍵被按下,去抖動延時(shí)計(jì)數(shù)器開始在定時(shí)中斷函數(shù)里累加,在還沒累加到 108*         閥值KEY_FILTER_TIME時(shí),如果在這期間由于受外界干擾或者按鍵抖動,而使 109*         IO口突然瞬間觸發(fā)成高電平,這個(gè)時(shí)候馬上把延時(shí)計(jì)數(shù)器Su16KeyCnt1清零了,這個(gè)過程 110*         非常巧妙,非常有效地去除瞬間的雜波干擾。以后凡是用到開關(guān)感應(yīng)器的時(shí)候, 111*         都可以用類似這樣的方法去干擾。 112* 第三步:如果按鍵按下的時(shí)間達(dá)到閥值KEY_FILTER_TIME時(shí),則觸發(fā)按鍵,把編號vGu8KeySec賦值。 113*         同時(shí),馬上把自鎖標(biāo)志Su8KeyLock1置1,防止按住按鍵不松手后一直觸發(fā)。 114* 第四步:等按鍵松開后,自鎖標(biāo)志Su8KeyLock1及時(shí)清零(解鎖),為下一次自鎖做準(zhǔn)備。 115* 第五步:以上整個(gè)過程,就是識別按鍵IO口下降沿觸發(fā)的過程。 116*/ 117void KeyScan(void) //此函數(shù)放在定時(shí)中斷里每1ms掃描一次 118{ 119 static unsigned char Su8KeyLock1; //1號按鍵的自鎖 120 static unsigned int Su16KeyCnt1; //1號按鍵的計(jì)時(shí)器 121 static unsigned char Su8KeyLock2; //2號按鍵的自鎖 122 static unsigned int Su16KeyCnt2; //2號按鍵的計(jì)時(shí)器 123 124 //1號按鍵 125 if(0!=KEY_INPUT1)//IO是高電平,說明按鍵沒有被按下,這時(shí)要及時(shí)清零一些標(biāo)志位 126 { 127 Su8KeyLock1=0; //按鍵解鎖 128 Su16KeyCnt1=0; //按鍵去抖動延時(shí)計(jì)數(shù)器清零,此行非常巧妙,是全場的亮點(diǎn)。  129 } 130 else if(0==Su8KeyLock1)//有按鍵按下,且是第一次被按下。這行很多初學(xué)者有疑問,請看專題分析。 131 { 132 Su16KeyCnt1++; //累加定時(shí)中斷次數(shù) 133 if(Su16KeyCnt1>=KEY_FILTER_TIME) //濾波的“穩(wěn)定時(shí)間”KEY_FILTER_TIME,長度是25ms。 134 { 135 Su8KeyLock1=1; //按鍵的自鎖,避免一直觸發(fā) 136 vGu8KeySec=1; //觸發(fā)1號鍵 137 } 138 } 139 140 //2號按鍵 141 if(0!=KEY_INPUT2) 142 { 143 Su8KeyLock2=0; 144 Su16KeyCnt2=0; 145 } 146 else if(0==Su8KeyLock2) 147 { 148 Su16KeyCnt2++; 149 if(Su16KeyCnt2>=KEY_FILTER_TIME) 150 { 151 Su8KeyLock2=1; 152 vGu8KeySec=2; //觸發(fā)2號鍵 153 } 154 } 155 156 157} 158 159void KeyTask(void) //按鍵任務(wù)函數(shù),放在主函數(shù)內(nèi) 160{ 161if(0==vGu8KeySec) 162{ 163return; //按鍵的觸發(fā)序號是0意味著無按鍵觸發(fā),直接退出當(dāng)前函數(shù),不執(zhí)行此函數(shù)下面的代碼 164} 165 166switch(vGu8KeySec) //根據(jù)不同的按鍵觸發(fā)序號執(zhí)行對應(yīng)的代碼 167{ 168 case 1: //1號按鍵 169 170 vGu8BeepTimerFlag=0; 171vGu16BeepTimerCnt=KEY_VOICE_TIME; //觸發(fā)按鍵后,發(fā)出固定長度的聲音 172 vGu8BeepTimerFlag=1; 173vGu8KeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號必須清零,避免一致觸發(fā) 174break; 175 176 case 2: //2號按鍵 177 178 vGu8BeepTimerFlag=0; 179vGu16BeepTimerCnt=KEY_VOICE_TIME; //觸發(fā)按鍵后,發(fā)出固定長度的聲音 180 vGu8BeepTimerFlag=1; 181vGu8KeySec=0; //響應(yīng)按鍵服務(wù)處理程序后,按鍵編號必須清零,避免一致觸發(fā) 182break; 183 184} 185}

【92.2   專題分析:else if(0==Su8KeyLock1)。】

疑問:

 1if(0!=KEY_INPUT1)  2 {  3 Su8KeyLock1=0;  4 Su16KeyCnt1=0;  5 }  6 else if(0==Su8KeyLock1)//有按鍵按下,且是第一次被按下。為什么?為什么?為什么?  7 {  8 Su16KeyCnt1++;  9 if(Su16KeyCnt1>KEY_FILTER_TIME) 10 { 11 Su8KeyLock1=1; 12 vGu8KeySec=1; 13 } 14 }

 解答:
       首先,我們要明白C語言的語法中,

1if(條件1) 2{ 3 4} 5else if(條件2) 6{ 7 8}

以上語句是一對組合語句,不能分開來看。當(dāng)(條件1)成立的時(shí)候,它是絕對不會判斷(條件2)的。當(dāng)(條件1)不成立的時(shí)候,才會判斷(條件2)。
       回到剛才的問題,當(dāng)程序執(zhí)行到(條件2) else if(0==Su8KeyLock1)的時(shí)候,就已經(jīng)默認(rèn)了(條件1) if(0!=KEY_INPUT1)不成立,這個(gè)條件不成立,就意味著0==KEY_INPUT1,也就是有按鍵被按下,因此,這里的else if(0==Su8KeyLock1)等效于else if(0==Su8KeyLock1&&0==KEY_INPUT1),而Su8KeyLock1是一個(gè)自鎖標(biāo)志位,一旦按鍵被觸發(fā)后,這個(gè)標(biāo)志位會變1,防止按鍵按住不松手的時(shí)候不斷觸發(fā)按鍵。這樣,按鍵只能按一次觸發(fā)一次,松開手后再按一次,又觸發(fā)一次。
【92.3   專題分析:if(0!=KEY_INPUT1)?!?/span>
       疑問:為什么不用if(1==KEY_INPUT1)而用if(0!=KEY_INPUT1)?
       解答:其實(shí)兩者在功能上是完全等效的,在這里都可以用。之所以本教程優(yōu)先選用后者if(0!=KEY_INPUT1),是因?yàn)榭紤]到了代碼在不同單片機(jī)平臺上的可移植性和兼容性。很多32位的單片機(jī)提供的是庫函數(shù),庫函數(shù)返回的按鍵狀態(tài)是一個(gè)字節(jié)變量來表示,當(dāng)被按下的時(shí)候是0,但是,當(dāng)沒有按下的時(shí)候并不一定等于1,而是一個(gè)“非0”的數(shù)值。
【92.4   專題分析:把KeyScan函數(shù)放在定時(shí)器中斷里?!?/span>
       疑問:為什么把KeyScan函數(shù)放在定時(shí)器中斷里?
       解答:中斷函數(shù)里放的函數(shù)或者代碼越少越好,但是KeyScan函數(shù)是特殊的函數(shù),是涉及到IO口輸入信號的濾波,濾波就涉及到時(shí)間的及時(shí)性與均勻性,放在定時(shí)中斷函數(shù)里更加能保證時(shí)間的一致性。比如,蜂鳴器驅(qū)動,動態(tài)數(shù)碼管驅(qū)動,按鍵掃描驅(qū)動,我個(gè)人都習(xí)慣放在定時(shí)中斷函數(shù)里。
【92.5   專題分析:if(0==vGu8KeySec)return?!?/span>
       疑問:if(0==vGu8KeySec)return是不是多此一舉?
       解答:在KeyTask函數(shù)這里,if(0==vGu8KeySec)return這行代碼刪掉,對程序功能是沒有影響的,這里之所以多插入這行判斷語句,是因?yàn)?,?dāng)按鍵多達(dá)幾十個(gè)的時(shí)候,避免主函數(shù)每次進(jìn)入KeyTask函數(shù),都挨個(gè)掃描判斷switch的狀態(tài)進(jìn)行多次判斷,如果增加了這行if(0==vGu8KeySec)return代碼,就可以直接退出省事,在理論上感覺更加運(yùn)行高效。其實(shí),不同單片機(jī)不同的C編譯器可能對switch語句的翻譯不一樣,因此,這里的是不是更加高效我不敢保證。但是可以保證的是,加了這行代碼也沒有其它副作用。


長期商務(wù)合作服務(wù):




免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉