Bootstrap

51单片机实现矩阵键盘密码锁

实验目的

使用51单片机的矩阵键盘模块以及led1602显示屏,实现模拟密码锁。

实验现象

当程序烧录到单片机中后,led1602屏幕会显示文字。

第一行会显示单词“PASSWORD”,第二行显示4个0,表示我们要写入的四位密码,每位默认为0。

在这里插入图片描述

矩阵键盘前两行与第三行的前两个分别代表输入1-9与0,第三行第三个按钮表示删除,第三行第四个按钮表示确认。

依次按下第一行的前三个按钮后,屏幕显示输入“0123”。

在这里插入图片描述

按下确认键后,若输入的密码就是设置的密码(这里是0123),显示“SUCCESS”字样,如下图:

在这里插入图片描述

反之,当输入的密码错误的时候,显示“FAIL”字样,并在5秒后回到输入密码界面,如下图:

在这里插入图片描述

当按下删除键时,删除最近输入的值,例如当前输入内容是“1234”,那么在按下删除键后将显示“0123”,如下图:
在这里插入图片描述

在这里插入图片描述

除了上述功能,当已经输入了四位数的时候,单片机将不再继续接受数据;当单片机数据为四个0的时候,按下删除键后单片机将不再继续删除数字。

硬件

本实验使用到4*4矩阵键盘,其内部结构图如下:

在这里插入图片描述

我们可以将独立按键的原理应用到矩阵键盘上。当独立按键被按下后,若有一端接地,那么另一端也将被置于低电平。矩阵键盘同理,当键盘上某个按键被按下后,如果它有一端为低电平(相当于接地),那么另一端也会被置0。

由此可见,想要找到被按下的那个按钮,如要对每个按钮进行如下操作:“先将按钮的一端置低电平,再监测按钮的另一端是否也为低电平。如果是,那么被按下的就是这个按钮”。

由上图可知,矩阵键盘16个按钮用8根线串在一起,每一行的按钮或者每一列的按钮用一根线连在一起,大大降低了GPIO口占用。P10到P13分别将第四列到第一列的按钮绑在一起,P14到P17分别将第四行到第一行的按钮绑在一起。

查找被按下的按钮时,可以按以下方式查找:

  1. 将P1口全部置高电平

  2. 将第i列的GPIO口置低电平(开始时i等于1,GPIO口就是P13)

  3. 依次检查第一行到第四行的GPIO口电平情况(P17到P14),有低电平的话就是这个按钮被按下

  4. 扫描完这一列,没有低电平,说明这一列没有被按下的,i+1后执行第二步

  5. 如果扫描四列都没有,说明没有按下的按钮。

程序

首先这个程序要在跑起来后,不断地对键盘进行扫描,如果扫描到了被按下的按钮,就执行对应的操作。

因此应当有一个带有返回值的函数,这个函数用于返回被按下的按钮编号。主函数中循环执行这个函数,直到返回按钮的数值:

unsigned short scan();//扫描键盘,有按钮被按下就返回编号,否则返回0

int main(){
	unsigned short res = 0;
	while(1){
		res = scan();
		if(res != 0){
			//扫描到被按下的按钮,执行对应操作
		}
	}
}

扫描函数

上面的scan()函数就是执行扫描键盘的函数。在scan中,要实现对四列的扫描,要先初始化P1,再将要扫描的那一列的GPIO口置0,再看每一行的电平。

由于要扫描四列,因此我写了四个函数,在scan中依次执行这四个函数,并返回数值。

unsigned short scanColumn1();
unsigned short scanColumn2();
unsigned short scanColumn3();
unsigned short scanColumn4();
unsigned short scan(){
	unsigned short res = 0;
	res = scanColumn1();
	if (res != 0) return res;
	res = scanColumn2();
	if (res != 0) return res;
	res = scanColumn3();
	if (res != 0) return res;
	res = scanColumn4();
	if (res != 0) return res;
	return 0;
}
unsigned short scanColumn1(){
	P1 = 0xff;//初始化P1口
	P1_3 = 0;//第一列GPIO口置低电平
	if (P1_7==0){//第一行有没有变成0
    	delay(20);//消抖
		while(P1_7==0);
		delay(20);
		return 1;//返回按钮编号
	}
	else if (P1_6==0){//第二行有没有变成0
	 	delay(20);
		while(P1_6==0);
		delay(20);
		return 5;
	}
	else if (P1_5==0){//第三行有没有变成0
	 	delay(20);
		while(P1_5==0);
		delay(20);
		return 9;
	}
	else if (P1_4==0){//第四行有没有变成0
	 	delay(20);
		while(P1_4==0);
		delay(20);
		return 13;
	}
	return 0;
}

扫描第2-4列的函数与上面扫描第一行的大体相同,只是GPIO口与返回编号不一样而已。

主函数

上面写道当返回值不为0时,执行对应操作。现在返回值与操作的关系如下:

返回值操作
0,13,14,15,16
1输入1
2输入2
3输入3
4输入4
5输入5
6输入6
7输入7
8输入8
9输入9
10输入0
11输入删除
12确认

我们要使用一个初始值为0的变量表示当前输入的密码,当接收到输入0-9的指令时,将这个变量乘以10在加上0-9。

例如最开始是0000,按下1后,0*10+1=1,led显示0001;当按下删除时,将这个变量除以10,由于c语言整形做除法舍去小数点,因此可实现删除最近输入的效果:1234,按下删除后1234/10=123,显示0123。

