在单片机系统中,只要单片机有空余的时间来解码时,我们往往利用的是软件解码的形式,即“非编码键盘”来实现矩阵键盘。这样可以节约成本。下面我就对矩阵键盘在51单片机中的软件解码方式应用总结一下。
原理图:
工作原理:
扫描程序查询的内容如下:
&129; 查询是否有键按下。首先单片机向行扫描口P1.0~P1.3输出全为0的扫描码F0H,然后从列检测口P1.4~P1.7输入列检测信号,只要有一列信号不为1,即P1口不为F0H,则表示有键按下。接着要查出按下键所在的行、列位置。
&8218; 查询按下键所在的行、列位置。单片机将得到的信号取反,P1.4~P1.7口中为1的位置便是键所在位置。
接下来要确定键所在的行,需进行行逐行扫描。单片机首先使用P1.0口接地,P1.1~P1.7口为1,即向P1口发送扫描码FEH,接着输入列检测信号,若为全1,则表示不在第一行。然后使P1.1接地,其余为1,再读入列信号……。这样逐行发0扫描码,直到找到按键所在的行,将该行扫描码取反保留。若各行都扫描以后仍没有找到,则放弃扫描,认为是键的误动作。
&402; 对得到的行号和列号译码,得到键值。
&8222; 键的消抖处理。当用手按下一个键时,往往会出现所按键在闭合位置和断开位置之间跳几下才稳定到闭合状态的情况;在释放一个键时,也会出现类似的情况。这就是键抖动。抖动的持续时间不一,通常不会大于10 ms。若抖动问题不解决,就会引起对闭合键的多次读入。解决键抖动最方便的方法是:当发现有键按下后,不要立即进行逐行扫描,而是延时10 ms后再进行。由于键按下的时间会持续上百ms,延时后再扫描也不迟。
程序如下:
unsigned char key_scan(void) // 按键扫描函数
{
unsigned char sccode,recode;
P1=0xf0; // 发全0行扫描码,列线输入
if((P1&0xf0)!=0xf0) // 若有键按下
{
delay_ms(10); // 延时去抖
if((P1&0xf0)!=0xf0)
{
sccode=0xfe; // 逐行扫描初值
While((sccode&0x10)!=0)
{
P1=sccode; // 输出行扫描码
if((P1&0xf0)!=0xf0) // 本行有键按下
{
recode=(P1&0xf0)|0xf0;
return(( ~ sccode)+( ~ recode)); // 返回特征字节码
}
else
sccode=(sccode<<1)|0x01; // 行扫描码左移一位
}
}
}
return(0); // 无键按下,返回值为0
}
说明:
此按键扫描函数是很多书上介绍的按键扫描函数,从原来的角度来说是完全正确可行的。但是在我上次做《密码锁》时,发现用此函数比较麻烦,且总是存在一些问题。通过查阅大量书籍和实验,我写出了比较简单而且切实可行的按键扫描程序。
程序如下 :
/*****************************************************
函 数 名:uchar Key_scan(void)
功 能:4x4按键,按键扫描
说 明:对按键进行扫描,从而得到键编码
入口参数:无
返 回 值:有键按下返回:键编码=行扫描值+列扫描值;无按键返回0
*****************************************************/
uchar Key_scan(void)
{
uchar key; // 存放键编码
P1=0xf0; // 发全0行扫描码,列线输入
if((P1&0xf0)!=0xf0) // 若有键按下
{
delay_ms(10); // 延时去抖
P1=0xf0; // 取高 4 位值.即:列扫描值
key=P1&0xf0; // 存放键编码的高4位
P1=0x0f; // 取低 4 位值.即:行扫描值
key=(P1&0x0f)|key; // 低4位与高4位进行合并
if(key!=0xff)
{
return(key); // 有键按下,返回键编码
}
}
return(0); // 无键按下,返回0
}
/*****************************************************
函 数 名:uchar Key_switch(void)
功 能:按键转换程序
说 明:对按键码进行转换
入口参数:无
返 回 值:ASCII码
*****************************************************/
uchar Key_switch(void)
{
uchar key;
key=Key_scan();
switch(key)
{
case 0xee:return(49);break; /* "1"键 */
case 0xed:return(50);break; /* "2"键 */
case 0xeb:return(51);break; /* "3"键 */
case 0xe7:return(65);break; /* "A"键 */
case 0xde:return(52);break; /* "4"键 */
case 0xdd:return(53);break; /* "5"键 */
case 0xdb:return(54);break; /* "6"键 */
case 0xd7:return(66);break; /* "B"键 */
case 0xbe:return(55);break; /* "7"键 */
case 0xbd:return(56);break; /* "8"键 */
case 0xbb:return(57);break; /* "9"键 */
case 0xb7:return(67);break; /* "C"键 */
case 0x7e:return(42);break; /* "*"键 */
case 0x7d:return(48);break; /* "0"键 */
case 0x7b:return(35);break; /* "#"键 */
case 0x77:return(68);break; /* "D"键 */
case 0x00:return(00);break; /* "无"键 */
}
}
/*接盘按键说明:
--------------------------------------------------
| 1 | 2 | 3 | A |
- - - - - - - - - - - - -
| 4 | 5 | 6 | B |
- - - - - - - - - - - - -
| 7 | 8 | 9 | C |
- - - - - - - - - - - - -
| * | 0 | # | D |
--------------------------------------------------
说明:
如果需要更改按键值,只需改 Key_switch ()函数中对应的返回值即可。对于延时函数比较简单只需一个for循环即可,所以在这里就没写出来。因每个单片机的I/O的应用是不一样的,所以本人写的这个函数只能在51单片机系统下运行。如果要在别的单片机系统下,可能需要用第一种进行改进。
上一篇:比较AVR和ARM,谈谈相同与区别
下一篇:串口通讯实例