通俗来讲,按键的原理其实就是按下S,使得电路短路。比方说按下S7低四位的P30和高四位的P44连通,这样两端的电平则相同,我们可以根据这个来检测在键盘中到底是哪个键盘按下了。
(注意:矩阵键盘在J5中1,2连通才有作用,需要跳线帽连接)
基本编程思路是:
1.检测在哪一列:给低四位赋值0,检测高四位哪一位是0
2.检测在哪一列:给高四位赋值0,检测低四位哪一位是0
3.根据行和列的位置,确定是哪个按键按下
(值得注意的是,在高四位的P4口中,可以直接对寄存器赋值,因为单片机中其他的外设没用到除了P42和P44口以外的口,所以检测的时候可以直接赋值P4=0xff或者0x00)
根据这个思路,我们可以这样写:
//矩阵键盘 - 获取键值
u8 vKBD_KeyIO()
{
u8 key_io = 0;
P3=0xf0;P4=0xff; //列扫描,低四位输出0,读取高四位的值
if(P44==0) key_io=4;
if(P42==0) key_io=8;
if(P35==0) key_io=12;
if(P34==0) key_io=16;
P3=0x0f;P4=0x00; //行扫描,高四位输出0,读取低四位的值
if(P33==0) key_io=key_io + 0;
if(P32==0) key_io=key_io + 1;
if(P31==0) key_io=key_io + 2;
if(P30==0) key_io=key_io + 3;
return key_io;
}
检测列的时候,给定义的变量key_io在不同列的情况下赋值基础值,如第一列是4,第二列是8等,是应为他们列的第一个键值就是这个,并没有其他的特殊含义。接着进行行检测时,每到哪一行我们就在key_io加多少,像第0行加0,低一行加1,完美实现了对key_io变量赋值键值。最后返回获取的键值key_io。
在知道怎么获取的键值后,我们接下来要做的工作就是需要判断按键按下,长按和弹起这几种状态,这里有两个方法,就是之前文章提到的状态机法和三行按键法。
1.状态机法
首先我们需要宏定义三个变量,代表着是三个状态,无按键状态,按下状态和弹起状态
可以看到在Switch循环中,有三个if语句分别判断按键的状态,用来执行不同的语句。其中 key_state是一个静态变量,保存每次按键的状态。此外还需要用到key_io, key_return这两个变量分别来存取IO的状态和返回的键值。在确定好按键的按下和弹起后,返回键值key_return。需要注意的是这里的两个key_io都是局部变量,与上面的获取键值函数没有关系,在后面的语句中也能看到上面这个函数的键值key_io作为函数的返回值已经赋值给了下面这个函数的键值key_io。
#define KEY_NO 0 //无按键状态,用于判断是否按下
#define KEY_DOWN 1 //有按键按下状态,判断是否为抖动
#define KEY_UP 2 //等待松手状态,判断是否弹起
u8 vKBD_Read_State()
{
static u8 key_state = 0; // 定义key_state为静态变量,用于保存每次按键的状态
u8 key_io = 0, key_return = 0; // key_io:读取的IO状态, key_return:返回的键值;
key_io = vKBD_KeyIO();
switch(key_state)
{
case KEY_NO: //无按键状态,用于判断是否按下
if(key_io) key_state = KEY_DOWN;
break;
case KEY_DOWN: //有按键按下状态,判断是否为抖动
if(key_io)
{
key_state = KEY_UP;
key_return = key_io;
}
else
key_state = KEY_NO;
break;
case KEY_UP: //等待松手状态,判断是否弹起
if(key_io==0) key_state = KEY_NO;
break;
}
return key_return;
}
2.三行按键法
这段代码和上面的状态机法实现效果一模一样,但是代码数量减少了很多,有利于比赛中的应用,但是内容可能有点抽象,便不再讲述。
unsigned char Trg_KBD;
unsigned char Cont_KBD;
void vKBD_Read_ThreeLine()
{
unsigned char ReadData = vKBD_KeyIO_ThreeLine() ^ 0xff;
Trg_KBD = ReadData & (ReadData ^ Cont_KBD);
Cont_KBD = ReadData;
}