前面我们学习了《8051单片机实战分析(以STC89C52RC为例) | 07 - 独立按键驱动》,但是在单片机系统中,若使用按键较多时如电子密码锁、电话机键盘等一般都至少有12到16个按键,通常采用矩阵式按键,即矩阵键盘。
1 矩阵键盘
矩阵键盘又称行列键盘,它是用四条I/O线作为行线,四条I/O线作为列线组成的键盘。在行线和列线的每个交叉点上设置一个按键。这样键盘上按键的个数就为4*4个。这种行列式键盘结构能有效地提高单片机系统中I/O口的利用率。
最常见的键盘布局如图所示。一般由16个按键组成,在单片机中正好可以用一个P1
实现16个按键功能,这也是在单片机系统中最常用的形式。
4*4矩阵键盘的电路如图所示:
工作原理:
当无按键闭合时,P10~P13
与P14~P17
之间开路。当有键闭合时,与闭合键相连的两条I/O口线之间短路。
判断有无按键按下的方法是:
① 设置列线P10~P13
为输入状态,从行线P14~P17
输出低电平,读入列线数据,若某一列线为低电平,则该列线上有键闭合。
② 行线轮流输出低电平,从列线P10~P13
读入数据,若有某一列为低电平,则对应行线上有键按下。
③ 综合 ① 与 ② 的结果,就可确定按键编号。但是键闭合一次只能进行一次键功能操作,因此须等到按键释放后,再进行键功能操作,否则按一次键,有可能会连续多次进行同样的键操作。
接下来我们实现一个通过按下矩阵键盘上的按键,然后在数码管上显示对应的数字!
按键对应的数码管显示数字定义如下:
S1-S4:0-3
S5-S8:4-7
S9-S12:8-B
S13-S16:C-F
2 原理图
① 矩阵键盘:
② 上拉电阻:
③ MCU原理图:
3 程序
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
#define GPIO_DIG P0
#define GPIO_KEY P1
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;
u8 KeyValue; //用来存放读取到的键值
u8 code LedChar[]={
0x3F, //"0"
0x06, //"1"
0x5B, //"2"
0x4F, //"3"
0x66, //"4"
0x6D, //"5"
0x7D, //"6"
0x07, //"7"
0x7F, //"8"
0x6F, //"9"
0x77, //"A"
0x7C, //"B"
0x39, //"C"
0x5E, //"D"
0x79, //"E"
0x71, //"F"
0xff, //全亮
0x00 //熄灭
};
/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,i=1时,大约延时10us
*******************************************************************************/
void delay(u16 i)
{
while(i--);
}
/*******************************************************************************
* 函 数 名 : KeyDown
* 函数功能 : 检测有按键按下并读取键值
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void KeyDown(void)
{
char timeout=0;
GPIO_KEY=0x0f;
//0x0f转化为为二进制为0000 1111,即矩阵按键的八个管脚,高位为低电平(0),低位为高电平(1)
if(GPIO_KEY!=0x0f)//读取按键是否按下
{
delay(1000);//延时10ms进行消抖
if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
{
/*对列进行测试(高位低电平,低位高电平)*/
GPIO_KEY=0x0f;
switch(GPIO_KEY)
{
case(0x07): KeyValue=0;break; //对应管脚高低电平0000 0111,第0列
case(0x0b): KeyValue=1;break; //对应管脚高低电平0000 1011,第1列
case(0x0d): KeyValue=2;break; //对应管脚高低电平0000 1101,第2列
case(0x0e): KeyValue=3;break; //对应管脚高低电平0000 1110,第3列
}
/*对行进行测试(低位高电平,高位低电平)*/
GPIO_KEY=0xf0;
switch(GPIO_KEY)
{
/*上一行对应的列号加上相应有规律的字号就等于按键号,可由原理图查看*/
case(0x70): KeyValue=KeyValue;break; //对应管脚高低电平0111 0000,第0行
case(0xb0): KeyValue=KeyValue+4;break; //对应管脚高低电平1011 0000,第1行
case(0xd0): KeyValue=KeyValue+8;break; //对应管脚高低电平1101 0000,第2行
case(0xe0): KeyValue=KeyValue+12;break; //对应管脚高低电平1110 0000,第0行
}
}
}
while((timeout<50)&&(GPIO_KEY!=0xf0)) //当按键按下的时间超过了50ms或者按键松开了就退出while循环
{
delay(100);
timeout++;
}
}
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
LSA=0; //给一个数码管提供位选
LSB=0;
LSC=0;
while(1)
{
KeyDown(); //按键判断函数
GPIO_DIG=LedChar[KeyValue]; //显示键值
}
}
简要分析:
① 程序定义一个全局变量KeyValue
,用于存放存放读取到的键值。使用宏定义用GPIO_DIG
表示P0
端口,用GPIO_KEY
表示P1
端口。
②main
函数中首先初始化数码管的位选,关于数码管部分,在《8051单片机实战分析(以STC89C52RC为例) | 05 - 静态数码管驱动》这篇博文中已有介绍,这里不再赘述。然后进入while(1)
大循环,执行按键判断函数KeyDown()
与显示键值GPIO_DIG=LedChar[KeyValue];
。
③ 在按键判断函数KeyDown()
中,首先对矩阵键盘的列进行测试,再按键消抖后判断那一列被按下,接着切换高四位与低四位的电平状态,进一步判断哪一行被按下,之后刷新键值。最后是通过while((timeout<50)&&(GPIO_KEY!=0xf0))
进行松手检测,变量timeout
的作用是给个50ms
的倒计时,不然如果按键一直不松开会一直处于死循环了,这会导致单片机就无法执行其他任务。
转载:https://blog.csdn.net/Neutionwei/article/details/117455613