日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當前位置:首頁 > > 充電吧
[導讀]新型的按鍵掃描程序不過入式處理器上面我在網上游逛了很久,也看過不少源程序了,沒有發(fā)現這種按鍵處理辦法的蹤跡,所以,我將他共享出來,和廣大同僚們共勉。我非常堅信這種按鍵處理辦法的便捷和高效,你可以移植到

新型的按鍵掃描程序

不過入式處理器上面我在網上游逛了很久,也看過不少源程序了,沒有發(fā)現這種按鍵處理辦法的蹤跡,所以,我將他共享出來,和廣大同僚們共勉。我非常堅信這種按鍵處理辦法的便捷和高效,你可以移植到任何一種嵌,因為C語言強大的可移植性。
同時,這里面用到了一些分層的思想,在單片機當中也是相當有用的,也是本文的另外一個重點。
對于老鳥,我建議直接看那兩個表達式,然后自己想想就會懂的了,也不需要聽我后面的自吹自擂了,我可沒有班門弄斧的意思,hoho~~但是對于新手,我建議將全文看完。因為這是實際項目中總結出來的經驗,學校里面學不到的東西。
以下假設你懂C語言,因為純粹的C語言描述,所以和處理器平臺無關,你可以在MCS-51,AVR,PIC,甚至是ARM平臺上面測試這個程序性能。當然,我自己也是在多個項目用過,效果非常好的。
好了,工程人員的習慣,廢話就應該少說,開始吧。以下我以AVR的MEGA8作為平臺講解,沒有其它原因,因為我手頭上只有AVR的板子而已沒有51的。用51也可以,只是芯片初始化部分不同,還有寄存器名字不同而已。
核心算法:

unsigned?char?Trg;
unsigned?char?Cont;
void?KeyRead(?void?)
{
????unsigned?char?ReadData?=?PINB^0xff;?//?1
????Trg?=?ReadData?&?(ReadData?^?Cont);?//?2
????Cont?=?ReadData;?//?3
}


