1.实现功能
- 矩阵键盘检测
- 设定六位密码,初试密码为123456
- 更改密码
- LCD1602显示
- 密码删除退格
- 中断实现蜂鸣器
- LED灯闪烁
功能非常简陋,制作时间很赶,所以质量一般。
2.逻辑流程图
3. Proteus原理图仿真
4.代码实现
矩阵键盘
#include <REGX52.H>
#include "Delay1ms.h"
#define key P1
/**
* @brief 先行扫描,再列扫描
*/
unsigned char MatrixKey(){
unsigned char keyValue[16] = {1,2,3,'A',
4,5,6,'B',
7,8,9,'C',
'*',0,'#','D'};
unsigned char num = 100;
key = 0xF0;
if(key != 0xF0){
Delay1ms(10); //延时抖消
if(key != 0xF0){ //check again
key = 0xF0;
switch(key) //先确定行
{
case 0x70: num=1; break; //第一行
case 0xb0: num=5; break; //第二行
case 0xd0: num=9; break; //第三行
case 0xe0: num=13; break; //第四行
}
key = 0x0F; //对应IO口拉高,以便检测
switch(key) //再确定列
{
case 0x07: num=num+0;while(key != 0x0F){}; break; //第一列
case 0x0b: num=num+1;while(key != 0x0F){}; break; //第二列
case 0x0d: num=num+2;while(key != 0x0F){}; break; //第三列
case 0x0e: num=num+3;while(key != 0x0F){}; break; //第四列
}
}
}
if(num == 100)
return 100;
else
return keyValue[num-1];
}
矩阵键盘的检测行列检测实现功能不赘述,相信各位大佬已经讲得很清楚明白了。
值得注意的是,检测键盘时要松手后再返回键值,所以最后列检测时加一个while循环,当未松手时就进入死循环不返回。
LCD部分
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
主函数检测密码部分
void main(){
Timer0Init();
LCD_Init();
led = 0;
while(1){
getkeyNum = MatrixKey();
//如果有按键输入
if(getkeyNum != 100){
//输入密码成功后修改密码
if(open == 't'){
resetPW(getkeyNum);
open = 'f';
continue;
}
//如果按下删除键'B'
if(getkeyNum == 'B'){
i--;
LCD_ShowChar(1,i,' ');
continue;
}
if(i<=6){
//输出按键值
if(getkeyNum < 10 )
LCD_ShowChar(1,i,getkeyNum+48);
else
LCD_ShowChar(1,i,getkeyNum);
pw[i-1] = getkeyNum; //存入按键值
i++;
}else{
if(getkeyNum == 'D'){ //如果密码相等,显示开锁成功
if(pw[0]==passWord[0]&& pw[1]==passWord[1]&& pw[2]==passWord[2]&& pw[3]==passWord[3]&& pw[4]==passWord[4] && pw[5]==passWord[5]){
LCD_ShowString(2,1,"OPEN!");
led = 1; //点亮LED
open = 't';
startBeep(); //打开蜂鸣器
Delay999ms();
stopBeep(); //关闭蜂鸣器
led = 0;
LCD_ShowString(2,1," ");
}
//如果密码不符合,清空输入密码的显示屏
else{
LCD_ShowString(2,1,"Failed!");
Delay999ms();
LCD_ShowString(2,1," ");
}
//重置pw,重置输入密码显示位置
LCD_ShowString(1,1," ");
i=1;
}
}
}
}
}
修改密码部分
//重置密码
void resetPW(unsigned char getkeyNum){
if(getkeyNum == 'A'){
LCD_ShowString(2,1,"RESET");
LCD_ShowString(1,1," ");
i=1; //重置显示位置
m=0;
while(1){
//输入六次,若已经输入完成,显示SUCCESS
if(m>=6){
LCD_ShowString(2,1,"SUCCESS");
Delay999ms();
LCD_ShowString(2,1," ");
LCD_ShowString(1,1," ");
i=1;
break;
}
resetNum = MatrixKey();
//如果有按键输入
if(resetNum != 100){
//如果按下删除键'B'
if(resetNum == 'B'){
m--;
LCD_ShowChar(1,m+1,' ');
continue;
}
//输出按键值
if(resetNum < 10 )
LCD_ShowChar(1,m+1,resetNum+48);
else
LCD_ShowChar(1,m+1,resetNum);
passWord[m] = resetNum; m++; //修改密码
}
}
}
}
修改密码部分有瑕疵,且代码冗余比较高。
中断实现蜂鸣器
void Timer0Init(){
EA = 0; //关闭总中断
ET0 = 1; //允许T0中断
TMOD = 0x01; //定时器模式,T0使用工作方式1
TH0 = 0x18; //该初值使隔段时间产生一次中断
TL0 = 0xFC;
TR0 = 1; //打开T0定时器
}
//打开蜂鸣器
void startBeep(){
EA = 1;
}
//关闭蜂鸣器
void stopBeep(){
EA = 0;
}
void Timer0() interrupt 1 {
TH0 = 63628/256; //通过定时器控制蜂鸣器发声频率
TL0 = 63628%256;
beep=~beep; //反复切换高低电平。
}
定时器随便找的一个可以发声的频率。通过中断总开关EA控制蜂鸣器的打开与否,若使用多个中断,此方法不可用。
所有代码:
https://gitee.com/ClarenceXH/51MCU-electronic-coded-lock
5.效果展示
更改密码: