矩陣鍵盤應(yīng)該是經(jīng)常能夠用到的一類器件了,4X4矩陣鍵盤只需要用到8個(gè)IO口,即可完成16位按鍵的讀取。其本質(zhì)原理也就是行列掃描。本片文章將帶你詳細(xì)的學(xué)習(xí)矩陣鍵盤的原理以及代碼編寫。
矩陣鍵盤本質(zhì):
矩陣鍵盤本質(zhì)是使用8個(gè)io口來進(jìn)行16個(gè)按鍵的控制讀取,可以減小io口的使用,用4條I/O線作為行線,4條I/O線作為列線組成的鍵盤。在行線和列線的每個(gè)交叉點(diǎn)上,設(shè)置一個(gè)按鍵。而這樣的按鍵中按鍵的個(gè)數(shù)是4 X 4個(gè)。
這樣的行列式鍵盤結(jié)構(gòu)能夠有效地提高單片機(jī)系統(tǒng)中I/O口的利用率。節(jié)約單片機(jī)的資源,其本質(zhì)和獨(dú)立按鍵類似,就是進(jìn)行逐行掃描和逐列掃描,然后判斷是第幾行的第幾列個(gè)按鍵,進(jìn)而進(jìn)行整體按鍵值得確定,我們使用的矩陣鍵盤是接到了單片機(jī)的P1口通過讀取P1口電平變換即可完成矩陣鍵盤的數(shù)值讀取,具體原理圖如下:
第一行接到p17,第二行接到p16,第三行接到p15,第4行接到p14
第一列接到p13,第二列接到p12,第三列接到p11,第四列接到p10
矩陣鍵盤掃描的方式有兩種:1.行列掃描,2.逐行/逐列掃描
其中行列掃描適用于8個(gè)IO口接到了單片機(jī)8個(gè)連續(xù)的IO口,則可以進(jìn)行行列掃描
逐行/逐列掃描 適用于矩陣鍵盤接到了任意的IO口,則使用逐行,逐列掃描
接下來我們分別介紹這兩種方式:
行列掃描:
原理:
先從P1口的高四位(四個(gè)行)輸出高電平,低四位(四個(gè)列)輸出低電平,假設(shè)有按鍵按下,從P1口的高四位讀取鍵盤狀態(tài)。判斷高四位的四行哪一行變成了低電平,就知道是第幾行,再?gòu)腜1口的低四位(四個(gè)列)輸出高電平,高四位(四個(gè)行)輸出低電平,從P1口的低四位讀取鍵盤狀態(tài)。判斷低四位的四列哪一行變成了低電平,就知道是第幾列,將兩次讀取結(jié)果組合起來就可以得到當(dāng)前按鍵的特征編碼。使用上述方法我們得到16個(gè)鍵的特征編碼。
紅色高電平,藍(lán)色低電平
詳解:
據(jù)矩陣鍵盤的原理圖可知,如果矩陣鍵盤的8個(gè)IO口連接到了連續(xù)的一個(gè)一個(gè)人P10-P17上,當(dāng)沒有按鍵按下時(shí),將P1口的P1^0 和 P13 置高電平 P14 和 P17 置低電平 ,也就是將4個(gè)行的IO口置高,4個(gè)列的IO口置低。也就是P1=0x0f(0000 1111);
如果這時(shí)候有按鍵按下那么P1^0 和 P13 就有一個(gè)會(huì)變成低電平。因此P1的值就不等于0x0f,按下按鍵所在的行就會(huì)變成低電平,這是就可以判斷有按鍵按下。
將對(duì)應(yīng)P1口的值和0x0f(00001111)相 與& 則可以得到高四位第幾行變成了0
按位“與”&(雙目運(yùn)算符):僅當(dāng)兩個(gè)操作數(shù)都為1時(shí),結(jié)果為1,否則為0。
比方說:0&0=0;0&1=0;1&0=0;1&1=1
即:兩個(gè)同時(shí)為1,結(jié)果為1,否則為0
比方說按下的第一行第一列 1x1
例:
0000 1110------------- 按下1x1之后P1的值
& 0000 1111------------- 0x0f
----------
0000 1110------------- 最后得到的結(jié)果,第一行為0
再給P1口賦值0X0f。將P1口的P1^0 和 P13 置低電平 P14 和 P17 置高電平 ,也就是將4個(gè)低的IO口置高,4個(gè)列的IO口置高然后讀取低四位的電平, 此時(shí)的P1口 (1111 0000)
讀取此時(shí)的P1口 和0xf0(11110000)相 與& 則可以得到低四位第幾列變成了低電平
比方說我們按下的是1x1 按鍵,也就是第一行第一列,這時(shí)在按下之后可以看到p1.0和p1.3都是低電平,將Row=P1&0x0f;(行的值) 和 Col=P1&0xf0;//列值 進(jìn)行相加,就可以得到按下的是那個(gè)按鍵
1x1:(一行一列)
Row=P1&0x0f =? 0000 1110
Col=P1&0xf0=? ? ?1110 0000
Row+Col=? ? ? ? ? ?1110 1110? ?= 0xee
低電平0表示對(duì)應(yīng)的行列按下
可以看到下方的p1.0和p1.4變成了低電平
2x2:(二行二列)
Row=P1&0x0f =? 0000 1101
Col=P1&0xf0=? ? ?1101 0000
Row+Col=? ? ? ? ? ?1101 1101? ?= 0xdd
3x4:(三行四列)
Row=P1&0x0f =? 0000 1011
Col=P1&0xf0=? ? ?0111 0000
Row+Col=? ? ? ? ? ?0111 1011? ?= 0x7b
這樣就可以得到所有的16個(gè)按鍵的數(shù)值,具體代碼如下:
unsigned char keyscan(){
unsigned char key,Row,Col;
P1=0x0f;
if(P1!=0x0f){
delay(10);//去抖
if(P1!=0x0f){
Row=P1&0x0f;//確保端口值正確(行的值)
P1=0xf0;
Col=P1&0xf0;//列值
?
}
while((P1&0xf0)!=0xf0);//判斷鍵是否抬起
}
switch(Row+Col){
case 0xee:key=0;break;
case 0xde:key=1;break;
case 0xbe:key=2;break;
case 0x7e:key=3;break;
?
case 0xed:key=4;break;
case 0xdd:key=5;break;
case 0xbd:key=6;break;
case 0x7d:key=7;break;
?
case 0xeb:key=8;break;
case 0xdb:key=9;break;
case 0xbb:key=10;break;
case 0x7b:key=11;break;
?
case 0xe7:key=12;break;
case 0xd7:key=13;break;
case 0xb7:key=14;break;
case 0x77:key=15;break;
?
?
}
return key;
}
?
運(yùn)行效果圖:
逐行/列掃描:
逐行,逐列掃描的本質(zhì)和行列掃描比較類似,本質(zhì)是給某一行/某一列,低電平,其余七個(gè)全部為高電平,這時(shí)候讀取電平變換,有電平變低表示按鍵按下,即可讀取按鍵數(shù)據(jù)。
比如逐行掃描:
置第1行為低電平,其余N-1行和N列為高電平,
讀取列線數(shù)據(jù),列線有低電平表示此行有按鍵按下,比如按下的是1行三列(1x3),那么第三列的列線IO口就為低電平。
置第2行為低電平,其余N-1行和N列為高電平,,讀取列線數(shù)據(jù),列線有低電平表示此行有按鍵按下。
以此類推,進(jìn)行逐行掃描。
根據(jù)行線列線的電平不同可以識(shí)別是否有按鍵按下,哪一個(gè)按鍵按下,獲取按鍵號(hào)。(N) 根據(jù)按鍵號(hào)跳轉(zhuǎn)至對(duì)應(yīng)的按鍵處理程序。
用我們的P1口來進(jìn)行舉例:
首先,給P1賦值 P1=0xfe(1111 1110);,這時(shí)P1.0為低電平,P1.1~p1.7為高電平,如果這時(shí)候有按鍵按下那么四個(gè)列線,P1.4,P1.5,P1.6,P1.7就有一個(gè)列會(huì)變成低電平。因此P1的值就不等于0xfe,這是就可以判斷有按鍵按下。
然后延時(shí)一段時(shí)間去抖動(dòng),然后給P1賦值0xfd(1111 1101),也就是P1.1為低電平,其他為高電平,這時(shí)如果有在P1.1線上的P1.4,P1.5,P1.6,P1.7有按鍵按下,那么就會(huì)出現(xiàn)低電平,從而判斷哪個(gè)按鍵按下;如果沒有那么就給P1賦值0xfb(1111 1011),也就是P1.2為低電平,其他為高電平.,相同方法判斷是否有按鍵按下;······如此類推,一共四次檢測(cè)。
比如當(dāng)?shù)?行有按鍵按下時(shí)P1的相應(yīng)值為:
? 1X1(11101110=0xee)
? 1x2(11011110=0xde)
1X3(10111110=0xbe)
1X4(01111110=0x7e)
第2行有按鍵按下時(shí)P1的相應(yīng)值為:
? ? ?2X1(11101101=0xed)
? ? ?2x2(11011101=0xdd)
? ? ?2X3(10111101=0xbd)
? ? ?2X4(01111101=0x7d)
將P1^2輸出低電平,其他的引腳都輸出高電平,即P1=0xfb,那么當(dāng)?shù)?行有按鍵按下時(shí)P1的相應(yīng)值為:
? ? ?3X1(11101011=0xeb)
? ? ?3x2(11011011=0xdb)
? ? ?3X3(10111011=0xbb)
? ? ?3X4(01111011=0x7b)
最后可得第四行的相對(duì)應(yīng)值為:
? ? ?4X1(11100111=0xe7)
? ? ?4x2(11010111=0xd7)
? ? ?4X3(10110111=0xb7)
? 4X4(01110111=0x77)
那么最后我們可以得到代碼:
/*****************************************************************************
** 函數(shù)名稱:keyscan
** 功能描述:按鍵獲取函數(shù)
******************************************************************************/
void keyscan(void)
{
P1=0xfe;
? ?temp=P1;
? ?temp=temp&0xf0;
? ?if(temp!=0xf0)
? ?{
delay(10);
? ? ? if(temp!=0xf0)
? ? ? {
temp=P1;
switch(temp)
{
case 0xee:key=0;break;
case 0xde:key=1;break;
case 0xbe:key=2;break;
case 0x7e:key=3;break;
? ? ? ? ?}
? ? ? ? ?while(temp!=0xf0)?
{
temp=P1;
temp=temp&0xf0;
? ? ? ? ?}
}
}
P1=0xfd;
temp=P1;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(10);
if(temp!=0xf0)
? ? ? {
temp=P1;
switch(temp)
{
case 0xed: key=4;break;
case 0xdd:key=5;break;
case 0xbd:key=6;break;
case 0x7d:key=7;break;
? ? ? ? ?}
? ? ? ? ?while(temp!=0xf0)
? ? ? ? ?{
? ? ? ? ? ?temp=P1;
? ? ? ? ? ?temp=temp&0xf0;
? ? ? ? ?}
? ? ? }
}
P1=0xfb;
temp=P1;
temp=temp&0xf0;
if(temp!=0xf0)
{
delay(10);
? ? ? if(temp!=0xf0)
? ? ? {
temp=P1;
switch(temp)
{
case 0xeb:key=8;break;
case 0xdb:key=9;break;
case 0xbb:key=10;break;
case 0x7b:key=11;break;
? ? ? ? ?}
beep=0;delay(50);beep=1;
while(temp!=0xf0)
? ? ? ? ?{
temp=P1;
temp=temp&0xf0;
? ? ? ? ?}
? ? ? }
? ?}
P1=0xf7;
? ?temp=P1;
? ?temp=temp&0xf0;
? ?if(temp!=0xf0)
? ?{
? ? ? delay(10);
? ? ? if(temp!=0xf0)
? ? ? {
temp=P1;
switch(temp)
{
case 0xe7:key=12;break;
case 0xd7:key=13;break;
case 0xb7:key=14;break;
case 0x77:key=15;break;
? ? ? ? ?}
while(temp!=0xf0)
? ? ? ? ?{
temp=P1;
temp=temp&0xf0;
? ? ? ? ?}
? ? ? }
}
}
?
運(yùn)行效果圖:
審核編輯:湯梓紅
評(píng)論
查看更多