完了。有沒有一種不可思議的感覺?當然,沒有想懂之前會那樣,想懂之后就會驚嘆于這算法的精妙?。?br />下面是程序解釋:
Trg(triger) 代表的是觸發(fā),Cont(continue)代表的是連續(xù)按下。
1:讀PORTB的端口數據,取反,然后送到ReadData 臨時變量里面保存起來。
2:算法1,用來計算觸發(fā)變量的。一個位與操作,一個異或操作,我想學過C語言都應該懂吧?Trg為全局變量,其它程序可以直接引用。
3:算法2,用來計算連續(xù)變量。
看到這里,有種“知其然,不知其所以然”的感覺吧?代碼很簡單,但是它到底是怎么樣實現我們的目的的呢?好,下面就讓我們繞開云霧看青天吧。
我們最常用的按鍵接法如下:AVR是有內部上拉功能的,但是為了說明問題,我是特意用外部上拉電阻。那么,按鍵沒有按下的時候,讀端口數據為1,如果按鍵按下,那么端口讀到0。下面就看看具體幾種情況之下,這算法是怎么一回事。
(1) 沒有按鍵的時候
端口為0xff,ReadData讀端口并且取反,很顯然,就是 0x00 了。
Trg = ReadData & (ReadData ^ Cont); (初始狀態(tài)下,Cont也是為0的)很簡單的數學計算,因為ReadData為0,則它和任何數“相與”,結果也是為0的。
Cont = ReadData; 保存Cont 其實就是等于ReadData,為0;
結果就是:
ReadData = 0;
Trg = 0;
Cont = 0;
(2) 第一次PB0按下的情況
端口數據為0xfe,ReadData讀端口并且取反,很顯然,就是 0x01 了。
Trg = ReadData & (ReadData ^ Cont); 因為這是第一次按下,所以Cont是上次的值,應為為0。那么這個式子的值也不難算,也就是 Trg = 0x01 & (0x01^0x00) = 0x01
Cont = ReadData = 0x01;
結果就是:
ReadData = 0x01;
Trg = 0x01;Trg只會在這個時候對應位的值為1,其它時候都為0
Cont = 0x01;
(3) PB0按著不松(長按鍵)的情況
端口數據為0xfe,ReadData讀端口并且取反是 0x01 了。
Trg = ReadData & (ReadData ^ Cont); 因為這是連續(xù)按下,所以Cont是上次的值,應為為0x01。那么這個式子就變成了 Trg = 0x01 & (0x01^0x01) = 0x00
Cont = ReadData = 0x01;
結果就是:
ReadData = 0x01;
Trg = 0x00;
Cont = 0x01;
因為現在按鍵是長按著,所以MCU會每個一定時間(20ms左右)不斷的執(zhí)行這個函數,那么下次執(zhí)行的時候情況會是怎么樣的呢?
ReadData = 0x01;這個不會變,因為按鍵沒有松開
Trg = ReadData & (ReadData ^ Cont) = 0x01 & (0x01 ^ 0x01) = 0 ,只要按鍵沒有松開,這個Trg值永遠為 0 ?。?!
Cont = 0x01;只要按鍵沒有松開,這個值永遠是0x01!!
(4) 按鍵松開的情況
端口數據為0xff,ReadData讀端口并且取反是 0x00 了。
Trg = ReadData & (ReadData ^ Cont) = 0x00 & (0x00^0x01) = 0x00
Cont = ReadData = 0x00;
結果就是:
ReadData = 0x00;
Trg = 0x00;
Cont = 0x00;
很顯然,這個回到了初始狀態(tài),也就是沒有按鍵按下的狀態(tài)。
總結一下,不知道想懂了沒有?其實很簡單,答案如下:
Trg 表示的就是觸發(fā)的意思,也就是跳變,只要有按鍵按下(電平從1到0的跳變),那么Trg在對應按鍵的位上面會置一,我們用了PB0則Trg的值為0x01,類似,如果我們PB7按下的話,Trg 的值就應該為 0x80 ,這個很好理解,還有,最關鍵的地方,Trg 的值每次按下只會出現一次,然后立刻被清除,完全不需要人工去干預。所以按鍵功能處理程序不會重復執(zhí)行,省下了一大堆的條件判斷,這個可是精粹哦??!Cont代表的是長按鍵,如果PB0按著不放,那么Cont的值就為 0x01,相對應,PB7按著不放,那么Cont的值應該為0x80,同樣很好理解。
如果還是想不懂的話,可以自己演算一下那兩個表達式,應該不難理解的。
因為有了這個支持,那么按鍵處理就變得很爽了,下面看應用:
應用一:一次觸發(fā)的按鍵處理
假設PB0為蜂鳴器按鍵,按一下,蜂鳴器beep的響一聲。這個很簡單,但是大家以前是怎么做的呢?對比一下看誰的方便?

#define?KEY_BEEP?0x01
void?KeyProc(void)
{
????if?(Trg?&?KEY_BEEP)?//?如果按下的是KEY_BEEP
????{
????????Beep();?//?執(zhí)行蜂鳴器處理函數
?????}
}


怎么樣?夠和諧不?記得前面解釋說Trg的精粹是什么?精粹就是只會出現一次。所以你按下按鍵的話,Trg & KEY_BEEP 為“真”的情況只會出現一次,所以處理起來非常的方便,蜂鳴器也不會沒事亂叫,hoho~~~
或者你會認為這個處理簡單,沒有問題,我們繼續(xù)。
應用2:長按鍵的處理
項目中經常會遇到一些要求,例如:一個按鍵如果短按一下執(zhí)行功能A,如果長按2秒不放的話會執(zhí)行功能B,又或者是要求3秒按著不放,計數連加什么什么的功能,很實際。不知道大家以前是怎么做的呢?我承認以前做的很郁悶。
但是看我們這里怎么處理吧,或許你會大吃一驚,原來程序可以這么簡單
這里具個簡單例子,為了只是說明原理,PB0是模式按鍵,短按則切換模式,PB1就是加,如果長按的話則連加(玩過電子表吧?沒錯,就是那個!)

