最近在笔记本EC中使用到了18*8矩阵键盘,我原本以为这应该是很简单的实现原理:按下某个键时有一根行线与一根列线电平变化。简单地举个例子,比如18根列线为C0~C17,8根行线和R0~R7, 初始时所有的26线全为高电平, 按下按键‘A’时R0和C0为低电平,按下按键‘B’时R0和C1为低电平, 按下按键‘C’时R1和C0为低电平, 按下按键‘D’时R1和C1和低电平…,以此类推。这个实现原理看起来似乎非常准确又设计简便。然而, 仔细想想这样的设计在检测多个按键同时按下时是有BUG存在的,比如当检测到R0、R1、C0、C1这4根线全为低时你根本无法判断‘A’,‘B’,‘C’,‘D’这4个按键到底被同时按了几个, AD、ABD、ACD、BC、ABC、BCD、ABCD这些组合都是可能的。
为了解决这个问题,矩阵键盘采用了另外一种方式,逐行扫描方式,为方便理解,我们采用4*4的矩阵键盘来分析。首先看下原理图:
P10~P13是4根设计成输出口的行线,P14~P17是4根设计成输入口的列线。如此的设计该怎样区别是什么按键被按下了呢?判断方法如下:
1. 扫描P10行:
a) 将P10~P13输出为0 、1、1、1
b) 读P14~P17这4个输入口的电平。如果S0~S3都没被按下,则因为P14~P17都是上拉到VCC,所以都为高;如果有1个被按下比如是S1,则因P10输出为低,所以S1开关导通后P16应为低;如果有2个按下比如是S1,S2,则P16与P17应为低。
c) 根据P14~P17的电平即可得知S0~S3中共有几个按键被按下。
2. 扫描P11行:
a) 将P10~P13输出为1 、0、1、1
b) 读P14~P17这4个输入口的电平。如果S4~S7都没被按下,则因为P14~P17都是上拉到VCC,所以都为高;如果有1个被按下比如是S5,则因P10输出为低,所以S5开关导通后P16应为低;如果有2个按下比如是S5,S6,则P16与P17应为低。
c) 根据P14~P17的电平即可得知S4~S7中共有几个按键被按下。
3. 扫描P12行:将P10~P13输出为1 、1、0、1,其他步骤同上。
4. 扫描P13行:将P10~P13输出为1 、1、1、0,其他步骤同上。
通过这4行的扫描,我们已经能够确定S0~S15这16个按键当前的状态了。
C语言实现如下:
void key_scan() //键盘扫描函数
{
int i;
unsigned char n, row, rowmask ;
row = 1;
for(i=0; i<4; i++) //共扫描4条行线
{
rowmask = ~(row<<i); //i=0时,rowmask=0xfe, i=1时rowmask=0xfd…
P1=rowmask;
n = P1;
n &= 0xf0;
if(n != 0xf0)
{
Delay();//防抖动
P1=rowmask;
n = P1;
n &= 0xf0;
if(n != 0xf0)
{
switch(n)
{
case 0xE0://S(3+i*4)按下
break;
case 0xD0://S(2+i*4)按下
break;
case 0xB0://S(1+i*4)按下
break;
case 0x70://S(0+i*4)按下
break;
}
}
}
}
}
void Delay() //延时函数
{
int i,j;
for(i=0; i<100; i++)
{
for(j=0; j<100; j++);
}
}