一个实时性高的4x4矩阵键盘算法
引言
这个办法是我大二搞直立智能车的时候想出来的,当时直立智能车需要用矩阵键盘调PID参数,但一般的矩阵键盘算法都有一个“去抖延时”和“等待按键松开”的步骤,在这个步骤里程序是陷入死循环的,只有松开按键,程序才会继续运行。一般来说,按一下按键再松开,程序就要停顿一秒左右。普通的单片机程序停顿一秒钟没问题,但直立车停顿一秒钟就倒了。
于是有了这么一个矩阵键盘算法,可以用空间换时间,按一下按键程序只需要停顿6~11毫秒。不需要中断,实时性高,支持组合键。
要使用这个矩阵键盘算法,只需要三步。
C51代码(矩阵键盘连接P3口)
1.定义全局变量
uchar key_TTL[16];
uint keys_val=0;
uint last_keys_val=0;
uchar max_TTL=3;
2.在key_spy()函数中添加按键功能
void key_spy()
{
uint temp=0;
//双循环扫描法 + 译码
uchar k,l;
for(l=0;l<4;l++){
P3=~(0x01<<l);
for(k=0;k<4;k++){
if((P3&(0x10<<k))==0) key_TTL[k+l*4]+=2;
}
}
//TTL去抖
for(k=0;k<16;k++){
if(key_TTL[k]>0)key_TTL[k]--;
if(key_TTL[k]>=max_TTL){
key_TTL[k]--;
temp=temp|(1<<k);
}
}
keys_val=temp;
//按键功能
if(keys_val!=last_keys_val){
if(keys_val==((1<<0))){
//TODO:按下了0号键
}
else if(keys_val==((1<<3)|(1<<4)|(1<<5))){
//TODO:同时按下了3号键、4号键、5号键
}
//TODO:加入更多else if,以扩充矩阵键盘功能
}
last_keys_val=keys_val;
}
3.主循环中添加key_spy()函数
void main()
{
while(1)
{
key_spy();
//TODO:主循环的其他事物
}
}
原理介绍
软件去抖的原理
key_spy()函数是检测矩阵键盘用的,放在主循环里就每隔一段时间检测一次。
检测第一次,发现有个按键被按下了,这有可能是电磁干扰造成的,不管它。
隔不久,检测第二次,发现刚才那个按键还是被按下的,这有可能是其他什么干扰造成的,不管。
相同的间隔,检测第三次,发现刚才那个按键仍被按下,就说明这个按键真的是被按下的。
对每个按键,都需要有一个计数器,就是用于计数的变量,这个变量称为“存活时间”,Time To Live,简称TTL。
一个TTL计数器,就对应一个不确定的事件(按键被按下),这个不确定的事件只有两个状态,发生了和没发生。事件的状态可以被检测,但检测得不一定准。每检测到事件发生了,事件的存活时间TTL就加1。检测到事件没发生,事件的存活时间TTL就减1。TTL的值必须大于等于0,小于等于阈值。当TTL等于阈值,就可以认为事件一定发生了。当TTL等于零,可以认为事件一定没发生。这就可以作为软件去抖的方法。
给矩阵键盘的16个按键分别设置一个TTL计数器,就可以对16个按键进行去抖。
组合键的原理
矩阵键盘是否支持组合键,跟矩阵键盘的扫描方法有很大关系。
线反转法
线反转法不支持组合键:
void key_spy1()
{
//线反转法
uchar flag=1,key,k;
P3=0xf0;
key=P3&0xf0;
P3=0x0f;
key|=(P3&0x0f);
switch(key)
{
case 0xee: key_TTL[0]+=2; break;
case 0xde: key_TTL[1]+=2; break;
case 0xbe: key_TTL[2]+=2; break;
case 0x7e: key_TTL[3]+=2; break;
case 0xed: key_TTL[4]+=2; break;
case 0xdd: key_TTL[5]+=2; break;
case 0xbd: key_TTL[6]+=2; break;
case 0x7d: key_TTL[7]+=2; break;
case 0xeb: key_TTL[8]+=2; break;
case 0xdb: key_TTL[9]+=2; break;
case 0xbb: key_TTL[10]+=2; break;
case 0x7b: key_TTL[11]+=2; break;
case 0xe7: key_TTL[12]+=2; break;
case 0xd7: key_TTL[13]+=2; break;
case 0xb7: key_TTL[14]+=2; break;
case 0x77: key_TTL[15]+=2; break;
default:break;
}
//check TTL
for(k=0;k<16;k++)
{
if(key_TTL[k]>0)key_TTL[k]--;
if(key_TTL[k]>=max_TTL)//>=改为=,就能自动连点
{
key_TTL[k]--;
key_val=k+1;
flag=0;
}
}
if(flag)key_val=0;//全局变量key_val即为键值
}
逐行扫描法
逐行扫描法支持跨行组合键,但不支持同行组合键:
void key_spy2()
{
//逐行扫描法
uchar flag=1,k;
P3=0xfe;
switch(P3)
{
case 0xee: key_TTL[0]+=2; break;
case 0xde: key_TTL[1]+=2; break;
case 0xbe: key_TTL[2]+=2; break;
case 0x7e: key_TTL[3]+=2; break;
}
P3=0xfd;
switch(P3)
{
case 0xed: key_TTL[4]+=2; break;
case 0xdd: key_TTL[5]+=2; break;
case 0xbd: key_TTL[6]+=2; break;
case 0x7d: key_TTL[7]+=2; break;
}
P3=0xfb;
switch(P3)
{
case 0xeb: key_TTL[8]+=2; break;
case 0xdb: key_TTL[9]+=2; break;
case 0xbb: key_TTL[10]+=2; break;
case 0x7b: key_TTL[11]+=2; break;
}
P3=0xf7;
switch(P3)
{
case 0xe7: key_TTL[12]+=2; break;
case 0xd7: key_TTL[13]+=2; break;
case 0xb7: key_TTL[14]+=2; break;
case 0x77: key_TTL[15]+=2; break;
}
//check TTL
for(k=0;k<16;k++)
{
if(key_TTL[k]>0)key_TTL[k]--;
if(key_TTL[k]>=max_TTL)//>=改为=,就能自动连点
{
key_TTL[k]--;
key_val=k+1;
flag=0;
}
}
if(flag)key_val=0;//全局变量key_val即为键值
}
双循环扫描法
双循环扫描法支持同行组合键,但不支持三角键
void key_spy3()
{
//双循环扫描法
uchar flag=1,k;
P3=0xfe;P3=0xfe;//高四位设为输入
OLED_write_bchar(0,0,P3);
if((P3&0x80)==0) key_TTL[0]+=2;
if((P3&0x40)==0) key_TTL[1]+=2;
if((P3&0x20)==0) key_TTL[2]+=2;
if((P3&0x10)==0) key_TTL[3]+=2;
P3=0xfd;P3=0xfd;//高四位设为输入
if((P3&0x80)==0) key_TTL[4]+=2;
if((P3&0x40)==0) key_TTL[5]+=2;
if((P3&0x20)==0) key_TTL[6]+=2;
if((P3&0x10)==0) key_TTL[7]+=2;
P3=0xfb;P3=0xfb;//高四位设为输入
if((P3&0x80)==0) key_TTL[8]+=2;
if((P3&0x40)==0) key_TTL[9]+=2;
if((P3&0x20)==0) key_TTL[10]+=2;
if((P3&0x10)==0) key_TTL[11]+=2;
P3=0xf7;P3=0xf7;//高四位设为输入
if((P3&0x80)==0) key_TTL[12]+=2;
if((P3&0x40)==0) key_TTL[13]+=2;
if((P3&0x20)==0) key_TTL[14]+=2;
if((P3&0x10)==0) key_TTL[15]+=2;
//check TTL
for(k=0;k<16;k++)
{
if(key_TTL[k]>0)key_TTL[k]--;
if(key_TTL[k]>=max_TTL)//>=改为=,就能自动连点
{
key_TTL[k]--;
key_val=k+1;
flag=0;
}
}
if(flag)key_val=0;//全局变量key_val即为键值
}
双循环扫描法经过简化代码,并且附加上按键处理代码以后,就是上文的key_spy()函数。