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