在之前的文章中,我们介绍过独立按键的使用,独立按键需要每个按键使用一个IO口进行读取,如果按键比较多,对IO资源的占用比较多。使用矩阵键盘,并采用扫描的方式对按键进行读取是解决按键多的一个有效办法。采用矩阵按键的方式就可以有效的节省I/O资源。4*4的矩阵键盘只需要8个I/O口进行采集。6*6的矩阵键盘只需要12个I/O口就可以采集。
本实验的硬件电路是一个4X4的矩阵键盘。矩阵键盘的原理图如下图所示。
由原理图可以看出J23的1~4与矩阵键盘的每一行相连。5~8与矩阵键盘的每一列相连。每一列上设置了上拉电阻。矩阵键盘扫描时,令J23的1~4中的一个为低电平,如果相应的行有按键按下,则J23的5~8中与这个按键对应的信号也被拉低。从而可以判断出按下的按键。
电路图中D21~D24的作用是实现中断功能。若令J23的1~4都为低电平,有按键按下时,必有一个列信号为低电平,由于二极管的作用,J24也被拉为低电平。如果将J24接在外部中断口上,就可以触发中断。
在这个实验中,我们采用查询的方式对矩阵键盘进行读取,这个实验的代码的主函数如下所示。
main()
{
init_T0();
for(;;)
{
keyma=KEYscan();
keyvalue=Keychange(keyma);
if(keyvalue!=0xff)
{
for(n=0;n<7;n++)
showdata[n]=showdata[n+1]; //显示的数据向左移位
showdata[7]=BJTY_DuanMa[keyvalue]; //读取最新的显示值
}
}
}
在主函数中,先初始化T0,T0主要是设置数码管的扫描定时器。在定时器中断中,进行数码管的扫描。之后进入for循环,在否循环中,先调用KEYscan()函数,对矩阵键盘进行扫描,返回值为IO的码值。再调用Keychange(keyma); 函数把码值转换为按键的键值,最后将按键值赋值给数码管的断码进行显示。
KEYscan()函数是对矩阵键盘进行扫描的函数,函数定义如下所示。函数返回值是8个IO口的码值。
unsigned char KEYscan(void)
{
unsigned char key;
KEYboard=0xf0; //低四位全部拉低
if(KEYboard!=0xf0) //表示有按键按下
{
delay1ms(10); //去抖
if(KEYboard!=0xf0)
{ //表示有按键按下
KEYboard=0xfe; //扫描第一行将第一行拉低
if(KEYboard!=0xfe)
{
delay1ms(10); //去抖
if(KEYboard!=0xfe)
key=KEYboard;
while(KEYboard!=0xfe); //等待按键释放
return key;
}
KEYboard=0xfd; //扫描第二行将第二行拉低
if(KEYboard!=0xfd)
{
delay1ms(10); //去抖
if(KEYboard!=0xfd)
key=KEYboard;
while(KEYboard!=0xfd); //等待按键释放
return key;
}
KEYboard=0xfb; //扫描第三行将第三行拉低
if(KEYboard!=0xfb)
{
delay1ms(10); //去抖
if(KEYboard!=0xfb)
key=KEYboard;
while(KEYboard!=0xfb); //等待按键释放
return key;
}
KEYboard=0xf7; //扫描第四行将第四行拉低
if(KEYboard!=0xf7)
{
delay1ms(10); //去抖
if(KEYboard!=0xf7)
key=KEYboard;
while(KEYboard!=0xf7); //等待按键释放
return key;
}
}
}
return 0xff;
}
Keychange(keyma)函数的定义如下所示。
unsigned char Keychange(unsigned char y)
{
switch(y)
{
case 0xee:return 0;break;//0
case 0xde:return 1;break;//1
case 0xbe:return 2;break;//2
case 0x7e:return 3;break;//3
case 0xed:return 4;break;//4
case 0xdd:return 5;break;//5
case 0xbd:return 6;break;//6
case 0x7d:return 7;break;//7
case 0xeb:return 8;break;//8
case 0xdb:return 9;break;//9
case 0xbb:return 10;break;//a
case 0x7b:return 11;break;//b
case 0xe7:return 12;break;//c
case 0xd7:return 13;break;//d
case 0xb7:return 14;break;//e
case 0x77:return 15;break;//f
default:return 0xff;break;
}
}
烧写之后可以看到实验现象,按下矩阵键盘S1~S16中的任意一个按键,S1~S16这16个按键,分别与数值0~F相对应。按下的按键可以在数码管上显示出来。每按一次按键,数码管上的显示内容向左移一位。
矩阵键盘的扫描与数码管的扫描大同小异。扫描个过程如下:
1、令行线输出全为0;
2、如果列线输入不全是1,则有按键按下;
3、扫描第一行,如果列线不全是1,则第一行有按键按下,返回按键码值;
4、扫描第二行,如果列线不全是1,则第二行有按键按下,返回按键码值;
5、扫描第三行,如果列线不全是1,则第三行有按键按下,返回按键码值;
6、扫描第四行,如果列线不全是1,则第四行有按键按下,返回按键码值。
在这个实验中,我们采用中断方式进行矩阵键盘的读取。例程的主函数如下所示。
main()
{
init_T0();
EX0=1; //打开外部中断0
IT0=1; //外部中断采用边沿触发
for(;;)
{
KEYboard=0xf0; //低四位全部拉低
if(flag==1)
{
flag=0;
keyma=KEYscan();
keyvalue=Keychange(keyma);
if(keyvalue!=0xff)
{
for(n=0;n<7;n++)
showdata[n]=showdata[n+1]; //显示的数据向左移位
showdata[7]=BJTY_DuanMa[keyvalue]; //读取最新的显示值
}
}
}
}
在主函数中,使能了外部中断的通道0,采用边沿触发模式。一旦有按键按下,则会触发外部中断,在中断处理函数中,将标志位flag置1。在主函数中,判断标志位为1时,进行键盘的扫描,并在数码管上显示。具体的扫描方法及显示方法与查询扫描方式是相同的。