关键词:矩阵按键;矩阵按键的扫描原理与基本应用;
一、知识点:
1.独立按键和矩阵键盘的基本原理
上图:一端接地,一端接单片机IO口,IO口处配有上拉电阻。当按键没有被按下时,中间短路,输出高电平;当按键被按下时,电路连通,输出低电平
下图:两端均接IO口,是否输出低/高电平,不仅取决于按键是否按下,还取决于输入端是1/0。
2.HC573锁存器电路部分
二、实验
实验目的:
实验步骤:
S1:编写HC573锁存器信道选择函数
void SelectHC573(unsigned char channel)
{
switch(channel)
{
case 4:P2=(P2&0x1F)|0x80;break;
case 5:P2=(P2&0x1F)|0xA0;break;
case 6:P2=(P2&0x1F)|0xC0;break;
case 7:P2=(P2&0x1F)|0xE0;break;
}
}
S2:编写数码管显示模块
void DisplayKeyNum(unsigned char value)
{
SelectHC573(6);//6锁存器控制COM端
P0=0x01;//选中第一个数码管
SelectHC573(7);//控制数码管的数字显示
P0=value;
}
S3:想编写矩阵键盘扫描模块,需要逐行逐列扫描,用到R1-R4,C1-C4,故先进行位声明
1.行端口位声明
sbit R1=P3^0; sbit R2=P3^1; sbit R3=P3^2; sbit R4=P3^3;//对行的端口进行位声明
2. 列端口位声明
sbit C4=P3^4; sbit C3=P3^5; sbit C2=P4^2; sbit C1=P4^4;//采用新版开发板的接口,具体实操看电路图
3. but,调试出现了错误
——>查看头文件,发现没有P4这一个特殊功能寄存器(sfr),需要自己再进行定义声明
sfr P4=0xC0;
S4:编写矩阵键盘扫描模块,先扫描第一行
思路:
1.行置值,列初始化
2.判断C1是否被按下,搭建小if判断
3.小if判断中,消抖->按键按下空循环->KeyNum赋值标注按键被按下->数码管显示被按下的值
4.再依次判断C2-C4
代码:
unsigned char KeyNum=0;
void ScanKeysMulti(void)
{
R1=0;
R2=R3=R4=1;//扫描第一行
C1=C2=C3=C4=1;//初始化列
if(C1==0)//检测R1&C1处的按键是否被按下
{
Delay(100);//消抖
while(C1==0);
KeyNum=0;//当按键松下后,跳出空循环,标注该按键被按下
DisplayKeyNum(SMG_duanma[KeyNum]);//数码管显示按下的是哪个键,KeyNum表示是第几个数, 还需要段码表说明是哪几个端口打开
}
else if(C2==0)
{
Delay(100);
while(C2==0);
KeyNum=1;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C3==0)
{
Delay(100);
while(C3==0);
KeyNum=2;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C4==0)
{
Delay(100);
while(C4==0);
KeyNum=3;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
}
S5:沿用上述模块,扫描剩余行
S6:再在main中调用扫描矩阵按键
实验现象:每按下矩阵键盘中的一个键并松开,数码管就会显示相应键值
完整代码:(Delay函数不必需)
#include <REGX52.H>
sfr P4=0xC0;//定义P4口地址
sbit R1=P3^0;
sbit R2=P3^1;
sbit R3=P3^2;
sbit R4=P3^3;//对行的端口进行位声明
sbit C4=P3^4;
sbit C3=P3^5;
sbit C2=P4^2;
sbit C1=P4^4;//采用新版开发板的接口,具体实操看电路图
unsigned char code SMG_duanma[18]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E,0xBF,0xFF};
//数码管0-9,A-F,-,空白(b,d用小写表示)
void SelectHC573(unsigned char channel)
{
switch(channel)
{
case 4:P2=(P2&0x1F)|0x80;break;
case 5:P2=(P2&0x1F)|0xA0;break;
case 6:P2=(P2&0x1F)|0xC0;break;
case 7:P2=(P2&0x1F)|0xE0;break;
}
}
void DisplayKeyNum(unsigned char value)
{
SelectHC573(6);//6锁存器控制COM端
P0=0x01;//选中第一个数码管
SelectHC573(7);//控制数码管的数字显示
P0=value;
}
void Delay(unsigned char t)
{
while(t--);
}
unsigned char KeyNum=0;
void ScanKeysMulti(void)
{
R1=0;
R2=R3=R4=1;//扫描第一行
C1=C2=C3=C4=1;//初始化列
if(C1==0)//检测R1&C1处的按键是否被按下
{
Delay(100);//消抖
while(C1==0);
KeyNum=0;//当按键松下后,跳出空循环,标注该按键被按下
DisplayKeyNum(SMG_duanma[KeyNum]);//数码管显示按下的是哪个键,KeyNum表示是第几个数,还需要段码表说明是哪几个端口打开
}
else if(C2==0)
{
Delay(100);
while(C2==0);
KeyNum=1;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C3==0)
{
Delay(100);
while(C3==0);
KeyNum=2;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C4==0)
{
Delay(100);
while(C4==0);
KeyNum=3;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
R2=0;
R1=R3=R4=1;//扫描第二行
C1=C2=C3=C4=1;//初始化列
if(C1==0)//检测R1&C1处的按键是否被按下
{
Delay(100);//消抖
while(C1==0);
KeyNum=4;//当按键松下后,跳出空循环,标注该按键被按下
DisplayKeyNum(SMG_duanma[KeyNum]);//数码管显示按下的是哪个键,KeyNum表示是第几个数,还需要段码表说明是哪几个端口打开
}
else if(C2==0)
{
Delay(100);
while(C2==0);
KeyNum=5;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C3==0)
{
Delay(100);
while(C3==0);
KeyNum=6;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C4==0)
{
Delay(100);
while(C4==0);
KeyNum=7;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
R3=0;
R2=R1=R4=1;//扫描第三行
C1=C2=C3=C4=1;//初始化列
if(C1==0)//检测R1&C1处的按键是否被按下
{
Delay(100);//消抖
while(C1==0);
KeyNum=8;//当按键松下后,跳出空循环,标注该按键被按下
DisplayKeyNum(SMG_duanma[KeyNum]);//数码管显示按下的是哪个键,KeyNum表示是第几个数,还需要段码表说明是哪几个端口打开
}
else if(C2==0)
{
Delay(100);
while(C2==0);
KeyNum=9;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C3==0)
{
Delay(100);
while(C3==0);
KeyNum=10;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C4==0)
{
Delay(100);
while(C4==0);
KeyNum=11;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
R4=0;
R2=R3=R1=1;//扫描第四行
C1=C2=C3=C4=1;//初始化列
if(C1==0)//检测R1&C1处的按键是否被按下
{
Delay(100);//消抖
while(C1==0);
KeyNum=12;//当按键松下后,跳出空循环,标注该按键被按下
DisplayKeyNum(SMG_duanma[KeyNum]);//数码管显示按下的是哪个键,KeyNum表示是第几个数,还需要段码表说明是哪几个端口打开
}
else if(C2==0)
{
Delay(100);
while(C2==0);
KeyNum=13;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C3==0)
{
Delay(100);
while(C3==0);
KeyNum=14;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
else if(C4==0)
{
Delay(100);
while(C4==0);
KeyNum=15;
DisplayKeyNum(SMG_duanma[KeyNum]);
}
}
void main()
{
while(1)
{
ScanKeysMulti();
}
}
三、改进思考:
改进原因:
每一次行扫描都是复制粘贴大段代码,显得冗余
改进:
四、经验总结:
1.矩阵键盘扫描逐行逐列
2.先从小处编写,关键处写好后可以沿用
3.位声明、寄存器声明可自定义
4.与按键相关,想到抖动这一模块,想要显示按键按下的键码,则可用数码管或者LD1602进行显示(需唯一确定一个键码值KeyNum)
五、矩阵键盘拓展(电子密码锁)——>参考B站江科大老师视频
(使用普中A2开发板)P1为矩阵按键对应端口
矩阵键盘代码:
#include <REGX52.H>
//对于本.c文件,P1和P1_3是没有定义的,需要添加引用的头文件
#include "Delay.h"
unsigned char MatrixKey()
{
unsigned char KeyNum=0;
P1=0xFF;//全部口设为高电平,理解为初始化,都不扫描
P1_3=0;//先扫描列,在扫描行
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNum=1;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNum=5;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNum=9;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNum=13;}
P1=0xFF;
P1_2=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNum=2;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNum=6;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNum=10;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNum=14;}
P1=0xFF;
P1_1=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNum=3;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNum=7;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNum=11;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNum=15;}
P1=0xFF;
P1_0=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNum=4;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNum=8;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNum=12;}
if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNum=16;}
return KeyNum;
}
main代码:
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "MatrixKey.h"
unsigned char KeyNum=0;
unsigned int Password=0,count;//初始化均为0
unsigned int key=3792;//正确密码设置key
void main()
{
LCD_Init();
LCD_ShowString(1,1,"Password:");
while(1)
{
KeyNum=MatrixKey();
if(KeyNum)
{
if(KeyNum<=10)//当s1-s10按键按下
{
if(count<4)//用计数器保证密码输入4位后不变
{
Password*=10; //密码左移一位
Password+=KeyNum%10;//使得s10是0,获取一位密码
count++;
}
LCD_ShowNum(2,1,Password,4);//更新显示
}
if(KeyNum==11)//按下确认键,与正确密码做比较
{
if(Password==key)
{
LCD_ShowString(1,14,"Yes");
Password=0;
count=0;//密码和计次清零
Delay(1000);
LCD_ShowString(1,14," ");
LCD_ShowNum(2,1,Password,4);//更新显示
}
else
{
LCD_ShowString(1,14,"Err");
Password=0;
count=0;//如果没有更新显示,虽然内部已初始化,但仍显示之前的密码
Delay(1000);
LCD_ShowString(1,14," ");
LCD_ShowNum(2,1,Password,4);//更新显示
}
}
if(KeyNum==12)//当s12按键按下,为取消(全部清零)
{
Password=0;
count=0;
LCD_ShowNum(2,1,Password,4);
}
if(KeyNum==13)//当s13按键按下,为取消(逐个取消)
{
Password/=10;
count--;
LCD_ShowNum(2,1,Password,4);
}
}
}
}
感谢B站小蜜蜂老师的教程,本笔记资料及代码均来自教程,仅作为个人复习、整理和学习交流用。