矩阵键盘是单片机外部设备中所使用的排布类似于矩阵的键盘组。矩阵式结构的键盘显然比直接法要复杂一些,识别也要复杂一些,列线通过电阻接正电源,并将行线所接的单片机的I/O口作为输出端,而列线所接的I/O口则作为输入。
矩阵键盘特点
矩阵键盘的编程是十分复杂的,但是矩阵键盘也节省IO口。而且还提高了I/O口利用率。
矩阵键盘构成与工作方式
图9-7为一个4x3的行列结构,可以构成12个键的键盘。如果使用4x4的行列结构,就能组成一个16键的键盘。很明显,在按键数量多的场合,矩阵键盘与独立式按键键盘相比可以节省很多的I/O口线。
矩阵键盘不仅在连接上比单独式按键复杂,它的按键识别方法也比单独式按键复杂。在矩阵键盘的软件接口程序中,常使用的按键识别方法有行扫描法和线反转法。这两种方法的基本思路是采用循环查循的方法,反复查询按键的状态,因此会大量占用MCU的时间,所以较好的方式也是采用状态机的方法来设计,尽量减少键盘查询过程对MCU的占用时间。
下面以图9-7为例,介绍采用行扫描法对矩阵键盘进行判别的思路。图9-7中,PD0、PD1、PD2为3根列线,作为键盘的输入口(工作于输入方式)。PD3、PD4、PD5、PD6为4根行线,工作于输出方式,由MCU(扫描)控制其输出的电平值。行扫描法也称为逐行扫描查询法,其按键识别的过程如下。
√将全部行线PD3-PD6置低电平输出,然后读PD0-PD2三根输入列线中
有无低电平出现。只要有低电平出现,则说明有键按下(实际编程时,还要考虑按键的消抖)。如读到的都是高电平,则表示无键按下。
√在确认有键按下后,需要进入确定具体哪一个键闭合的过程。其思路是:依次将行线置为低电平,并检测列线的输入(扫描),进而确认是具体的按键位置。如当PD5输出低电平时(PD3=1、PD4=1、PD5=0、PD6=1),测到PD1的输入为低电平(PD0=1、PD1=0、PD2=1),则可确认按键K3-2处于闭合状态。通过以上分析可以看出,MCU对矩阵键盘的按键识别,是采用扫描方式控制行线的输出和检测列线输入的信号相配合实现的。
√矩阵按键的识别仅仅是确认和定位了行和列的交叉点上的按键,接下来还要
考虑键盘的编码,即对各个按键进行编号。在软件中常通过计算的方法或查表的方法对按键进行具体的定义和编号。
在单片机嵌入式系统中,键盘扫描只是MCU的工作内容之一。MCU除了要检测键盘和处理键盘操作之外,还要进行其他事物的处理,因此,MCU如何响应键盘的输入需要在实际系统程序设计时认真考虑。
通常,完成键盘扫描和处理的程序是系统程序中的一个专用子程序,MCU调用该键盘扫描子程序对键盘进行扫描和处理的方式有三种:程序控制扫描、定时扫描和中断扫描。
√程序控制扫描方式。在主控程序中的适当位置调用键盘扫描程序,对键盘进
行读取和处理。
√定时扫描方式。在该方式中,要使用MCU的一个定时器,使其产生一个
10ms的定时中断,MCU响应定时中断,执行键盘扫描,当在连续两次中断中都读到相同的按
√键按下(间隔10ms作为消抖处理),MCU才执行相应的键处理程序中断
方式。使用中断方式时,键盘的硬件电路要做一定的改动,增加一个按键产生中断信号的输入线,当键盘有按键按下时,键盘硬件电路产生一个外部的中断信号,
MCU响应外部中断,进行键盘处理。
下面我们介绍基于状态机并采用定时键盘扫描的键盘处理系统的设计方法。
定时扫描方式的键盘接口程序
根据图9-7,下面的键盘接口函数read_keyboaed()完成了对4*3键盘的扫描识别和键盘的编码。编码键盘的定义使用define语句定义,键盘的形式类似电话和手机键盘,如图9-8所示。
#defineNo_key255
#defineK1_11
#defineK1_22
#defineK1_33
#defineK2_14
#defineK2_25
#defineK2_36
#defineK3_17
#defineK3_28
#defineK3_39
#defineK4_110
#defineK4_20
#defineK4_311
#defineKey_mask0b00000111
charread_keyboard()
{
staticcharkey_state=0,key_value,key_line;
charkey_return=No_key,i;
switch(key_state)
{
case0:
key_line=0b00001000;
for(i=1;i《=4;i++)//扫描键盘
PORTD=~key_line;//输出行线电平
PORTD=~key_line;//必须送2次!!!(注1key_value=Key_mask&PIND;//读列电平if(key_value==Key_mask)
key_line《《=1;//没有按键,继续扫描
else
{
key_state++;//有按键,停止扫描
break;//转消抖确认状态
}
}
break;case1:
if(key_value==(Key_mask&PIND))//再次读列电平,{
switch(key_line|key_value)//与状态0的相同,确认按键{//键盘编码,返回编码值case0b00001110:key_return=K1_1;break;
case0b00001101:
key_return=K1_2;break;
case0b00001011:
key_return=K1_3;break;
case0b00010110:
key_return=K2_1;break;
case0b00010101:key_return=K2_2;
break;
case0b00010011:
key_return=K2_3;break;
case0b00100110:
key_return=K3_1;break;
case0b00100101:
key_return=K3_2;break;
case0b00100011:
key_return=K3_3;break;
case0b01000110:
key_return=K4_1;
break;
case0b01000101:
key_return=K4_2;
break;
case0b01000011:
key_return=K4_3;
break;}
key_state++;//转入等待按键释放状态
}
else
key_state--;//两次列电平不同返回状态0,(消抖处理)
break;
case2://等待按键释放状态
PORTD=0b00000111;//行线全部输出低电平
PORTD=0b00000111;//重复送一次
if((Key_mask&PIND)==Key_mask)
key_state=0;//列线全部为高电平返回状态0
break;
}
returnkey_return;
}
系统主程序应每隔10ms调用该键盘接口函数read_keyboaed(),函数返回值为255时表示无按键按下。检测和确认按键按下时,函数返回值为0到11之间的一个,该返回值已经是经过了键盘编码的值。
键盘接口函数read_keyboaed()是基于状态机实现的,将键盘扫描处理过程化分成三个状态,每个状态的功能为:
√状态0,键盘扫描检测。控制PD3-PD6,4根行线逐行输出低电平,对键盘
进行扫描检测。一旦检测到有键按下(key_value),立即停止键盘的扫描,状态转换到状态1。注意此时变量key_value中保存着读到的列线输入值,而且该行线低电平的输出是保持不变的。
√状态1,消抖处理和键盘编码。再次检测键盘列线的输入,并与状态0时的
key_value比较,不相等则返回状态0,实现了消抖处理。相等则确认该键的输入,进行键盘编码和设置函数的返回值,状态转化到状态2。
√状态2,等待按键释放。控制PD3-PD6,4根行线全部输出低电平,检测3
根列线输入全部为高电平(无按键按下)时状态返回到状态0。
读者在阅读该段程序时,请注意key_mask、key_value、key_line的作和用它们不仅与键盘的硬件连接有关系,同时还要注意他们在程序中是如何使用的,其值的保存等等。
通过这个例子也可以看出,在硬件设计的过程中,也需要认真考虑软件的写。比如,你也可以将4根键盘行线与PD0、PD2、PD3、PD6连接,列线使用PD1、PD4、PD5,从硬件的角度看是完全可以的,但会给软件编写造成很多麻烦。所以,一个好的嵌入式系统工程师必须同时具备良好的软件设计编写能力和硬件设计能力。
实际上read_keyboaed()还是一个比较简单的键盘接口函数,还不能处理类似多键按下的识别、按键“连发”等功能。但当你真正掌握了基本键盘接口的设计思想和方法后,就可以在这个简单的键盘接口函数基础上,设计出功能更加完善的键盘系统了。
(注1)当AVR的I/O口处于输入方式工作时,其对应的PINx就是外部引脚上的实际电平。为了防止读入PINx时数据的不稳定和不确定,(例如在读的期间,正好引脚电平发生改变),AVR的I/O输入端到总线之间加入一个同步锁存器,用以保证读入数据的稳定和确定。同步锁存器在系统I/O时钟的作用下,经过1/2-3/2个系统时钟周期,将引脚电平锁定,提供MCU读取。也就是说外部引脚的电平变化要经过1/2-3/2个时钟周期才能真正的被读到,见图9-9。
因此,当编写程序读取AVR的I/O口PINx电平值时应注意:
当I/O口从输出方式改成输入方式后,应延时一个CLK再读。
当I/O外部引脚电平改变后,也要延时一个CLK后再读取。
在本例的键盘扫描程序中,根据扫描条件,首先改变行线输出的电平,如果按键按下的话,那么对应点的列线(输入口)电平也马上改变了,此时需要延时一个CLK后读列线的输入电平值才是正确的。程序中采用输出2次相同的行电平的方式,第2次输出的实际作用是起到延时的作用(其实相当于2个NOP)。当然,也可以输出1次行电平,在读I/O口时采用读2次的方式,只要满足规定的延时时间就可以的。
矩阵键盘的原理
其中矩阵键盘是一种比较常用的方法。矩阵键盘的电路图如下:
矩阵式键盘由行线和列线组成,按键位于行、列的交叉点上。当键被按下时,其交点的行线和列线接通,相应的行线或列线上的电平发生变化,单片机通过检测行或列线上的电平变化可以确定哪个按键被按下。
矩阵键盘不仅在连接上比单独式按键复杂,它的按键识别方法也比单独式按键复杂。矩阵键盘的检测方法有多种,常见的有:逐点扫描法、逐行扫描法、全局扫描法。在本实例中我们采用逐行扫描法来实现按键检测,其中PD0-PD3作为列线,PD4-PD7作为行线。识别过程如下:
1、判断键盘中是否有键按下。设置所有行线为输出口,并输出低电平;设置列线为输入口,读取列线上的电平状态,只要有一列的电平为低,就表示有按键按下,并且被按下的键位于电平为低的列线与4跟行线相交叉的4个按键中,若所有列线都为高电平,表示没有按键按下;
2、判断被按下按键所在的位置。在确认有键按下后(进行按键消抖处理后),接下来就是确定具体哪个案件被按下,方法是:依次将每根行线设置为输出口,并输出低电平(同时剩余行线输出高电平),然后逐列检查每根列线的电平状态,若某列为低电平,则该列线与设置为输出低电平的行线交叉处的按键就是被按下的按键。
3、按键位置确定后,接下来就要给矩阵键盘中的每个按键进行编号,也就是进行按键编码,程序设计中常用计算法和查表法两种方式对按键进行编码,本实例采用计算法编码。
从上面的电路图中我们可以看到,键盘的所有行线和列线都接了上拉电阻,这是为了确保在没有按键按下的时候,I/O口的电平状态始终为高电平,从而消除外界干扰。
对于AVR单片机来说,我们已经知道在I/O口输入状态下,可以使能其内部上拉电阻,所以上面电路图中连接4根列线的上拉电阻可以不用,直接使能内部上拉电阻即可。
评论
查看更多