小蜜蜂51单片机之矩阵按键的扫描原理与基本应用

关键词:矩阵按键;矩阵按键的扫描原理与基本应用;

一、知识点:

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站小蜜蜂老师的教程,本笔记资料及代码均来自教程,仅作为个人复习、整理和学习交流用。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值