標(biāo)題:
行列掃描法,反轉(zhuǎn)法,狀態(tài)機(jī)法三種矩陣鍵盤掃描方法詳解 帶程序
[打印本頁(yè)]
作者:
51hei學(xué)習(xí)技術(shù)中
時(shí)間:
2017-3-13 01:50
標(biāo)題:
行列掃描法,反轉(zhuǎn)法,狀態(tài)機(jī)法三種矩陣鍵盤掃描方法詳解 帶程序
實(shí)現(xiàn)矩陣鍵盤掃描的三種方法(代碼見附件):
1.行列掃描法
2.反轉(zhuǎn)法
3.狀態(tài)機(jī)法(結(jié)合定時(shí)器中斷)
第一種和第二種方法的本質(zhì)都是進(jìn)行循環(huán)查詢,大量占用MCU的時(shí)間,效率比較低。初學(xué)者一般會(huì)學(xué)這兩種
第三種方法屬于狀態(tài)機(jī)方法,它是結(jié)合定時(shí)器中斷的,相比于前兩種采用延時(shí)函數(shù)的方法,大大提高了MCU的效率。
我們?cè)O(shè)計(jì)矩陣鍵盤的掃描函數(shù)時(shí),要保證:既要及時(shí)的判斷按鍵是否被按下,又要讓MCU有時(shí)間去做其他的事。
代碼所對(duì)應(yīng)的硬件電路:
0.png
(47.32 KB, 下載次數(shù): 149)
下載附件
2017-3-13 01:54 上傳
0.png
(89.41 KB, 下載次數(shù): 133)
下載附件
2017-3-13 01:54 上傳
完整的原理圖:
51start單片機(jī)開發(fā)板V3.0原理圖.pdf
(805.5 KB, 下載次數(shù): 88)
2017-3-13 01:44 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
全部源碼下載:
矩陣鍵盤 反轉(zhuǎn)法.zip
(55.58 KB, 下載次數(shù): 220)
2017-3-13 01:44 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
矩陣鍵盤 狀態(tài)機(jī)法.zip
(40.12 KB, 下載次數(shù): 243)
2017-3-13 01:44 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
矩陣鍵盤(行列掃描法).zip
(33.98 KB, 下載次數(shù): 182)
2017-3-13 01:44 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
意見整理:
這幾個(gè)方法其中包含的一些問題。(先說(shuō)明一點(diǎn),我只看了 “反轉(zhuǎn)法” 和 “狀態(tài)機(jī)” 法。)
“反轉(zhuǎn)法” 里面的松手檢測(cè)使用了 " while(...) " 這種方式(本人從前也是運(yùn)用這種方式,所以現(xiàn)在就遇到問題了。所以在此建議大家在了解了基本的按鍵檢測(cè)方法后,要及時(shí)的學(xué)習(xí)更加高級(jí)和完善的方法,不要滿足現(xiàn)狀。), 這種松手檢測(cè)只能運(yùn)用在極少數(shù)情況下才能使用,因?yàn)榈却尫诺倪^(guò)程中 “CPU” 一直在 “等待”釋放,導(dǎo)致其余的任務(wù)都不被執(zhí)行。假設(shè)一個(gè)系統(tǒng)中有動(dòng)態(tài)數(shù)碼管顯示(不考慮定時(shí)中斷。這里說(shuō)一點(diǎn)題外話,其實(shí)我們應(yīng)該盡可能減少對(duì)外設(shè)的依賴。這樣做,一來(lái)能夠提高自己的編程能力;二來(lái)能提高系統(tǒng)穩(wěn)定性,特別是中斷,開多了是件很 “危險(xiǎn)” 的事情。),則數(shù)碼管會(huì)熄滅,又或者其他實(shí)時(shí)性較高任務(wù)無(wú)法被執(zhí)行。所以這里提供的 “反轉(zhuǎn)法” 我認(rèn)為只能提供一種思路,不大可能應(yīng)用到實(shí)際中。
接著說(shuō)一下 “狀態(tài)機(jī)法”(與其說(shuō)是 “狀態(tài)機(jī)法” 倒不如說(shuō)是“ 狀態(tài)機(jī) + 線反轉(zhuǎn) 法” ) 。第一個(gè)問題我認(rèn)為倒不是樓主所說(shuō)的 “上電數(shù)碼管就亮” ,而是 “松手檢測(cè)” 沒有起作用。其實(shí)也只差一步,那就是判斷。源碼中不論經(jīng)過(guò)任何 “狀態(tài)” 都會(huì) “return” 最新的值,那么按下了就會(huì)被立即 “return” 。所以在 “return” 前加一個(gè) “釋放” 判斷: “ if(key_state==key_state0) ” 便可。問題解決了么? 還真沒有,我們會(huì)在編譯時(shí)(在 keil 環(huán)境下) 獲得一條警告 “warning C291: not every exit path returns a value(大意是:某些情況下沒有返回值)” ,選擇 “無(wú)視” , 然后燒寫,運(yùn)行(我開始也是這么做的)。好了,的確有了松手檢測(cè),但是又出現(xiàn) “反轉(zhuǎn)法” 中出現(xiàn)的情況——數(shù)碼管全滅。雖然無(wú)法判斷是 “卡死” ,還是 “return” 了個(gè)未知數(shù),但可以確定的是,這顯然也是不符合我們的需要的。我們?cè)倩氐疆?dāng)初的判斷那里,總結(jié)前面的問題,任何情況下我們都要 “return” 。靜下心來(lái)分析發(fā)現(xiàn),只有兩種情況:一是沒有釋放, 那么就要“return” 上一次按鍵的值;二是已經(jīng)釋放,則“return”這一次的值。于是我們就再引入一個(gè)變量 “l(fā)ast_key_value” ,用來(lái)存放 “上一次” 的按鍵值(這里我們要清楚,“上一次” 是一個(gè)相對(duì)的情況,因?yàn)?“這一次” 的按鍵值立刻就會(huì)變成 “上一次”)。 添加的代碼如下:
/***************CHANGE--START******************************/
if(key_state==key_state0) {
last_key_value=key_value;
return key_value;
}
else return last_key_value;
/***************CHANGE--END******************************/
說(shuō)了一堆 “廢話” 終于把第一個(gè)問題解決了,接下來(lái)解決樓主所說(shuō)的那個(gè)問題。其實(shí)這是一個(gè)識(shí)別的問題,也就是說(shuō)要 “ 如何識(shí)別是首次上電呢? ”。
這里我們?cè)?“ main.c ” 文件中引入一個(gè)標(biāo)志位“ bit power_on=1; /* 標(biāo)志位為 1 表示上電后未按下任何按鍵的狀態(tài) */ ”
并在 “main” 函數(shù)中加入
“ if (power_on==0) Display(readkey); /* 上電后有按鍵被按下,才顯示對(duì)應(yīng)數(shù)據(jù)*/ ”
到這里,我們又引入了另一個(gè)問題, “ power_on ” 需要在哪里被 “清零” ,你也許會(huì)說(shuō),很簡(jiǎn)單啊,在 “ if(key_state==key_state0) {...} ” 清零啊。我們把 “ power_on = 0 ; /* 注意要聲明 extern bit power_on; */ ” 直接加入其中,然后編譯、燒寫、運(yùn)行。接著發(fā)現(xiàn),咦!怎么還亮。然后,憑借我的直覺又把它放到了 “ else ” ,再來(lái)一遍,然后你發(fā)現(xiàn)直覺真管用,不亮了。 趕緊按一個(gè)鍵試試,顯示沒錯(cuò),不過(guò)你會(huì)突然發(fā)現(xiàn)好像下去的時(shí)候顯示的是 “ 0 ” 松手以后,才顯示對(duì)應(yīng)的數(shù)字。復(fù)位再來(lái)一遍,真的是這樣,怎么辦?認(rèn)真分析代碼發(fā)現(xiàn)(這里就不廢話了,留給各位“ 廢 ”好了),你會(huì)發(fā)現(xiàn),的確應(yīng)該在 “ if(key_state==key_state0) {...} ” 中清零(第一感覺真的很準(zhǔn))。不過(guò)還要添加一個(gè) “標(biāo)志位” 可以是 “釋放標(biāo)志位” 也可以是 “按下標(biāo)志位”,為什么?就留給各位來(lái)想了。這里選擇 “釋放標(biāo)志位”。修改的代碼如下:
/***************CHANGE--START******************************/
case key_state2:{ //狀態(tài)2,判斷是否被釋放
if(key_temp==no_key){ //釋放,轉(zhuǎn)回到狀態(tài)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 也就是釋放標(biāo)志位 */
return key_value;
}
/***************CHANGE--END******************************/
完成以后,編譯、燒寫、運(yùn)行,OK!
矩陣鍵盤 狀態(tài)機(jī)法主程序:
/*==========================================================
* 開發(fā)人員:laowang
* 當(dāng)前版本:V1.0
* 創(chuàng)建時(shí)間:11/26
* 修改時(shí)間:
* 功能說(shuō)明:對(duì)4*4矩陣鍵盤掃描,用8位數(shù)碼管進(jìn)行顯示,剛開始時(shí)不亮,依次按下按鍵時(shí)
數(shù)碼管依次顯示0-F,掃描方法為狀態(tài)機(jī)方法+定時(shí)器中斷
*==========================================================*/
#include<reg52.h>
#include"Define.h"
#include"display.h"
#include"matrixkeyscan.h"
void Timer0_init(); //定時(shí)器初始化函數(shù)
uint flag=0; //按鍵掃描標(biāo)志,每中斷一次,掃描一次
//主函數(shù)
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產(chǎn)生一次中斷
TL0=0xf0;
flag=1;
}
void Timer0_init()
{
TMOD=0x01;
TH0=0xd8; //12MHz 10Ms
TL0=0xf0;
EA=1;
ET0=1;
TR0=1;
}
復(fù)制代碼
行列掃描法的主程序:
/*==========================================================
* 開發(fā)人員:laowang
* 當(dāng)前版本:V1.0
* 創(chuàng)建時(shí)間:11/19
* 修改時(shí)間:
* 功能說(shuō)明:讓數(shù)碼管的前兩位顯示顯示一個(gè)二位數(shù)(0-59),
通過(guò)三個(gè)按鍵控制,按下key1num加1,按下key2num減1,按下key3num清零
*==========================================================*/
/*==================硬件電路=============================================
*
*
* MCU=89x51/52 8位數(shù)碼管(共陰)
* +---------------+ +------+ +------------+------------+
* | 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|-----| 位選 |--------------------------|
* | | +------+
* | |
* | |
* +---------------+
*
* 說(shuō)明:此例程中只用到了數(shù)碼管的前兩位
*=================================================================================*/
#include<reg52.h>
#include"display.h"
#include"matrixkey.h"
void main()
{
while(1)
{
Matrixkeyscan();
Display(key);
}
}
復(fù)制代碼
完整的源碼請(qǐng)到本帖頂部下載附件.
作者:
uaeng
時(shí)間:
2017-3-13 16:45
在劉平老師的《深入淺出玩轉(zhuǎn)51單片機(jī)》里學(xué)習(xí)過(guò)狀態(tài)機(jī)法,看得不是太懂。謝樓主分享了。
作者:
2721412lcy
時(shí)間:
2017-4-15 17:14
就是沒有金幣了
作者:
2721412lcy
時(shí)間:
2017-4-19 08:30
下載看了,覺得上電數(shù)碼管亮,好像是初始化顯示函數(shù)Display_init();那里一開始就把位選全打開了,而每10Ms產(chǎn)生一次中斷,主函數(shù)中一開始readkey=0xff;然后if(flag){flag=0;readkey=Keyscan();Display(readkey);一開始10ms就顯示0XFF,覺得這個(gè)位選應(yīng)該在按鍵松手才打開,修改如下
void Display_init()
{
P1=0xFF; //將所有的位選關(guān)閉,所有數(shù)碼管不亮,
wela=1;
wela=0;
P1=0x00; //將段選關(guān)閉,也就是剛開始的時(shí)候數(shù)碼管都不亮
dula=1;
dula=0;
}
void Display(uchar num1)
{
wela=1;
P1=0x00; //將所有的位選打開,所有數(shù)碼管同時(shí)亮,
wela=0;
dula=1;
P1=smg_duan[num1];
dula=0;
}
作者:
2721412lcy
時(shí)間:
2017-4-19 08:31
哦,我看的是狀態(tài)機(jī)那個(gè)。
下載看了,覺得上電數(shù)碼管亮,好像是初始化顯示函數(shù)Display_init();那里一開始就把位選全打開了,而每10Ms產(chǎn)生一次中斷,主函數(shù)中一開始readkey=0xff;然后if(flag){flag=0;readkey=Keyscan();Display(readkey);一開始10ms就顯示0XFF,覺得這個(gè)位選應(yīng)該在按鍵松手才打開,修改如下
void Display_init()
{
P1=0xFF; //將所有的位選關(guān)閉,所有數(shù)碼管不亮,
wela=1;
wela=0;
P1=0x00; //將段選關(guān)閉,也就是剛開始的時(shí)候數(shù)碼管都不亮
dula=1;
dula=0;
}
void Display(uchar num1)
{
wela=1;
P1=0x00; //將所有的位選打開,所有數(shù)碼管同時(shí)亮,
wela=0;
dula=1;
P1=smg_duan[num1];
dula=0;
}
作者:
2721412lcy
時(shí)間:
2017-4-19 09:11
下載看了狀態(tài)機(jī),覺得上電數(shù)碼管亮,好像是初始化顯示函數(shù)Display_init();那里一開始就把位選全打開了,而每10Ms產(chǎn)生一次中斷,主函數(shù)中一開始readkey=0xff;然后if(flag){flag=0;readkey=Keyscan();Display(readkey);一開始10ms就顯示0XFF,覺得這個(gè)位選應(yīng)該在按鍵松手才打開,修改如下
void Display_init()
{
P1=0xFF; //將所有的位選關(guān)閉,所有數(shù)碼管不亮,
wela=1;
wela=0;
P1=0x00; //將段選關(guān)閉,也就是剛開始的時(shí)候數(shù)碼管都不亮
dula=1;
dula=0;
}
void Display(uchar num1)
{
wela=1;
P1=0x00; //將所有的位選打開,所有數(shù)碼管同時(shí)亮,
wela=0;
dula=1;
P1=smg_duan[num1];
dula=0;
}
作者:
小~灰~灰
時(shí)間:
2017-7-24 14:24
目前用的反轉(zhuǎn),出了些問題,剛好學(xué)習(xí)學(xué)習(xí)
多謝樓主了
作者:
GaryHobson
時(shí)間:
2018-3-7 13:33
之前都是用反轉(zhuǎn)法,現(xiàn)在試試狀態(tài)機(jī)
作者:
wandiyh
時(shí)間:
2018-5-24 10:46
感謝樓主分享
,學(xué)習(xí)一下
作者:
aw3aw3
時(shí)間:
2018-5-26 15:32
很有用。非常感謝分享
作者:
yuhang98
時(shí)間:
2018-11-20 20:07
學(xué)習(xí)一下
作者:
黑米粥粥
時(shí)間:
2018-12-2 14:45
感謝樓主分享,學(xué)習(xí)一下
作者:
hulilanyua
時(shí)間:
2019-6-10 00:03
多謝樓主了
作者:
L1BB
時(shí)間:
2019-7-13 22:00
感謝樓主
作者:
2448289424
時(shí)間:
2019-7-14 17:20
寫得挺好的,點(diǎn)贊,,我下載了你的狀態(tài)機(jī)程序,,為了省點(diǎn)黑幣,其他兩個(gè)我就沒下載了,,我看了后自己有些看法,,如果我同一行一個(gè)按鍵壓死,,然后我在同一行按下另一個(gè)按鍵呢????還有主函數(shù)10Ms檢測(cè)一次,,為什么數(shù)碼管顯示也放在這個(gè)里面???上電數(shù)碼管亮我覺得不是上面6#說(shuō)的, readkey=Keyscan();
Display(readkey);
上電按鍵沒按的換Keyscan()返回的應(yīng)該是0吧,,display(0)對(duì)應(yīng)你的數(shù)組不就是全亮???,我覺得應(yīng)該是這樣,試試把數(shù)組的全滅數(shù)據(jù)也就是0x00,放在第一個(gè),,剩下的就按你的按鍵去排顯示的數(shù)據(jù)就好了
作者:
ZzzZM
時(shí)間:
2019-11-18 21:52
哇,感謝樓主的分享,給我們這些小白提供了學(xué)習(xí)的資料
作者:
Why丶
時(shí)間:
2019-12-8 15:50
感謝分享
歡迎光臨 (http://www.torrancerestoration.com/bbs/)
Powered by Discuz! X3.1