前言
最近做单片机的课程设计用到矩阵键盘,在此做个记录。
1 矩阵键盘的扫描方式
使用矩阵键盘时,首先要判断是否有按键按下,这个过程称为矩阵键盘的全局扫描。单片机对于键盘按下的响应方式一般有三种:(1)定时扫描:利用定时器每间隔一段时间扫描一次键盘(2)程序扫描:在程序运行的过程中,当有必要了解是否有按键按下时,调用键盘扫描函数进行扫描(3)中断扫描:当有按键按下时,触发外部中断。
网络上关于矩阵键盘的扫描方式的介绍多局限于(1)和(2)两种方式,这两种方式在单一的矩阵键盘的实验中是可行的,但对于一个复杂的系统来说,单片机要处理的任务较多,宜采用中断扫描的方式,仅在有按键按下时触发中断,以节约CPU资源,本文介绍第(3)种方式
2 获取按下的按键的键值的方法
上面提到的全局扫描只能了解是否有按键按下,具体是哪一个按键按下,需要进一步扫描确定,在这个过程中完成按键的键值(编号)的获取。获取键值主要有线反转法和逐行逐列扫描法,本文介绍逐行逐列扫描法,线反转法的效率更高,但本文不作介绍。
3 逐行逐列扫描法
对于上图所示的矩阵键盘,键盘全局扫描(判断是否有按键按下)的一般原理如下:首先使列线所连接的I/O引脚P14至P16输出低电平,行线所连接的I/O引脚P10-P13输出高电平。当没有按键按下时,四条行线R1-R4所连接的I/O引脚读取到的将全部是高电平;而当有按键按下时,由于该行线与列线接通,将被下拉到低电平。此时读取行线所连接的引脚,将不再全是高电平,由此可以判断出有按键按下。
获取按下的按键键值的方法与之类似,逐条拉低每一条列线(其他列线为高电平),读取行线的状态,如果某一条行线被拉至低电平,则该行线就是被按下的按键所在行,而此时我们所拉低的那条列线,就是按下的按键所在的列,由此唯一确定按下的按键。
4 中断的触发电路
根据上面的逐行逐列扫描法,如果将行线置为高电平,而列线置为低电平,一旦有按键按下,将会有行线被下拉至低电平。因此只要有一条行线为低电平,就应该输出一个低电平,向单片机请求中断。可以用一个四输入与门(74LS21)来实现这种逻辑功能。这就是基于中断的全局扫描法
有了这个中断触发电路,我们就只需要通过中断就能了解到是否有按键按下
可以将外部中断0设置为下降沿触发方式。有按键按下时将触发中断。但是要注意上面的逐行逐列扫描的过程中可能会误触发中断,为避免误触发,在扫描键值的函数退出前应该把中断标志位EX0清除,这样就不会因为逐行逐列的扫描方式而误触发中断。下面给出逐行逐列扫描的程序。其实扫描键盘还可以采用线反法,效率更高,有兴趣的可以自行了解一下。
//keycode是全局变量,用于返回按键键值,对于上图的键盘,按键键值从左到右、从上到下依次为1-12
//使用前需包含 intrins.h
void int0() interrupt 0 //在中断服务函数中进行键盘扫描
{
unsigned char row_scan_code=0x01; //行扫描码
unsigned char col_scan_code=0xEF;//列扫描码
unsigned char i,x,j;
for(i=0;i<3;i++)//逐列扫描,将列线逐列拉低
{
keycode=i+1;
keyboard=col_scan_code;
x=keyboard; //读取行线状态
for(j=0;j<4;j++)//逐行扫描
{
if(!(x&row_scan_code))//说明对应行的按键按下,使行线被拉低
{
keycode+=3*j;
//如果按键还未释放,则行线仍被拉至低电平,与门输出0
while(!P32);//等待按键释放 P32为外部中断0输入引脚
P1=0x0F; //恢复原状态,为下次按键做准备
IE0=0; //清除因扫描而误触发的中断标志
return; //已检测到按键键码,返回
}
else
row_scan_code=_crol_(row_scan_code,1);
}
col_scan_code=_crol_(col_scan_code,1);//左移一位,将下一列线拉低
row_scan_code=0x01;//重置行扫描码,为下一行扫描作准备
}
IE0=0; //清除因扫描而误触发的中断标志
keycode=0;//没有按键按下,键值记为0
}