除了要有一个变量表示密码,还要有一个初始值为0的变量表示已输入长度,每输入一位密码,这个变量值加1,当值为4时不再继续接受新的密码数字,当值为0是不支持删除操作。

当按下12时,做一个简单的if-else判断,判断密码变量的值是否等于0-9999中的值(代码中设置),是的话打印SUCCESS,不是的话先打印FAIL,停滞几秒后程序打印刚刚输入的密码,等待重新输入。

代码

lcd1602显示屏的代码是我在b站找到的现成代码,不是我自己写的,就不放出了,只知道是在lcd1602显示屏上打印变量的值即可。

main.c

#include <REGX52.H>
#include "LCD1602.h"
#include "matrixKeyboard.h"
#include "Delay.h"
void main(){
	unsigned short _code,_len,a;//code属于keil关键字,不可用code当变量名!
	_code = 0;//密码变量
	_len = 0;//长度变量
	LCD_Init();
	LCD_ShowString(1,1,"PASSSWORD");
	LCD_ShowNum(2,1,_code,4);
	while(1){
		a = scan();//循环扫描,取编号
		if (a>0&&a<=10){//按下输入0-9,且已输入密码长度小于4
		if (_len<4){
			if (a == 10) a = 0;
			_len++;
			_code = _code * 10+a;
			LCD_ShowNum(2,1,_code,4);
		}	
		}
		else if(a == 11){//按下删除且当前密码长度大于0
		if (_len>0){
			_len--;
			_code = _code / 10;
			LCD_ShowNum(2,1,_code,4);
		}
		}
		else if(a == 12){//确认
			if (_code == 123) LCD_ShowString(2,1,"SUCCESS");//密码正确,这里是123
			else{//密码错误
				LCD_ShowString(2,1,"FAIL");//错误信息,延时后打印刚刚输入的密码
				delay(5000);
				LCD_ShowString(2,1,"      ");
				LCD_ShowNum(2,1,_code,4);
			}
		}
	}
}

matrixKeyboard.h

#ifndef __MATRIXKEYBOARD_H__
#define __MATRIXKEYBOARD_H__
#include <REGX52.H>
#include "Delay.h"
unsigned short scanColumn1();
unsigned short scanColumn2();
unsigned short scanColumn3();
unsigned short scanColumn4();
unsigned short scan();
#endif

matrixKeyboard.c

#include "matrixKeyboard.h"
unsigned short scanColumn1(){
	 P1 = 0xff;//P1初始化
	 P1_3 = 0;//相应列的GPIO置低电平
	 if (P1_7==0){//看哪个是低电平
	 	delay(20);
		while(P1_7==0);
		delay(20);
		return 1;
	 }
	 else if (P1_6==0){
	 	delay(20);
		while(P1_6==0);
		delay(20);
		return 5;
	 }
	 else if (P1_5==0){
	 	delay(20);
		while(P1_5==0);
		delay(20);
		return 9;
	 }
	 else if (P1_4==0){
	 	delay(20);
		while(P1_4==0);
		delay(20);
		return 13;
	 }
	 return 0;
}
unsigned short scanColumn2(){
	P1 = 0xff;
	 P1_2 = 0;
	 if (P1_7==0){
	 	delay(20);
		while(P1_7==0);
		delay(20);
		return 2;
	 }
	 else if (P1_6==0){
	 	delay(20);
		while(P1_6==0);
		delay(20);
		return 6;
	 }
	 else if (P1_5==0){
	 	delay(20);
		while(P1_5==0);
		delay(20);
		return 10;
	 }
	 else if (P1_4==0){
	 	delay(20);
		while(P1_4==0);
		delay(20);
		return 14;
	 }
	return 0;
}
unsigned short scanColumn3(){
	P1 = 0xff;
	 P1_1 = 0;
	 if (P1_7==0){
	 	delay(20);
		while(P1_7==0);
		delay(20);
		return 3;
	 }
	 else if (P1_6==0){
	 	delay(20);
		while(P1_6==0);
		delay(20);
		return 7;
	 }
	 else if (P1_5==0){
	 	delay(20);
		while(P1_5==0);
		delay(20);
		return 11;
	 }
	 else if (P1_4==0){
	 	delay(20);
		while(P1_4==0);
		delay(20);
		return 15;
	 }
	 return 0;
}
unsigned short scanColumn4(){
	P1 = 0xff;
	 P1_0 = 0;
	 if (P1_7==0){
	 	delay(20);
		while(P1_7==0);
		delay(20);
		return 4;
	 }
	 else if (P1_6==0){
	 	delay(20);
		while(P1_6==0);
		delay(20);
		return 8;
	 }
	 else if (P1_5==0){
	 	delay(20);
		while(P1_5==0);
		delay(20);
		return 12;
	 }
	 else if (P1_4==0){
	 	delay(20);
		while(P1_4==0);
		delay(20);
		return 16;
	 }
	 return 0;
}
unsigned short scan(){	
	unsigned short res = 0;
	res = scanColumn1();
	if (res != 0) return res;
	res = scanColumn2();
	if (res != 0) return res;
	res = scanColumn3();
	if (res != 0) return res;
	res = scanColumn4();
	if (res != 0) return res;
	return 0; 
}

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__
#include "intrins.h"
void delay(int ms);
#endif

Delay.c

#include "Delay.h"
void Delay(int ms)		//@11.0592MHz
{
	unsigned char i, j;
	while(ms--){
		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}

}

其他

"code"属于keil关键字,不可取做变量名。

;