#define?KEY_MODE?0x01?//?模式按鍵
#define?KEY_PLUS?0x02?//?加
void?KeyProc(void)
{
????if?(Trg?&?KEY_MODE)?//?如果按下的是KEY_MODE,而且你常按這按鍵也沒有用,
????{?//它是不會執(zhí)行第二次的哦?,?必須先松開再按下
	Mode++;?//?模式寄存器加1,當然,這里只是演示,你可以執(zhí)行你想
	//?執(zhí)行的任何代碼
????}

????if?(Cont?&?KEY_PLUS)?//?如果“加”按鍵被按著不放
????{
	cnt_plus++;?//?計時
		if?(cnt_plus?>?100)?//?20ms*100?=?2S?如果時間到
		{
		????Func();?//?你需要的執(zhí)行的程序
		}?
????}
}


不知道各位感覺如何?我覺得還是挺簡單的完成了任務,當然,作為演示用代碼。
應用3:點觸型按鍵和開關型按鍵的混合使用
點觸形按鍵估計用的最多,特別是單片機。開關型其實也很常見,例如家里的電燈,那些按下就不松開,除非關。這是兩種按鍵形式的處理原理也沒啥特別,但是你有沒有想過,如果一個系統(tǒng)里面這兩種按鍵是怎么處理的?我想起了我以前的處理,分開兩個非常類似的處理程序,現在看起來真的是笨的不行了,但是也沒有辦法啊,結構決定了程序。不過現在好了,用上面介紹的辦法,很輕松就可以搞定。
原理么?可能你也會想到,對于點觸開關,按照上面的辦法處理一次按下和長按,對于開關型,我們只需要處理Cont就OK了,為什么?很簡單嘛,把它當成是一個長按鍵,這樣就找到了共同點,屏蔽了所有的細節(jié)。程序就不給了,完全就是應用2的內容,在這里提為了就是說明原理~~
好了,這個好用的按鍵處理算是說完了??赡軙信笥褧?,為什么不說延時消抖問題?哈哈,被看穿了。果然不能偷懶。下面談談這個問題,順便也就非常簡單的談談我自己用時間片輪辦法,以及是如何消抖的。
延時消抖的辦法是非常傳統(tǒng),也就是 第一次判斷有按鍵,延時一定的時間(一般習慣是20ms)再讀端口,如果兩次讀到的數據一樣,說明了是真正的按鍵,而不是抖動,則進入按鍵處理程序。
當然,不要跟我說你delay(20)那樣去死循環(huán)去,真是那樣的話,我衷心的建議你先放下手上所有的東西,好好的去了解一下操作系統(tǒng)的分時工作原理,大概知道思想就可以,不需要詳細看原理,否則你永遠逃不出“菜鳥”這個圈子。當然我也是菜鳥。我的意思是,真正的單片機入門,是從學會處理多任務開始的,這個也是學校程序跟公司程序的最大差別。當然,本文不是專門說這個的,所以也不獻丑了。
我的主程序架構是這樣的:

volatile?unsigned?char?Intrcnt;
void?InterruptHandle()?//?中斷服務程序
{
????Intrcnt++;?//?1ms?中斷1次,可變
}
void?main(void)
{
????SysInit();
????while(1)?//?每20ms?執(zhí)行一次大循環(huán)
????{
	KeyRead();?//?將每個子程序都掃描一遍
	KeyProc();
	Func1();
	Funt2();
	…
	…
	while(1)
	{
		if?(Intrcnt>20)?//?一直在等,直到20ms時間到
		{
		????Intrcnt="0";
		????break;?//?返回主循環(huán)
		}
	}
????}
}


貌似扯遠了,回到我們剛才的問題,也就是怎么做按鍵消抖處理。我們將讀按鍵的程序放在了主循環(huán),也就是說,每20ms我們會執(zhí)行一次KeyRead()函數來得到新的Trg 和 Cont 值。好了,下面是我的消抖部分:很簡單
基本架構如上,我自己比較喜歡的,一直在用。當然,和這個配合,每個子程序必須執(zhí)行時間不長,更加不能死循環(huán),一般采用有限狀態(tài)機的辦法來實現,具體參考其它資料咯。
懂得基本原理之后,至于怎么用就大家慢慢思考了,我想也難不到聰明的工程師們。例如還有一些處理,
怎么判斷按鍵釋放?很簡單,Trg 和Cont都為0 則肯定已經釋放了。

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

