1、矩阵键盘的介绍
在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式 采用逐行或逐列的“扫描”,就可以读出任何位置按键的状态。
扫描的概念:
- 数码管扫描(输出扫描)原理:显示第1位→显示第2位→显示第3位→……,然后快速循环这个过程,最终实现所有数码管同时显示的效果
- 矩阵键盘扫描(输入扫描) 原理:读取第1行(列)→读取第2行(列) →读取第3行(列) → ……,然后快速循环这个过程,最终实现所有按键同时检测的效果
- 以上两种扫描方式的共性:节省I/O口
比如这里的矩阵键盘:
若采用逐行扫描模式,先令P17、P16、P15、P14分别为0111,代表扫描第一行(即按键S1、S2、S3、S4),若能检测到P13、P12、P11、P10有0或低电平,则表示某按键被按下;检测完第一行后,令P17、P16、P15、P14分别为1011,即检测第二行,再检测P13、P12、P11、P10;令P17、P16、P15、P14分别为1101,即检测第三行,再检测P13、P12、P11、P10;令P17、P16、P15、P14分别为1110,即检测第四行,再检测P13、P12、P11、P10。
若采用逐列扫描模式,令P13、P12、P11、P10分别为0111,即检测第一列,再检测P17、P16、P15、P14;以此类推。
2、弱上拉概念理解
如下如所示:单片机内部某个引脚结构简图,若外部也是接入的高电平,则单片机读入高电平;若外部读入的是低电平,则强低电平把内部电源Vcc拉成低电平,单片机读入低电平。
P1、P2、P3都是弱上拉,P0是开漏输出,但外接上拉电阻,因此和其他几个端口都是同样的模式。
3、Debug本节汇总:
- 若添加了LCD1602.c函数后,转到相关子函数报错,可以保存程序退出,重新打开工程文件。
- 写法问题:在main函数里面定义变量要放在开头,放在中间系统会报错,变量没有定义。
4、代码:按下按键,松开后在第二行显示按下按键的键号
(1)主函数
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "Matrixkey.h"
void main()
{
unsigned char Keynum=0;
LCD_Init();
LCD_ShowString(1,1,"HelloWorld");
while(1)
{
Keynum=MatrixKey();
if(Keynum)
{
LCD_ShowNum(2,1,Keynum,2);
}
}
}
(2)矩阵键盘函数(Matrixkey.c):按行检索。
#include <REGX52.H>
#include "Delay.h"
unsigned char MatrixKey()
{
unsigned char keynumber=0;
P1=0xFF;
P1_7=0;
if(P1_3==0){Delay(20);while(P1_3==0);Delay(20);keynumber=1;}
if(P1_2==0){Delay(20);while(P1_2==0);Delay(20);keynumber=2;}
if(P1_1==0){Delay(20);while(P1_1==0);Delay(20);keynumber=3;}
if(P1_0==0){Delay(20);while(P1_0==0);Delay(20);keynumber=4;}
P1=0xFF;
P1_6=0;
if(P1_3==0){Delay(20);while(P1_3==0);Delay(20);keynumber=5;}
if(P1_2==0){Delay(20);while(P1_2==0);Delay(20);keynumber=6;}
if(P1_1==0){Delay(20);while(P1_1==0);Delay(20);keynumber=7;}
if(P1_0==0){Delay(20);while(P1_0==0);Delay(20);keynumber=8;}
P1=0xFF;
P1_5=0;
if(P1_3==0){Delay(20);while(P1_3==0);Delay(20);keynumber=9;}
if(P1_2==0){Delay(20);while(P1_2==0);Delay(20);keynumber=10;}
if(P1_1==0){Delay(20);while(P1_1==0);Delay(20);keynumber=11;}
if(P1_0==0){Delay(20);while(P1_0==0);Delay(20);keynumber=12;}
P1=0xFF;
P1_4=0;
if(P1_3==0){Delay(20);while(P1_3==0);Delay(20);keynumber=13;}
if(P1_2==0){Delay(20);while(P1_2==0);Delay(20);keynumber=14;}
if(P1_1==0){Delay(20);while(P1_1==0);Delay(20);keynumber=15;}
if(P1_0==0){Delay(20);while(P1_0==0);Delay(20);keynumber=16;}
return keynumber;
}
5、本节补充
(1)函数注释说明
/**
* @brief :
* @param :
* @retval:
*/
例如对矩阵键盘函数进行说明,如下所示:
/**
* @brief : 矩阵键盘读取键码
* @param : 无参数
* @retval: Keynumber:按下按键的键码值
如果按下按键不动,则程序停留在此函数处;松开后的一瞬间,返回按键键码值
*/
(2)部分问题分析:
<1>起初代码为:
void main()
{
unsigned char Keynum=0;
LCD_Init();
LCD_ShowString(1,1,"HelloWorld");
while(1)
{
Keynum=MatrixKey();
LCD_ShowNum(2,1,Keynum,2);
}
}
故障描述:无论如何按下按键,按何种按键,数码管显示始终为00;
分析原因:数码管闪烁太快,无法观察显示的键码,因此加入if判断语句,当接收到矩阵键盘函数返回的非0值时,则进入数码管显示函数显示具体键码值,返回值很快重置为0,但进入不到if语句里面的显示函数,因此,数码管不显示重置后的值,也就是00。除非再次有按键按下,才会更改数码管的显示数值。
void main()
{
unsigned char Keynum=0;
LCD_Init();
LCD_ShowString(1,1,"HelloWorld");
while(1)
{
Keynum=MatrixKey();
if(Keynum)
{
LCD_ShowNum(2,1,Keynum,2);
}
}
}
(3)省时小技巧
对于编程中的一些相似语句,可以通过封装来节省时间,提高效率。
具体操作为:Templates——>右击并选择Configure Templates——>添加范例名和范例具体内容
若想让光标显示在某处,可以用“|”来指定光标位置。
小项目:矩阵键盘密码锁:
S1~S10分别对应数字1~9和0,S11代表确认键,S12代表取消键
#include <REGX52.H>
#include "LCD1602.h"
#include "Delay.h"
#include "Matrixkey.h"
void main()
{
unsigned char Keynum=0;
unsigned int password=0;
unsigned char Count=0;
LCD_Init();
LCD_ShowString(1,1,"Password:");
while(1)
{
Keynum=MatrixKey();
if(Keynum)
{
if(Keynum<=10) //如果S1~S10按键被按下,即为输入密码
{
if(Count<4)
{
password=password*10; //密码左移一位
password+=Keynum%10; //获取一位密码
Count++; //计次加一
}
LCD_ShowNum(2,1,password,4); //更新显示,这里的函数值显示范围为0-65535,输入不能超过65535,否则会溢出
}
if(Keynum==11) //S11设置为确认键
{
if(password==2345) //如果密码等于正确密码
{
LCD_ShowString(1,14,"OK "); //显示OK,OK后面细节加了一个空格,主要是为了在输错密码显示ERR后,能覆盖之前的ERR。
password=0; //密码清0
Count=0; //计次清0
LCD_ShowNum(2,1,password,4); //更新显示,此举是为了输入密码确认后,将屏幕清零
}
else
{
LCD_ShowString(1,14,"ERR"); //显示ERR
password=0; //密码清0
Count=0; //计次清0
LCD_ShowNum(2,1,password,4); //更新显示
}
}
if(Keynum==12) //S12设置为取消键
{
password=0; //密码清0
Count=0; //计次清0
LCD_ShowNum(2,1,password,4); //更新显示
}
}
}
}