|
實現矩陣鍵盤掃描的三種方法(代碼見附件):
1.行列掃描法
2.反轉法
3.狀態機法(結合定時器中斷)
第一種和第二種方法的本質都是進行循環查詢,大量占用MCU的時間,效率比較低。初學者一般會學這兩種
第三種方法屬于狀態機方法,它是結合定時器中斷的,相比于前兩種采用延時函數的方法,大大提高了MCU的效率。
我們設計矩陣鍵盤的掃描函數時,要保證:既要及時的判斷按鍵是否被按下,又要讓MCU有時間去做其他的事。
代碼所對應的硬件電路:
0.png (47.32 KB, 下載次數: 149)
下載附件
2017-3-13 01:54 上傳
0.png (89.41 KB, 下載次數: 133)
下載附件
2017-3-13 01:54 上傳
完整的原理圖:
51start單片機開發板V3.0原理圖.pdf
(805.5 KB, 下載次數: 88)
2017-3-13 01:44 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
全部源碼下載:
矩陣鍵盤 反轉法.zip
(55.58 KB, 下載次數: 220)
2017-3-13 01:44 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
矩陣鍵盤 狀態機法.zip
(40.12 KB, 下載次數: 243)
2017-3-13 01:44 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
矩陣鍵盤(行列掃描法).zip
(33.98 KB, 下載次數: 182)
2017-3-13 01:44 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
意見整理:
這幾個方法其中包含的一些問題。(先說明一點,我只看了 “反轉法” 和 “狀態機” 法。)
“反轉法” 里面的松手檢測使用了 " while(...) " 這種方式(本人從前也是運用這種方式,所以現在就遇到問題了。所以在此建議大家在了解了基本的按鍵檢測方法后,要及時的學習更加高級和完善的方法,不要滿足現狀。), 這種松手檢測只能運用在極少數情況下才能使用,因為等待釋放的過程中 “CPU” 一直在 “等待”釋放,導致其余的任務都不被執行。假設一個系統中有動態數碼管顯示(不考慮定時中斷。這里說一點題外話,其實我們應該盡可能減少對外設的依賴。這樣做,一來能夠提高自己的編程能力;二來能提高系統穩定性,特別是中斷,開多了是件很 “危險” 的事情。),則數碼管會熄滅,又或者其他實時性較高任務無法被執行。所以這里提供的 “反轉法” 我認為只能提供一種思路,不大可能應用到實際中。
接著說一下 “狀態機法”(與其說是 “狀態機法” 倒不如說是“ 狀態機 + 線反轉 法” ) 。第一個問題我認為倒不是樓主所說的 “上電數碼管就亮” ,而是 “松手檢測” 沒有起作用。其實也只差一步,那就是判斷。源碼中不論經過任何 “狀態” 都會 “return” 最新的值,那么按下了就會被立即 “return” 。所以在 “return” 前加一個 “釋放” 判斷: “ if(key_state==key_state0) ” 便可。問題解決了么? 還真沒有,我們會在編譯時(在 keil 環境下) 獲得一條警告 “warning C291: not every exit path returns a value(大意是:某些情況下沒有返回值)” ,選擇 “無視” , 然后燒寫,運行(我開始也是這么做的)。好了,的確有了松手檢測,但是又出現 “反轉法” 中出現的情況——數碼管全滅。雖然無法判斷是 “卡死” ,還是 “return” 了個未知數,但可以確定的是,這顯然也是不符合我們的需要的。我們再回到當初的判斷那里,總結前面的問題,任何情況下我們都要 “return” 。靜下心來分析發現,只有兩種情況:一是沒有釋放, 那么就要“return” 上一次按鍵的值;二是已經釋放,則“return”這一次的值。于是我們就再引入一個變量 “last_key_value” ,用來存放 “上一次” 的按鍵值(這里我們要清楚,“上一次” 是一個相對的情況,因為 “這一次” 的按鍵值立刻就會變成 “上一次”)。 添加的代碼如下:
/***************CHANGE--START******************************/
if(key_state==key_state0) {
last_key_value=key_value;
return key_value;
}
else return last_key_value;
/***************CHANGE--END******************************/
說了一堆 “廢話” 終于把第一個問題解決了,接下來解決樓主所說的那個問題。其實這是一個識別的問題,也就是說要 “ 如何識別是首次上電呢? ”。
這里我們在 “ main.c ” 文件中引入一個標志位“ bit power_on=1; /* 標志位為 1 表示上電后未按下任何按鍵的狀態 */ ”
并在 “main” 函數中加入
“ if (power_on==0) Display(readkey); /* 上電后有按鍵被按下,才顯示對應數據*/ ”
到這里,我們又引入了另一個問題, “ power_on ” 需要在哪里被 “清零” ,你也許會說,很簡單啊,在 “ if(key_state==key_state0) {...} ” 清零啊。我們把 “ power_on = 0 ; /* 注意要聲明 extern bit power_on; */ ” 直接加入其中,然后編譯、燒寫、運行。接著發現,咦!怎么還亮。然后,憑借我的直覺又把它放到了 “ else ” ,再來一遍,然后你發現直覺真管用,不亮了。 趕緊按一個鍵試試,顯示沒錯,不過你會突然發現好像下去的時候顯示的是 “ 0 ” 松手以后,才顯示對應的數字。復位再來一遍,真的是這樣,怎么辦?認真分析代碼發現(這里就不廢話了,留給各位“ 廢 ”好了),你會發現,的確應該在 “ if(key_state==key_state0) {...} ” 中清零(第一感覺真的很準)。不過還要添加一個 “標志位” 可以是 “釋放標志位” 也可以是 “按下標志位”,為什么?就留給各位來想了。這里選擇 “釋放標志位”。修改的代碼如下:
/***************CHANGE--START******************************/
case key_state2:{ //狀態2,判斷是否被釋放
if(key_temp==no_key){ //釋放,轉回到狀態0
key_state=key_state0;
shifang=1;
}
}break;
......
if(key_state==key_state0) {
last_key_value=key_value;
if(bit_release) power_on=0; /* bit_release 也就是釋放標志位 */
return key_value;
}
/***************CHANGE--END******************************/
完成以后,編譯、燒寫、運行,OK!
矩陣鍵盤 狀態機法主程序:
- /*==========================================================
- * 開發人員:laowang
- * 當前版本:V1.0
- * 創建時間:11/26
- * 修改時間:
- * 功能說明:對4*4矩陣鍵盤掃描,用8位數碼管進行顯示,剛開始時不亮,依次按下按鍵時
- 數碼管依次顯示0-F,掃描方法為狀態機方法+定時器中斷
- *==========================================================*/
- #include<reg52.h>
- #include"Define.h"
- #include"display.h"
- #include"matrixkeyscan.h"
- void Timer0_init(); //定時器初始化函數
- uint flag=0; //按鍵掃描標志,每中斷一次,掃描一次
- //主函數
- void main()
- {
- uchar key_state=0;
- uchar readkey;
- readkey=0xff;
- Timer0_init();
- Display_init(); //使之不亮
- while(1)
- {
- if(flag)
- {
- flag=0;
- readkey=Keyscan();
- Display(readkey);
- }
- }
- }
- void Timer0() interrupt 1
- {
- TH0=0xd8; //10Ms產生一次中斷
- TL0=0xf0;
- flag=1;
- }
- void Timer0_init()
- {
- TMOD=0x01;
- TH0=0xd8; //12MHz 10Ms
- TL0=0xf0;
- EA=1;
- ET0=1;
- TR0=1;
- }
復制代碼
行列掃描法的主程序:
- /*==========================================================
- * 開發人員:laowang
- * 當前版本:V1.0
- * 創建時間:11/19
- * 修改時間:
- * 功能說明:讓數碼管的前兩位顯示顯示一個二位數(0-59),
- 通過三個按鍵控制,按下key1num加1,按下key2num減1,按下key3num清零
- *==========================================================*/
- /*==================硬件電路=============================================
- *
- *
- * MCU=89x51/52 8位數碼管(共陰)
- * +---------------+ +------+ +------------+------------+
- * | p1.0|-----| |---->a| | |
- * | . | | 573 | . |8. 8. 8. 8. |8. 8. 8. 8. |
- * | . | | | . | | |
- * | p1.7|-----| |--->dp| | |
- * +--key1--|P2.0 p3.4|-----| 段選 | +------------+------------+
- * | | | +------+ | |
- * |--key2--|P2.1 | | . . . . . . |
- * | | | +------+ | |
- * |--key3--|P2.2 p3.5|-----| | | |
- * | | p1.0|-----| 573 |-----------| |
- * GND | . | | | |
- * | . | | | |
- * | p1.7|-----| 位選 |--------------------------|
- * | | +------+
- * | |
- * | |
- * +---------------+
- *
- * 說明:此例程中只用到了數碼管的前兩位
- *=================================================================================*/
- #include<reg52.h>
- #include"display.h"
- #include"matrixkey.h"
- void main()
- {
- while(1)
- {
- Matrixkeyscan();
- Display(key);
- }
- }
復制代碼
完整的源碼請到本帖頂部下載附件.
|
|