【51单片机】【protues仿真】基于51单片机密码锁系统
目录
一、主要功能
二、使用步骤
三、硬件资源
四、软件设计
五、实验现象
一、主要功能
首次使用时输入:131420,对密码进行初始化,
当显示:initpassword,证明密码初始化完成,
此时的密码为:000000。
主要功能如下:
1、LCD1602液晶显示
2、矩阵按键
3、修改密码:在开锁状态下,再次输入正确的密码并按下 # 键,
此时听到两声提示,输入新的六位密码并按 D 键,再重复输入一
次新密码并按 D ,会听到两声提示音,表示重设密码成功,内部
保存新密码并存储。如两次输入的新密码不一样,则重设密码失败。
4、24C02有复位、掉电保存密码功能
5、若密码输入错误次数超过3次,蜂鸣器报警并且锁定键盘
6、当输入密码错误后,报警并锁定键盘3秒钟
二、使用步骤
基于51单片机的电子密码锁是一种结合数字控制与安全验证的智能门禁系统,支持6位数字密码输入,通过矩阵键盘或独立按键输入,LCD1602实时显示操作提示(如“输入密码”“密码错误”)。密码比对成功后触发继电器或电磁锁开。
三、硬件资源
1、51单片机核心模块
2、按键模块
3、DHT11温湿度传感器、MQ烟雾传感器模块
4、蜂鸣器模块
5、继电器模块
6、LCD1602显示模块
四、软件设计
//包含头文件
#include <REG51.h>
#include<intrins.h>
//宏定义
#define LCM_Data P0 //将P0口定义为LCM_Data
#define uchar unsigned char
#define uint unsigned int
//1602的控制脚
sbit lcd1602_rs=P2^5;
sbit lcd1602_rw=P2^6;
sbit lcd1602_en=P2^7;
sbit Scl=P3^4; //24C02串行时钟
sbit Sda=P3^5; //24C02串行数据
sbit ALAM = P2^1; //报警
sbit KEY = P3^6; //开锁
bit pass=0; //密码正确标志
bit ReInputEn=0; //重置输入允许标志
bit s3_keydown=0; //3秒按键标志位
bit key_disable=0; //锁定键盘标志
unsigned char countt0,second; //t0中断计数器,秒计数器
void Delay5Ms(void); //声明延时函数
unsigned char code a[]={0xFE,0xFD,0xFB,0xF7}; //控盘扫描控制表
//液晶显示数据数组
unsigned char code start_line[] = {"password: "};
unsigned char code name[] = {"===Coded Lock==="}; //显示名称
//unsigned char code Correct[] = {" correct "}; //输入正确
unsigned char code Error[] = {" error "}; //输入错误
//unsigned char code codepass[] = {" pass "};
unsigned char code LockOpen[] = {" open "}; //OPEN
unsigned char code SetNew[] = {"SetNewWordEnable"};
unsigned char code Input[] = {"input: "}; //INPUT
unsigned char code ResetOK[] = {"ResetPasswordOK "};
unsigned char code initword[] = {"Init password..."};
unsigned char code Er_try[] = {"error,try again!"};
unsigned char code again[] = {"input again "};
unsigned char InputData[6]; //输入密码暂存区
unsigned char CurrentPassword[6]={0,0,0,0,0,0}; //读取EEPROM密码暂存数组
unsigned char TempPassword[6];
unsigned char N=0; //密码输入位数记数
unsigned char ErrorCont; //错误次数计数
unsigned char CorrectCont; //正确输入计数
unsigned char ReInputCont; //重新输入计数
unsigned char code initpassword[6]={0,0,0,0,0,0}; //输入管理员密码后将密码初始为000000
unsigned char code adminpassword[6]={1,3,1,4,2,0}; //输入管理员密码后将密码初始为000000
//=====================5ms延时==============================
void Delay5Ms(void)
{
unsigned int TempCyc = 5552;
while(TempCyc--);
}
//===================400ms延时==============================
void Delay400Ms(void)
{
unsigned char TempCycA = 5;
unsigned int TempCycB;
while(TempCycA--)
{
TempCycB=7269;
while(TempCycB--);
}
}
//=============================================================================================
//================================24C02========================================================
//=============================================================================================
void mDelay(uint t) //延时
{
uchar i;
while(t--)
{
for(i=0;i<125;i++)
{;}
}
}
void Nop(void) //空操作
{
_nop_(); //仅作延时用一条语句大约1us
_nop_();
_nop_();
_nop_();
}
/*****24c02程序参照24c02时序图*****/
/*起始条件*/
void Start(void)
{
Sda=1;
Scl=1;
Nop();
Sda=0;
Nop();
}
/*停止条件*/
void Stop(void)
{
Sda=0;
Scl=1;
Nop();
Sda=1;
Nop();
}
/*应答位*/
void Ack(void)
{
Sda=0;
Nop();
Scl=1;
Nop();
Scl=0;
}
/*反向应答位*/
void NoAck(void)
{
Sda=1;
Nop();
Scl=1;
Nop();
Scl=0;
}
/*发送数据子程序,Data为要求发送的数据*/
void Send(uchar Data)
{
uchar BitCounter=8;
uchar temp;
do
{
temp=Data; //将待发送数据暂存temp
Scl=0;
Nop();
if((temp&0x80)==0x80) //将读到的数据&0x80
Sda=1;
else
Sda=0;
Scl=1;
temp=Data<<1; //数据左移
Data=temp; //数据左移后重新赋值Data
BitCounter--; //该变量减到0时,数据也就传送完成了
}
while(BitCounter); //判断是否传送完成
Scl=0;
}
/*读一字节的数据,并返回该字节值*/
uchar Read(void)
{
uchar temp=0;
uchar temp1=0;
uchar BitCounter=8;
Sda=1;
do
{
Scl=0;
Nop();
Scl=1;
Nop();
if(Sda) //数据位是否为1
temp=temp|0x01; //为1 temp的最低位为1(|0x01,就是将最低位变为1)
else //如果为0
temp=temp&0xfe; //temp最低位为0(&0xfe(11111110)最低位就是0)
if(BitCounter-1) //BitCounter减1后是否为真
{
temp1=temp<<1; //temp左移
temp=temp1;
}
BitCounter--; //BitCounter减到0时,数据就接收完了
}
while(BitCounter); //判断是否接收完成
return(temp);
}
void WrToROM(uchar Data[],uchar Address,uchar Num)
{
uchar i;
uchar *PData;
PData=Data;
for(i=0;i<Num;i++)
{
Start();
Send(0xa0);
Ack();
Send(Address+i);
Ack();
Send(*(PData+i));
Ack();
Stop();
mDelay(20);
}
}
void RdFromROM(uchar Data[],uchar Address,uchar Num)
{
uchar i;
uchar *PData;
PData=Data;
for(i=0;i<Num;i++)
{
Start();
Send(0xa0);
Ack();
Send(Address+i);
Ack();
Start();
Send(0xa1);
Ack();
*(PData+i)=Read();
Scl=0;
NoAck();
Stop();
}
}
//==================================================================================================
//=======================================LCD1602====================================================
//==================================================================================================
#define yi 0x80 //LCD第一行的初始位置,因为LCD1602字符地址首位D7恒定为1(100000000=80)
#define er 0x80+0x40 //LCD第二行初始位置(因为第二行第一个字符位置地址是0x40)
//----------------延时函数,后面经常调用----------------------
void delay(uint xms)//延时函数,有参函数
{
uint x,y;
for(x=xms;x>0;x--)
for(y=110;y>0;y--);
}
//--------------------------写指令---------------------------
void write_1602com(uchar com)//****液晶写入指令函数****
{
lcd1602_rs=0;//数据/指令选择置为指令
lcd1602_rw=0; //读写选择置为写
P0=com;//送入数据
delay(1);
lcd1602_en=1;//拉高使能端,为制造有效的下降沿做准备
delay(1);
lcd1602_en=0;//en由高变低,产生下降沿,液晶执行命令
}
//-------------------------写数据-----------------------------
void write_1602dat(uchar dat)//***液晶写入数据函数****
{
lcd1602_rs=1;//数据/指令选择置为数据
lcd1602_rw=0; //读写选择置为写
P0=dat;//送入数据
delay(1);
lcd1602_en=1; //en置高电平,为制造下降沿做准备
delay(1);
lcd1602_en=0; //en由高变低,产生下降沿,液晶执行命令
}
//-------------------------初始化-------------------------
void lcd_init(void)
{
write_1602com(0x38);//设置液晶工作模式,意思:16*2行显示,5*7点阵,8位数据
write_1602com(0x0c);//开显示不显示光标
write_1602com(0x06);//整屏不移动,光标自动右移
write_1602com(0x01);//清显示
}
//========================================================================================
//=========================================================================================
//==============将按键值编码为数值=========================
unsigned char coding(unsigned char m)
{
unsigned char k;
switch(m)
{
case (0x11): k=1;break;
case (0x21): k=2;break;
case (0x41): k=3;break;
case (0x81): k='A';break;
case (0x12): k=4;break;
case (0x22): k=5;break;
case (0x42): k=6;break;
case (0x82): k='B';break;
case (0x14): k=7;break;
case (0x24): k=8;break;
case (0x44): k=9;break;
case (0x84): k='C';break;
case (0x18): k='*';break;
case (0x28): k=0;break;
case (0x48): k='#';break;
case (0x88): k='D';break;
}
return(k);
}
unsigned char keynum(void)
{
unsigned char row,col,i;
P1=0xf0;//P1整组IO口赋值 11110000
if((P1&0xf0)!=0xf0)//判断P1口是否有变化
{
Delay5Ms();
Delay5Ms();//延时去抖
if((P1&0xf0)!=0xf0)//再次判断
{//
row=P1^0xf0; //
i=0;
P1=a[i]; //精确定位fe
while(i<4)
{
if((P1&0xf0)!=0xf0)
{
col=~(P1&0xff); //确定列线 11111110 00011111
break; //已定位后提前退出
}
else
{
i++;
P1=a[i];
}
}
}
else
{
return 0;
}
while((P1&0xf0)!=0xf0);
return (row|col); //行线与列线组合后返回
}
else return 0; //无键按下时返回0
}
//=======================一声提示音,表示有效输入========================
void OneAlam(void)
{
ALAM=0;
Delay5Ms();
ALAM=1;
}
//========================二声提示音,表示操作成功========================
void TwoAlam(void)
{
ALAM=0;
Delay5Ms();
ALAM=1;
Delay5Ms();
ALAM=0;
Delay5Ms();
ALAM=1;
}
//========================三声提示音,表示错误========================
void ThreeAlam(void)
{
ALAM=0;
Delay5Ms();
ALAM=1;
Delay5Ms();
ALAM=0;
Delay5Ms();
ALAM=1;
Delay5Ms();
ALAM=0;
Delay5Ms();
ALAM=1;
}
//=======================显示提示输入=========================
void DisplayChar(void)
{
unsigned char i;
if(pass==1)
{
//DisplayListChar(0,1,LockOpen);
write_1602com(er); //在二行开始显示
for(i=0;i<16;i++)
{
write_1602dat(LockOpen[i]); //显示open 开锁成功
}
}
else
{
if(N==0)
{
//DisplayListChar(0,1,Error);
write_1602com(er);
for(i=0;i<16;i++)
{
write_1602dat(Error[i]); //显示错误
}
}
else
{
//DisplayListChar(0,1,start_line);
write_1602com(er);
for(i=0;i<16;i++)
{
write_1602dat(start_line[i]);//显示开始输入
}
}
}
}
void Cancel(void)
{
unsigned char i;
unsigned char j;
//DisplayListChar(0, 1, start_line);
write_1602com(er);
for(j=0;j<16;j++)
{
write_1602dat(start_line[j]); //显示开机输入密码界面
}
TwoAlam(); //提示音
for(i=0;i<6;i++)
{
InputData[i]=0; //将输入密码清零
}
KEY=1; //关闭锁
ALAM=1; //报警关
pass=0; //密码正确标志清零
ReInputEn=0; //重置输入充许标志清零
ErrorCont=0; //密码错误输入次数清零
CorrectCont=0; //密码正确输入次数清零
ReInputCont=0; //重置密码输入次数清零
s3_keydown=0;
key_disable=0; //锁定键盘标志清零
N=0; //输入位数计数器清零
}
//==============================主函数===============================
void main(void)
{
unsigned char KEY,NUM;
unsigned char i,j;
P1=0xFF; //P1口复位
TMOD=0x11; //定义工作方式
TL0=0xB0;
TH0=0x3C; //定时器赋初值
EA=1; //打开中断总开关
ET0=1; //打开中断允许开关
TR0=0; //打开定时器开关
Delay400Ms(); //启动等待,等LCM讲入工作状态
lcd_init(); //LCD初始化
write_1602com(yi);//日历显示固定符号从第一行第0个位置之后开始显示
for(i=0;i<16;i++)
{
write_1602dat(name[i]);//向液晶屏写开机画面
}
write_1602com(er);
for(i=0;i<16;i++)
{
write_1602dat(start_line[i]);//写输入密码等待界面
}
write_1602com(er+9); //设置光标位置
write_1602com(0x0f); //设置光标为闪烁
Delay5Ms(); //延时片刻(可不要)
N=0; //初始化数据输入位数
while(1) //进入循环
{
if(key_disable==1) //锁定键盘标志为1时
Alam_KeyUnable(); //报警键盘锁
else
ALAM=1; //关报警
KEY=keynum(); //读按键的位置码
if(KEY!=0) //当有按键按下时
{
if(key_disable==1) //锁定键盘标志为1时
{
second=0; //秒清零
}
else //没有锁定键盘时
{
NUM=coding(KEY); //根据按键的位置将其编码,编码值赋值给NUM
{
switch(NUM) //判断按键值
{
case ('A'): ; break;
case ('B'): ; break;
case ('C'):
write_1602com(yi);//日历显示固定符号从第一行第0个位置之后开始显示
for(i=0;i<16;i++)
{
write_1602dat(name[i]);//向液晶屏写开机画面
}
if(N>=1) N--; OneAlam(); //按键提示音
//DisplayOneChar(6+N,1,'*');
for(j=N;j<16;j++)
{
write_1602com(er+6+j);
write_1602dat(' ');
}
for(j=0;j<N;j++)
{
write_1602com(er+6+j); //显示位数随输入增加而增加
write_1602dat('*'); //但不显示实际数字,用*代替
}
InputData[N]=N+4;
break; //ABC是无定义按键
case ('D'): ResetPassword(); break; //重新设置密码
case ('*'): Cancel(); break; //取消当前输入
case ('#'): Ensure(); break; //确认键,
default: //如果不是功能键按下时,就是数字键按下
{
//DisplayListChar(0,1,Input);
if(N<6) //当输入的密码少于6位时,接受输入并保存,大于6位时则无效。
{
write_1602com(er);
for(i=0;i<16;i++)
{
write_1602dat(Input[i]); //显示输入画面
}
OneAlam(); //按键提示音
for(j=0;j<=N;j++)
{
write_1602com(er+6+j); //显示位数随输入增加而增加
write_1602dat('*'); //但不显示实际数字,用*代替
}
InputData[N]=NUM; //将数字键的码赋值给InputData[]数组暂存
N++; //密码位数加
}
else //输入数据位数大于6后,忽略输入
{
N=6; //密码输入大于6位时,不接受输入
break;
}
}
}
}
}
}
}
}
五、实验现象
演示视频:
【51单片机-B036】【protues仿真】基于51单片机密码锁仿真