独立按键与单片机连接时,每一个按键都需要单片机的一个 I/O 口,若某单片机系统需较多按键,如果用独立按键便会占用过多的 I/O 口资源。单片机系统中 I/O 口资源往往比较宝贵,当用到多个按键时为了减少 I/O 口引脚,引入了矩阵按键。
无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都是一样的,也就是检测与该键对应的 I/O 口是否为低电平。独立键盘有一端固定为低电平,此种方式编程比较简单。 而矩阵键盘两端都与单片机 I/O 口相连,因此在检测时需编程通过单片机 I/O 口送出低电平。检测方法有多种,最常用的是行列扫描和线翻转法。
行列扫描法检测时,先送一列为低电平,其余几列全为高电平(此时我们确定了列数),然后立即轮流检测一次各行是否有低电平,若检测到某一行为低电平(这时我们又确定了行数),则我们便可确认当前被按下的键是哪一行哪一列的,用同样方法轮流送各列一次低电平,再轮流检测一次各行是否变为低电平,这样即可检测完所有的按键,当有键被按下时便可判断出按下的键是哪一个键。当然我们也可以将行线置低电平,扫描列是否有低电平。从而达到整个键盘的检测。
线翻转法,就是使所有行线为低电平时,检测所有列线是否有低电平,如果有,就记录列线值;然后再翻转,使所有列线都为低电平,检测所有行线的值,由于有按键按下,行线的值也会有变化,记录行线的值。从而就可以检测到全部按键。
矩阵键盘也少不了按键消抖的环节。
通过数码管显示矩阵按键 S1-S16 按下后键值 0-F。
#include "reg52.h"
typedef unsigned char u8;
typedef unsigned int u16;
#define SMG_A_AP_PORT P0 //数码管
#define KEY_MATRIX_PORT P1 //矩阵按键
u8 gsmg_code[] = {
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
void delay(u16 us)
{
while(us--);
}
//行列式扫描
u8 key_matrix_ranks_scan(void)
{
u8 key_value = 0;
KEY_MATRIX_PORT = 0xf7; //第一列为低电平--p13为低电平
if(KEY_MATRIX_PORT != 0xf7) //第一列是否有按键按下
{
delay(1000);//按键按下 消抖
switch (KEY_MATRIX_PORT)
{
case 0x77: key_value = 1; break; //S1
case 0xb7: key_value = 5; break; //S5
case 0xd7: key_value = 9; break; //S9
case 0xe7: key_value = 13; break; //S13
}
while(KEY_MATRIX_PORT != 0xf7); //等待按键松开
}
KEY_MATRIX_PORT = 0xfb; //第二列为低电平--p12为低电平
if(KEY_MATRIX_PORT != 0xfb) //第一列是否有按键按下
{
delay(1000);//按键按下 消抖
switch (KEY_MATRIX_PORT)
{
case 0x7b: key_value = 2; break; //S2
case 0xbb: key_value = 6; break; //S6
case 0xdb: key_value = 10; break; //S10
case 0xeb: key_value = 14; break; //S114
}
while(KEY_MATRIX_PORT != 0xfb); //等待按键松开
}
KEY_MATRIX_PORT = 0xfd; //第三列为低电平--p11为低电平
if(KEY_MATRIX_PORT != 0xfd) //第一列是否有按键按下
{
delay(1000);//按键按下 消抖
switch (KEY_MATRIX_PORT)
{
case 0x7d: key_value = 3; break; //S3
case 0xbd: key_value = 7; break; //S7
case 0xdd: key_value = 11; break; //S11
case 0xed: key_value = 15; break; //S15
}
while(KEY_MATRIX_PORT != 0xfd); //等待按键松开
}
KEY_MATRIX_PORT = 0xfe; //第四列为低电平--p10为低电平
if(KEY_MATRIX_PORT != 0xfe) //第一列是否有按键按下
{
delay(1000);//按键按下 消抖
switch (KEY_MATRIX_PORT)
{
case 0x7e: key_value = 4; break; //S4
case 0xbe: key_value = 8; break; //S8
case 0xde: key_value = 12; break; //S12
case 0xee: key_value = 16; break; //S16
}
while(KEY_MATRIX_PORT != 0xfe); //等待按键松开
}
return key_value;
}
//线翻转法扫描
u8 key_matrix_flip_scan()
{
u8 key_value = 0;
KEY_MATRIX_PORT = 0x0f; //行全为0 判断列
//测试列
if(KEY_MATRIX_PORT != 0x0f)
{
delay(1000);
switch (KEY_MATRIX_PORT)
{
case 0x07: key_value = 1; break; //第1列有按键按下
case 0x0b: key_value = 2; break; //第2列有按键按下
case 0x0d: key_value = 3; break; //第3列有按键按下
case 0x0e: key_value = 4; break; //第4列有按键按下
}
//测试行
KEY_MATRIX_PORT = 0xf0;
switch (KEY_MATRIX_PORT)
{
case 0x70: key_value = key_value; break; //第1行有按键按下
case 0xb0: key_value = key_value + 4; break; //第2行有按键按下
case 0xd0: key_value = key_value + 8; break; //第3行有按键按下
case 0xe0: key_value = key_value + 12; break; //第4行有按键按下
}
while (KEY_MATRIX_PORT != 0xf0);
}
return key_value;
}
void main()
{
u8 ret = 0;
while(1)
{
#if 0
//行列式扫描
ret = key_matrix_ranks_scan();
if(ret != 0)
SMG_A_AP_PORT = gsmg_code[ret-1]; //保证有按键按下
#endif
//线翻转
ret = key_matrix_flip_scan();
if(ret != 0)
SMG_A_AP_PORT = gsmg_code[ret-1]; //保证有按键按下
}
}