键盘接口.docx
《键盘接口.docx》由会员分享,可在线阅读,更多相关《键盘接口.docx(19页珍藏版)》请在冰豆网上搜索。
键盘接口
第二十六课 矩阵式键盘接口技术及编程
作者:
佚名 来源:
不详 录入:
Admin 更新时间:
2008-7-2716:
14:
08 点击数:
2
【字体:
】
1、矩阵式键盘的结构与工作原理
在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式,如图1所示。
在矩阵式键盘中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接。
这样,一个端口(如P1口)就可以构成4*4=16个按键,比之直接将端口线用于键盘多出了一倍,而且线数越多,区别越明显,比如再多加一条线就可以构成20键的键盘,而直接用端口线则只能多出一键(9键)。
由此可见,在需要的键数比较多时,采用矩阵法来做键盘是合理的。
矩阵式结构的键盘显然比直接法要复杂一些,识别也要复杂一些,上图中,列线通过电阻接正电源,并将行线所接的单片机的I/O口作为输出端,而列线所接的I/O口则作为输入。
这样,当按键没有按下时,所有的输出端都是高电平,代表无键按下。
行线输出是低电平,一旦有键按下,则输入线就会被拉低,这样,通过读入输入线的状态就可得知是否有键按下了。
具体的识别及编程方法如下所述。
2、矩阵式键盘的按键识别方法
确定矩阵式键盘上何键被按下介绍一种“行扫描法”。
行扫描法行扫描法又称为逐行(或列)扫描查询法,是一种最常用的按键识别方法,如上图所示键盘,介绍过程如下。
1、判断键盘中有无键按下将全部行线Y0-Y3置低电平,然后检测列线的状态。
只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平线与4根行线相交叉的4个按键之中。
若所有列线均为高电平,则键盘中无键按下。
2、判断闭合键所在的位置在确认有键按下后,即可进入确定具体闭合键的过程。
其方法是:
依次将行线置为低电平,即在置某根行线为低电平时,其它线为高电平。
在确定某根行线位置为低电平后,再逐行检测各列线的电平状态。
若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键。
下面给出一个具体的例子:
图仍如上所示。
8031单片机的P1口用作键盘I/O口,键盘的列线接到P1口的低4位,键盘的行线接到P1口的高4位。
列线P1.0-P1.3分别接有4个上拉电阻到正电源+5V,并把列线P1.0-P1.3设置为输入线,行线P1.4-P.17设置为输出线。
4根行线和4根列线形成16个相交点。
1、检测当前是否有键被按下。
检测的方法是P1.4-P1.7输出全“0”,读取P1.0-P1.3的状态,若P1.0-P1.3为全“1”,则无键闭合,否则有键闭合。
2、去除键抖动。
当检测到有键按下后,延时一段时间再做下一步的检测判断。
3、若有键被按下,应识别出是哪一个键闭合。
方法是对键盘的行线进行扫描。
P1.4-P1.7按下述4种组合依次输出:
P1.71110
P1.61101
P1.51011
P1.40111
在每组行输出时读取P1.0-P1.3,若全为“1”,则表示为“0”这一行没有键闭合,否则有键闭合。
由此得到闭合键的行值和列值,然后可采用计算法或查表法将闭合键的行值和列值转换成所定义的键值
4、为了保证键每闭合一次CPU仅作一次处理,必须却除键释放时的抖动。
键盘扫描程序:
从以上分析得到键盘扫描程序的流程图如图2所示。
程序如下
SCAN:
MOVP1,#0FH
MOVA,P1
ANLA,#0FH
CJNEA,#0FH,NEXT1
SJMPNEXT3
NEXT1:
ACALLD20MS
MOVA,#0EFH
NEXT2:
MOVR1,A
MOVP1,A
MOVA,P1
ANLA,#0FH
CJNEA,#0FH,KCODE;
MOVA,R1
SETBC
RLCA
JCNEXT2
NEXT3:
MOVR0,#00H
RET
KCODE:
MOVB,#0FBH
NEXT4:
RRCA
INCB
JCNEXT4
MOVA,R1
SWAPA
NEXT5:
RRCA
INCB
INCB
INCB
INCB
JCNEXT5
NEXT6:
MOVA,P1
ANLA,#0FH
CJNEA,#0FH,NEXT6
MOVR0,#0FFH
RET
键盘处理程序就作这么一个简单的介绍,实际上,键盘、显示处理是很复杂的,它往往占到一个应用程序的大部份代码,可见其重要性,但说到,这种复杂并不来自于单片机的本身,而是来自于操作者的习惯等等问题,因此,在编写键盘处理程序之前,最好先把它从逻辑上理清,然后用适当的算法表示出来,最后再去写代码,这样,才能快速有效地写好代码。
到本课为止,本站教程暂告一个段落!
感谢大家的关心和支持!
文件keyboard.h:
/*
按键扫描程序工作原理说明:
按键扫描采取行线扫描输出高电平列线扫描接收的方式进行工作
首先在一根行线上输出一个高电平,然后列线扫描接收,如果其
中有按键按下则按键所在列线上接收到高电平信号否则为低,从
而根据接收到高电平时行列编号可确定按键编号。
*/
#ifndef _KEYBOARD_H_
#define _KEYBOARD_H_
#include
#include "define.h"
#define KEYSUM 9///按键数量
#define HANGSUM 3///按键行数
#define LIESUM 3///按键列数
#define HANGSTART 0///按键行起始端口号
#define LIEEND 6///按键列末位端口号
#define KEYOUT PORTB///按键扫描高电平输出端口
#define KEYIN PINB///按键扫描输入端口
struct key
{
uchar keynum; ///按键编号
uchar keyconter; ///按键灵敏度
uchar keyburstmode; ///按键触发方式0为延时电平触发1为按下触发2为抬起触发
}; ///按键键值缓冲区
extern volatile uchar nowkeynum;///当前按键编号无按键按下时为0
/*!
* \brief 按键扫描程序
*
*读入按键键值并写入全局变量nowkeynum
*
* \return 按键键值
*/
uchar keyscan(void);
#endif
**********************************************************************
文件keyboard.c:
#include "keyboard.h"
/*按键记录电平计数缓冲区
每次扫描有按键按下记录缓冲区加1,直到超过按键灵敏度值。
如无按键按下则清零。
*/
uchar keytmp[KEYSUM + 1] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*按键记录缓冲区
当使用沿触发方式时记录按键状态
*/
uchar keytmpnow[KEYSUM + 1] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*按键记录历史缓冲区
当使用沿触发方式时记录上一次按键状态
*/
uchar keytmpold[KEYSUM + 1] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/*按键声明区
定义各个按键属性
*/
const struct key keyunit[KEYSUM + 1] =
{
0, 1, 0, ///
1, 2, 1, ///
2, 5, 0, ///
3, 5, 0, ///
4, 5, 0, ///
5, 5, 0, ///
6, 5, 0, ///
7, 5, 0, ///
8, 5, 0, ///
9, 5, 0, ///
};
uchar keyscan(void)
{
uchar loop1 = 0; ///行扫描循环控制
uchar loop2 = 0; ///列扫描循环控制
uchar keynumtmp = 0; ///按键编号缓存
uchar keyloopmasklie = 0; ///列扫描掩码
///列扫描掩码设定
keyloopmasklie |= (1 << LIEEND);
KEYOUT = 0x00;
for (loop1 = 0; loop1 < HANGSUM; loop1++)
{
///置行线
KEYOUT |= (1 << (loop1 + HANGSTART));
for (loop2 = 0; loop2 < LIESUM; loop2++)
{
keynumtmp = loop1 * LIESUM + loop2 + 1; ///计算出当前正在处理的按键编号
///判断按键的触发方式然后执行相应的判断程序
switch (keyunit[keynumtmp].keyburstmode)
{
///电平触发
case 0:
{
///如果有键按下相应键值计数器加1
if ((KEYIN &keyloopmasklie) !
= 0)
{
keytmp[keynumtmp]++;
///如果计数器计数超出灵敏度设定值有效按键键值赋值
if (keytmp[keynumtmp] >= keyunit[keynumtmp].keyconter)
{
nowkeynum = keynumtmp;
///计数器计数保持最大值
keytmp[keynumtmp] = keyunit[keynumtmp].keyconter;
return nowkeynum;
}
}
///没有按键按下相应按键计数寄存器清零
else
{
keytmp[keynumtmp] = 0;
}
}
break;
///按下触发
case 1:
///抬起触发
case 2:
{
///如果有键按下相应键值计数器加1
if ((KEYIN &keyloopmasklie) !
= 0)
{
keytmp[keynumtmp]++;
}
///没有按键按下相应按键计数寄存器清零
else
{
keytmp[keynumtmp] = 0;
}
///如果计数器计数超出灵敏度设定值按键键值赋高
if (keytmp[keynumtmp] >= keyunit[keynumtmp].keyconter)
{
///置当前按键电平状态为高
keytmpnow[keynumtmp] = HIGH;
///计数器计数保持最大值
keytmp[keynumtmp] = keyunit[keynumtmp].keyconter;
///按下触发
if (keyunit[keynumtmp].keyburstmode == 1)
{
///上升沿
if (keytmpold[keynumtmp] == LOW && keytmpnow[keynumtmp] == HIGH)
{
nowkeynum = keynumtmp;
keytmpold[keynumtmp] = keytmpnow[keynumtmp];
return nowkeynum;
}
}
}
else
{
///置当前按键电平状态为低
keytmpnow[keynumtmp] = LOW;
///抬起触发
if (keyunit[keynumtmp].keyburstmode == 2)
{
///下降沿
if (keytmpold[keynumtmp] == HIGH && keytmpnow[keynumtmp] == LOW)
{
nowkeynum = keynumtmp;
keytmpold[keynumtmp] = keytmpnow[keynumtmp];
return nowkeynum;
}
}
}
///按键记录推移
keytmpold[keynumtmp] = keytmpnow[keynumtmp];
}
break;
}
keyloopmasklie >>= 1;
}
///列扫描掩码设定
keyloopmasklie = 0;
keyloopmasklie |= (1 << LIEEND);
///复位行线到KL1
KEYOUT = 0x00;
}
nowkeynum = 0;
return nowkeynum;
}
*********************************************************************************
主程序中定义
volatile uchar nowkeynum = 0; ///当前按键编号无按键按下时为0
*********************************************************************************
程序说明:
本程序适用于3*3矩阵键盘。
输入,输出均使用PB口,也可以改为其他口,和其他行列数,更改
keyboard.h中的声明部分即可。
本按键扫描程序可选择电平触发或者沿触发方式,最后的按键键值存在变量nowkeynum中。
使用时在各位的原文件中包含上面的keyboard.h和keyboard.c文件并在主程序中定义
volatile uchar nowkeynum = 0;
然后就可以调用keyscan();读出键值了。
推荐使用定时器定时调用的方式来完成按键扫描操作。
10.5说明矩阵式键盘按键按下的识别原理(2010/08/2716:
26)
目录:
网商感悟
浏览字体:
大 中 小
第十章MCS-51与键盘、显示器的接口设计2010年06月08日
10.1为什么要排除按键的机械抖动?
打消按键的机械抖动的方式有哪多少种?
原理是什么?
答:
为了确保CPU对一次按键动作只确认一次按键有效,所以必需清除抖动,修理装备。
常采取软件来消除按键抖动,其基础思维是:
在第一次检测到有键按下时,该键所对应的行线为低点平,很久以后才发现这个人早已经操纵了我们的喜怒哀乐,手机游戏,执行一段延时10ms的子程序后,确认该行线电平是否仍为低点平,假如仍为低点平,则确以为该行确切有键按下。
当按键松开时,行线的低电平变为高电平,履行一段延时10ms的子程序后,检测该行线为高电平,解释按键确实已经松开。
10.3LED的静态显示方式与动态显示方式有何差别?
各有什么优毛病?
答:
详见书本P228-P230
10.5阐明矩阵式键盘按键按下的辨认原理
答:
详见书本P234
10.7键盘有哪三种工作方法?
它们各自的工作原理及特色是什么?
答:
详见书本P236-P237
10.9依据图10-14的电路,编写在6个LED显示器上轮流显示“1,2,3,4,资料2-博文预览,手机游戏,5,6”的显示程序.
MOVR2,#06H
MOVR0,#79H
MOVA,#01H
LOOP:
MOV@RO,A
INCA
INCR0
DJNZR2,LOOP
以下代码见P239接
DIR:
MOVR9,#79H
MOVR3,#01H
。
。
。
。
。
。
。
。
矩阵式键盘的按键识别方法
[日期:
2010-09-16][来源:
本站原创作者:
佚名][字体:
大中小](投递新闻)
在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式,如图1所示。
在矩阵式键盘中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接。
这样,一个端口(如P1口)就可以构成4*4=16个按键,比之直接将端口线用于键盘多出了一倍,而且线数越多,区别越明显,比如再多加一条线就可以构成20键的键盘,而直接用端口线则只能多出一键(9键)。
由此可见,在需要的键数比较多时,采用矩阵法来做键盘是合理的。
矩阵式结构的键盘显然比直接法要复杂一些,识别也要复杂一些,上图中,列线通过电阻接正电源,并将行线所接的单片机的I/O口作为输出端,而列线所接的I/O口则作为输入。
这样,当按键没有按下时,所有的输出端都是高电平,代表无键按下。
行线输出是低电平,一旦有键按下,则输入线就会被拉低,这样,通过读入输入线的状态就可得知是否有键按下了。
具体的识别及编程方法如下所述。
矩阵式键盘的按键识别方法
确定矩阵式键盘上何键被按下介绍一种“行扫描法”。
行扫描法行扫描法又称为逐行(或列)扫描查询法,是一种最常用的按键识别方法,如上图所示键盘,介绍过程如下。
判断键盘中有无键按下将全部行线Y0-Y3置低电平,然后检测列线的状态。
只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平线与4根行线相交叉的4个按键之中。
若所有列线均为高电平,则键盘中无键按下。
判断闭合键所在的位置在确认有键按下后,即可进入确定具体闭合键的过程。
其方法是:
依次将行线置为低电平,即在置某根行线为低电平时,其它线为高电平。
在确定某根行线位置为低电平后,再逐行检测各列线的电平状态。
若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键。
下面给出一个具体的例子:
8031单片机的P1口用作键盘I/O口,键盘的列线接到P1口的低4位,键盘的行线接到P1口的高4位。
列线P1.0-P1.3分别接有4个上拉电阻到正电源+5V,并把列线P1.0-P1.3设置为输入线,行线P1.4-P.17设置为输出线。
4根行线和4根列线形成16个相交点。
检测当前是否有键被按下。
检测的方法是P1.4-P1.7输出全“0”,读取P1.0-P1.3的状态,若P1.0-P1.3为全“1”,则无键闭合,否则有键闭合。
去除键抖动。
当检测到有键按下后,延时一段时间再做下一步的检测判断。
若有键被按下,应识别出是哪一个键闭合。
方法是对键盘的行线进行扫描。
P1.4-P1.7按下述4种组合依次输出:
P1.71110
P1.61101
P1.51011
P1.40111
实例程序:
ORG0000H
AJMPMAIN
ORG0030H
MAIN:
MOVDPTR,#TAB;将表头放入DPTR
LCALLKEY;调用键盘扫描程序
MOVCA,@A+DPTR;查表后将键值送入ACC
MOVP0,A;将Acc值送入P0口
CLRP2.1;开显示
LJMPMAIN;返回反复循环显示
KEY:
LCALLKS;调用检测按键子程序
JNZK1;有键按下继续
LCALLDELAY2;无键按调用延时去抖
AJMPKEY;返回继续检测按键
K1:
LCALLDELAY2
LCALLDELAY2;有键按下延时去抖动
LCALLKS;再调用检测按键程序
JNZK2;确认有按下进行下一步
AJMPKEY;无键按下返回继续检测
K2:
MOVR2,#0EFH;将扫描值送入R2暂存
MOVR4,#00H;将第一列值送入R4暂存
K3:
MOVP1,R2;将R2的值送入P1口
L6:
JBP1.0,L1;P1.0等于1跳转到L1
MOVA,#00H;将第一行值送入ACC
AJMPLK;跳转到键值处理程序
L1:
JBP1.1,L2;P1.1等于1跳转到L2
MOVA,#04H;将第二行的行值送入ACC
AJMPLK;跳转到键值理程序进行键值处理
L2:
JBP1.2,L3;P1.2