鏈表作為一種基礎的數據結構,在程序設計中扮演著重要角色。掌握鏈表的高效操作技巧,特別是逆序、合并和循環(huán)檢測,對于提升算法性能和解決復雜問題至關重要。本文將詳細介紹這些操作的C語言實現,并分析其時間復雜度。

關鍵字: 鏈表 C語言

在C/C++多文件編程中,靜態(tài)變量(static)與全局變量的作用域規(guī)則看似簡單,實則暗藏諸多陷阱。開發(fā)者若未能準確理解其鏈接屬性與生命周期,極易引發(fā)難以調試的內存錯誤、競態(tài)條件以及維護災難。本文將深入剖析這兩類變量的作...

關鍵字: 靜態(tài)變量 全局變量 C語言

在嵌入式系統(tǒng)和服務器開發(fā)中,日志系統(tǒng)是故障排查和運行監(jiān)控的核心組件。本文基于Linux環(huán)境實現一個輕量級C語言日志庫,支持DEBUG/INFO/WARN/ERROR四級日志分級,并實現按大小滾動的文件輪轉機制。該設計在某...

關鍵字: C語言 嵌入式系統(tǒng)

在嵌入式系統(tǒng)和底層驅動開發(fā)中,C語言因其高效性和可控性成為主流選擇,但缺乏原生單元測試支持成為開發(fā)痛點。本文提出一種基于宏定義和測試用例管理的輕量級單元測試框架方案,通過自定義斷言宏和測試注冊機制,實現無需外部依賴的嵌入...

關鍵字: C語言 嵌入式系統(tǒng) 驅動開發(fā)

在嵌入式系統(tǒng)開發(fā)中,實時操作系統(tǒng)(RTOS)的任務調度算法直接影響系統(tǒng)的響應速度和資源利用率。時間片輪轉(Round-Robin, RR)作為一種經典的公平調度算法,通過為每個任務分配固定時間片實現多任務并發(fā)執(zhí)行。本文將...

關鍵字: 實時操作系統(tǒng) RTOS C語言

在Linux設備驅動開發(fā)中,等待隊列(Wait Queue)是實現進程睡眠與喚醒的核心機制,它允許進程在資源不可用時主動放棄CPU,進入可中斷睡眠狀態(tài),待資源就緒后再被喚醒。本文通過C語言模型解析等待隊列的實現原理,結合...

關鍵字: 驅動開發(fā) C語言 Linux

在嵌入式系統(tǒng)開發(fā)中,C語言與匯編的混合編程是優(yōu)化性能、訪問特殊指令或硬件寄存器的關鍵技術。然而,內聯(lián)匯編的語法差異和寄存器使用規(guī)則常導致難以調試的問題。本文以ARM Cortex-M和x86架構為例,系統(tǒng)梳理內聯(lián)匯編的核...

關鍵字: C語言 匯編混合編程

在計算機安全領域,緩沖區(qū)溢出攻擊長期占據漏洞利用榜首。這種攻擊通過向程序緩沖區(qū)寫入超出其容量的數據,覆蓋相鄰內存區(qū)域(如返回地址),進而實現任意代碼執(zhí)行。本文將深入探討棧保護機制與安全函數(如snprintf)的集成防御...

關鍵字: 棧保護 安全函數 C語言

在嵌入式系統(tǒng)和大規(guī)模數值計算等性能敏感場景中,程序優(yōu)化是提升效率的關鍵環(huán)節(jié)。gprof作為GNU工具鏈中的性能分析工具,能夠精準定位CPU時間消耗熱點。本文通過實際案例演示gprof的三個核心使用步驟,幫助開發(fā)者快速識別...

關鍵字: C語言 gprof 熱點函數

哈希表作為高效數據檢索的核心結構,其性能高度依賴沖突解決策略。本文通過C語言實現對比鏈地址法與開放尋址法,揭示兩種方法在內存占用、查詢效率及實現復雜度上的差異,為工程實踐提供量化參考。

關鍵字: 哈希表 鏈地址法 開放尋址法 C語言
關閉