一、 设计要求(设计一个由单片机控制的电子密码锁) 1.能设定一组4位的数字开启密码(设定密码功能) 2.用LED小灯代替锁开启显示,输入密码正确,则小灯亮起(开锁功能) 3.如果3次密码错误,则进行鸣叫报警,并在1分钟之内不能再次输入(报警功能) 4.密码输入显示在数码管上,输入正确显示on,输入错误显示err(显示功能) 5.可在以上功能上扩展。
二、原理说明(包含流程图) 密码锁因为初始无密码,所以程序首先运行一个设置密码程序让用户设置一个1-4位的密码,输入密码中可回删。然后程序会进入一个循环输入密码的环节。密码设置完成后点击关闭键后,屏幕关闭。点击准备输入即可开始输入密码,输入密码完了点击确认键,屏幕会显示打开与否即on与err,显示错误次数的数码管也会实时显示错误次数。当输入次数达到三次后,将在1分钟内无法输入无法关闭且在屏幕显示倒计时同时蜂鸣器报警,其他时刻都可点击关闭键。当密码输入正确后,小灯亮,且可以点击修改密码键,让用户输入新的密码,然后确定。
三、电路设计 矩阵键盘(输入):接在P1口且用一个4与门,上拉电阻来通过中断方式来实现。 4位数码管(显示屏幕):位选接P3的0,1,6,7口,段选接在P2的0-6口,不需要点。 1位数码管(显示错误次数):通过上拉电阻接在P0的0-6口。 小灯(标志密码输入正确):接在P3的3口。 蜂鸣器(警报):接在P3的5口。
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
四、 仿真结果 设置密码: 输入密码 密码错误: 密码错误三次报警倒计时60s: 密码正确小灯亮: 修改密码; 密码修改成功: 五、 结果分析 首先初始化,然后让用户点击设置密码,点击准备输入键,屏幕待输入状态,用户在键盘上按下1536(可以点击delete回删键),点击确认键,屏幕显示on,小灯亮,表示密码设置完成。点击close键后又初始化一些标志,点击准备输入键,屏幕待输入状态,用户输入453,点击确认键,屏幕显示err,表示密码输入错误,且单位数码管显示1,表示输入错误次数。点击close键关闭输入。当再次点击准备输入,屏幕待输入状态,当用户输入次数达到3次屏幕显示倒计时60s后才能再次操作,蜂鸣器报警60s,且键盘按任何键无作用。倒计时完了,可再次输入。当用户输入1536密码正确,则屏幕显示on,小灯亮表示密码锁打开,并可以修改密码,点击修改密码,屏幕进入待输入状态,输入新密码123,点击确认键,屏幕显示on,小灯亮表示密码修改成功。
六、 单片机代码 - #include <reg52.h>
- #define uint unsigned int
- #define uchar unsigned char
- sbit LED=P3^3; //小灯
- sbit ALTER=P3^5; //警报器
- void delay(uint); //延迟函数
- void Show_Pwd(); //数码管显示密码
- void Show_on(); //数码管显示on
- void Show_err(); //数码管显示err
- void Sure_on_err(); //判断密码是否正确
- void Show_Sixty(); //数码管显示倒计时60s
- void Show_Time(uint); //数码管显示给定的数字(倒计时)
- void Init(); //初始化
- void Total_Show(); //数码管总显示
- void Close_Init(); //close关闭后的初始化
- void Pwd_Modity(); //修改密码
- void SetPwd(); //设置密码
- char Key=-1; //保存键号
- uchar PwdRight=0; //正确密码的位数(因为我设置的是1-4为密码都可以,所以判断比较密码时需要密码位数)
- char PwdDigit=-1; //记录当前输入密码的位数(方便存数组,比较所以初始值为-1)
- uchar PwdErrTime=0; //密码错误次数(触发警报的判断依据)
- uchar TimeCount=0; //计时器中断函数计数器 (定时器计了50000us即50ms,则加1,达到20次即计了1s)
- uchar ShowSign=1; //Total_Show()函数根据该标志来判断显示什么信息
- uchar Keycount=0; //循环功能按键的次数(当该功能按键次数改变即有功能按键按下,则进入switch-case中选择执行,防止不按功能按键时,主程序依然继续执行上一次的功能)
- char Password[4]={-1,-1,-1,-1}; //储存密码
- uchar Pwd_Now[4]={10,10,10,10}; //储存当前输入的密码
- uchar code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40,0x37,0x7b,0x31}; //数模0-9与'-'(10),'n'(11),'e'(12),'r'(13)
- uchar code key_buf[]={0xd7,0xeb,0xdb,0xbb,0xed,0xdd,0xbd,0xee,
- 0xde,0xbe,0xb7,0x7e,0x7d,0x7b,0x77,0xe7};
- //键模0-9,sure(10),delete(11),setpwd(12),modify(13),close(14),ready(15)
- void delay(uint time)
- {
- uchar i=0;
- for(;time>0;time--)
- for(i=0;i<113;i++);
- }
- void Getkey(void) interrupt 0
- {
- uchar key_scan[]={0xef,0xdf,0xbf,0x7f}; //键扫描码(1-4列)
- uchar i=0,j=0;
- for(i=0;i<4;i++)
- {
- P1=key_scan[i]; //P1送出键扫描码
- if((P1&0x0f)!=0x0f) //判断有无按键按下
- {
- delay(10);
- if((P1&0x0f)!=0x0f)
- {
- for(j=0;j<16;j++)
- {
- if(key_buf[j]==P1) //找到按键
- {
- while(P1!=key_scan[i]) //按键松开
- {
- Total_Show(); //按键时数码管显示
- }
- Key=j; //获取键值
- if(j<10)
- {
- PwdDigit++; //只有按下数字键该密码位数才会自加
- if(PwdDigit<4)
- Pwd_Now[PwdDigit]=j; //存储有效密码
- }
- else if(j!=12&&j!=15)
- Keycount++; //循环功能(除了setpwd和ready)按键的次数
- P1=0x0f;
- return;
- }
- }
- }
- }
- }
- }
- void Show_Pwd()
- {
- switch(PwdDigit) //通过密码位数来实时显示密码的输入,当前存储的密码作为下标显示密码
- {
- case 3: P3=0x4f;P2=table[Pwd_Now[3]];delay(10); //只打开第四个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=0关闭警报
- case 2: P3=0x8f;P2=table[Pwd_Now[2]];delay(10); //只打开第三个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=0关闭警报
- case 1: P3=0xcd;P2=table[Pwd_Now[1]];delay(10); //只打开第二个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=0关闭警报
- case 0: P3=0xce;P2=table[Pwd_Now[0]];delay(10);break; //只打开第一个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=0关闭警报
- default:P3=0x0c;P2=table[10];PwdDigit=-1;delay(10);break;//打开所有位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=0关闭警报
- //显示'-',当PwdDigit=-1与delay(10)位置不同有问题
- }
- }
- void Total_Show()
- {
- switch(ShowSign) //根据数码管展示的标志位来判断展示不同的信息
- {
- case 1:Show_Pwd();break; //显示输入的密码
- case 2:Show_on();break; //显示密码输入正确的on并点亮小灯
- case 3:Show_err();break; //显示密码输入错误的err
- case 4:Show_Sixty();break; //显示60s倒计时并打开警报
- }
- }
- void Show_on()
- {
- P3=0xc6; //只打开第一个位选,P3.2=1(无影响),P3.3=0打开LED,P3.5=0关闭警报
- P2=table[0]; //显示0
- delay(15);
- P3=0xc5; //只打开第二个位选,P3.2=1(无影响),P3.3=0打开LED,P3.5=0关闭警报
- P2=table[11]; //显示n
- delay(15);
- }
- void Show_err()
- {
- P3=0xce; //只打开第一个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=0关闭警报
- P2=table[12]; //显示'e'
- delay(10);
- P3=0xcd; //只打开第二个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=0关闭警报
- P2=table[13]; //显示'r'
- delay(10);
- P3=0x8e; //只打开第三个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=0关闭警报
- P2=table[13]; //显示'r'
- delay(10);
- }
- void Sure_on_err()
- {
- uchar i=0;
- ShowSign=3; //默认输入错误,数码管显示标志为3显示err
- if(PwdRight==PwdDigit) //当正确密码的位数与当前输入的密码的位数相同
- {
- for(i=0;i<=PwdDigit;i++) //每一位密码进行比较
- {
- if(Password[i]!=Pwd_Now[i]) //当有一位不同就退出循环比较
- break;
- }
- if(i==PwdDigit+1)
- { //如果密码正确,比较完不满足判断条件退出的循环则i=PwdDigit+1
- ShowSign=2; //更改数码管显示的标志为2显示on点亮小灯
- PwdErrTime=0; //将错误次数清0
- }
- else //如果是break退出的循环则错误次数加1,不需要更改数码管显示标志
- PwdErrTime++;
- }
- else //如果正确密码的位数与当前输入的密码的位数不相同,则错误次数加1
- PwdErrTime++;
- if(PwdErrTime==3) //密码错误次数达到三次
- {
- P0=table[PwdErrTime]; //单个数码管先显示密码错误次数3
- ShowSign=4; //更改数码管显示的标志为4显示倒计时并打开警报
- }
- }
- void SetPwd()
- {
- uchar i=0;
- uchar count=0; //存储按功能键的次数
- uchar sign=0; //是否确认密码标志(0:未确认,1:已确认)
- P1=0x7f; //将第四列设为低电平
- while(P1!=key_buf[12]); //setpwd第一次设置密码
- P1=0xef; //将第一列设为低电平
- while(P1!=key_buf[15]); //查询是否按下ready键
- EX0=1; //打开按键中断开关
- while(1)
- {
- Total_Show();
- if(count!=Keycount&&Key==11) //当有delete功能按键按下时将密码位数减1(count!=Keycount为了防止当按下delete后,由于在循环体中位数会一直自减)
- {
- count=Keycount; //功能键次数重新赋值给count
- PwdDigit--; //当前密码位数--
- }
- if(PwdDigit>=0&&Key==10) //输完1-4位密码并且按下sure键后显示并保存密码
- {
- ShowSign=2; //数码管显示on并点亮小灯
- Total_Show();
- sign=1; //表示已确认设置密码
- for(i=0;i<=PwdDigit;i++) //保存密码
- Password[i]=Pwd_Now[i];
- PwdRight=PwdDigit; //将当前密码位数赋值给正确密码位数标志
- }
- if(Key==14&&sign==1) //只能确认设置完密码才能点击close退出
- {
- return;
- }
- }
- }
- void Show_Time(uint number)
- {
- P3=0xee; //只打开第一个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=1打开警报
- P2=table[10]; //显示 -
- delay(5);
- P3=0xed; //只打开第二个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=1打开警报
- P2=table[number/10]; //显示十位
- delay(5);
- P3=0xaf; //只打开第三个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=1打开警报
- P2=table[number%10]; //显示个位
- delay(5);
- P3=0x6f; //只打开第四个位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=1打开警报
- P2=table[10]; //显示 -
- delay(5);
- }
- void Show_Sixty()
- {
- uchar toal=60; //倒计时总时间
- EX0=0; //关闭键盘中断(防止按键进行中断数码管显示)
- TR0=1; //打开定时器开关
- do
- {
- if(TimeCount==20) //中断一次50ms,TimeCount++,当达到20次即1s显示值减一
- {
- TimeCount=0; //重新赋值为0
- toal--; //显示值--
- }
- Show_Time(toal); //掉用显示函数
- }while(toal>0); //当显示到0时退出
- TR0=0; //关闭定时器开关
- EX0=1; //打开键盘中断开关
- PwdErrTime=0; //错误次数重新赋值为0
- PwdDigit=-1; //密码位数赋值为-1重新输入
- ShowSign=1; //数码管显示标志设为1,显示密码
- }
- int0_srv() interrupt 1
- {
- TimeCount++;
- }
- void Init()
- {
- IT0=0; //设为跳变沿有问题
- TMOD=0x01; //定时器0工作方式为1
- TH0=0x3c;
- TL0=0xb0; //一次定时50ms
- ET0=1; //打开定时器0的中断开关
- EA=1; //打开总开关
- P0=table[0];//单个数码管显示0
- LED=1; //关闭数码管
- ALTER=0; //关闭警报器
- }
- void Close_Init()
- {
- uchar i=0;
- P3=0xcf; //关闭所有位选,P3.2=1(无影响),P3.3=1关闭LED,P3.5=0关闭警报
- EX0=0; //关闭键盘中断
- PwdDigit=-1; //密码位数赋值为-1
- P1=0xef; //将第一列设为低电平
- while(P1!=key_buf[15]);//查询是否按下ready键
- ShowSign=1; //数码管显示密码
- EX0=1; //打开键盘中断
- for(i=0;i<4;i++) //将当前密码初始化
- Pwd_Now[i]=10;
- }
- void Pwd_Modity()
- {
- uchar count=0; //存储按功能键的次数
- PwdDigit=-1; //密码位数赋值为-1
- ShowSign=1; //数码管显示密码
- while(1)
- {
-
- Total_Show();
- if(count!=Keycount&&Key==11) //当有delete功能按键按下时将密码位数减1(count!=Keycount为了防止当按下delete后,由于在循环体中位数会一直自减)
- {
- count=Keycount; //功能键次数重新赋值给count
- PwdDigit--; //当前密码位数--
- }
- if(PwdDigit>=0&&Key==10) //输完1-4位密码并且按下sure键后显示并保存密码
- {
- uchar i=0;
- for(i=0;i<=PwdDigit;i++) //保存密码
- Password[i]=Pwd_Now[i];
- for(i=PwdDigit+1;i<4;i++) //将不是密码位初始化位-1
- Password[i]=-1;
- PwdRight=PwdDigit; //将当前密码位数赋值给正确密码位数标志
- ShowSign=2; //数码管显示on并点亮小灯
- Key=12; //防止返回主函数后进入switch-case
- return;
- }
- }
- }
- void main(void)
- {
- uchar count=0;//功能按键的次数
- Init(); //初始化
- SetPwd(); //设置密码
- while(1)
- {
- P0=table[PwdErrTime]; //close关闭后单个数码管关闭
- if(count!=Keycount) //当有功能按键按下时进入switch-case(为了防止当按下delete后,由于在循环体中位数会一直自减)
- {
- count=Keycount; //功能按键次数重新赋值给count
- switch(Key)
- {
- case 10:Sure_on_err();break; //调用确认密码函数
- case 11:if(ShowSign==1){PwdDigit--;Pwd_Now[PwdDigit+1]=10;}break; //密码位数减1,并把上一位存储的密码初始化为10
- case 13:if(ShowSign==2)Pwd_Modity();break; //当密码正确了才能修改密码
- case 14:Close_Init();break; //close关闭后的初始化
- }
- }
- Total_Show();
- }
- }
复制代码
Keil代码与Proteus8.13仿真下载:
密码锁.zip
(2.98 MB, 下载次数: 46)
|