资源描述
湖南大学电气与信息工程学院
课 程 设 计
课 程 名 称: 单 片 机 原 理 与 应 用
课 题 名 称: 数 字 密 码 锁 设 计
所在 学院 名称: 湖南大学电气与信息工程学院
设 计 人 员: 宁 枫 根 宋 浩 冉
目录
一、 前言 - 2 -
二、 总体方案设计 - 3 -
2.1方案论证与比较 - 3 -
2.2方案选择 - 4 -
三、 单元模块设计 - 4 -
3.1硬件支持 - 4 -
3.2功能单元模块设计 - 4 -
3.2.1 开锁机构 - 4 -
3.2.2 矩阵键盘设计电路 - 5 -
3.2.3 声音提示电路设计 - 5 -
3.2.4 显示模块设计 - 6 -
四、程序设计与系统仿真 - 7 -
4.1 模块介绍 - 7 -
4.1.1 主程序模块 - 9 -
4.1.2 密码比较判断模块 - 9 -
4.1.3 键盘扫描模块 - 9 -
4.1.4 修改密码模块 - 10 -
五、系统功能 - 12 -
六.设计总结 - 12 -
七、附录:源程序 - 12 -
一、 前言
随着社会物质财富的日益增长和人们生活水平的提高,安全成为现代居民最关心的问题之一。而锁自古以来就是把守门的铁将军,人们对它要求甚高,即要求可靠地防盗,又要使用方便。传统的门锁既要备有大量的钥匙,又要担心钥匙丢失后的麻烦。随着单片机的问世,出现了数字密码锁,其保密性高,使用灵活性好,安全系数高,故受到广大用户的青睐。
数字密码锁通常使用ARM和单片机控制,单片机相对ARM实现较为简单,功能较为完善,因此使用单片机控制较多。用单片机控制的密码锁常使用汇编语言编写程序,数码管做显示器 。但本设计使用移植性及可读性强的C语言编写;同时采用显示清楚、功率消耗小而且寿命长的1602A液晶显示器。从经济实用的角度出发,采用ADUC848单片机设计出一种具有密码设置、报警等功能的数字密码锁,并通过Proteus软件成功地进行了仿真。
任务及设计要求
1.设计一多位电子密码锁,输入密码用“F”表示,输入密码正确,绿灯亮(或显示其它标志、蜂鸣器替代),输入密码错误,红灯亮(或显示其它标志、蜂鸣器替代).
2.具有确定键和取消键,在未确定之前可以取消,重新输入.
3.连续输入三次错误密码,红灯闪烁,报警电路动作,键盘锁定.
4.具有密码重置、修改功能.
5.具有密码输入等待操作时间限制功能,超过限定时间报警.
6.显示北京时间,时间可调整.
7.可利用蜂鸣器添加提示音.
二、 总体方案设计
2.1方案论证与比较
方案一:以ADUC848单片机为数字密码锁系统核心,使用4*4矩阵键盘作为数据输入方式,驱动1602A显示器提示程序运行过程和开锁的步骤,利用AT24C02芯片实现掉电存储。图1为单片机控制密码锁的系统原理框图。
ADUC848
单片机
AT24C02存储芯片
矩阵
键盘
控制
LCD
1602A
蜂鸣器提示电路
电源模块
LED
密码校验提示
图1 单片机控制密码锁的系统原理框图
方案二:以74LS112双JK触发器构成的数字逻辑电路控制方案,如图2。
输入按扭开关组
输入锁存电路
密码存储电路
开锁控制电路
机械动作构件
5秒定时电路
20秒定时电路
声光指示电路
图2 数字逻辑控制方案数字密码锁原理框图
2.2方案选择
由于利用单片机灵活的编程设计和强大的I/O端口,及其控制的准确性,不但能实现基本的密码锁功能,还可以增添掉电存储、声光提示等功能,故选用方案一。
三、 单元模块设计
3.1硬件支持
使用的元器件有:核心芯片ADUC848、存储芯片AT24C02、液晶显示1602A、4×4矩阵键盘、报警蜂鸣器、发光二极管和三极管。
3.2功能单元模块设计
3.2.1 开锁机构
通过单片机送给开锁执行机构,电路驱动电磁锁吸合,从而达到开锁的目的。如图3所示,为密码锁开锁电路原理图。
当用户输入的密码正确时,单片机便输出开门信号,送到开锁驱动电路,然后驱动电磁锁,达到开门的目的。本次设计中,基于节省成本考虑,用发光二极管代替电磁锁,信息通过LCD显示,并利用蜂鸣器和二极管声光指示。其中,绿发光二极管亮,表示开锁;否则,表示密码输入错误并开启报警电路。
开锁驱动电路
STC89C52
单片机
密码
正确
超次锁定
电磁锁
是
否
图3 密码锁开锁电路原理图
3.2.2 矩阵键盘设计电路
每一条水平(行线)与垂直线(列线)的交叉处不相通,而是通过一个按键来连通,利用这种行列式矩阵结构只需要M条行线和N条列线,即可组成具有M×N个按键的键盘。由于本设计中要求使用16个按键输入,为减少键盘与单片机接口时所占用的I/O线的数目,故使用矩阵键盘。本设计中,矩阵键盘行线和单片机P1.0-P1.3相连,列线与单片机P1.4-P1.7相连。矩阵键盘设计电路图,如图4所示。
键盘扫描采用行扫描法,即依次置行线中的每一行为低电平,其余均为高电平,扫描列线电平状态,为低电平即表示该键按下。
1
2
3
4
5
6
7
8
9
0
退位
确认
设置时间
*
关闭
图4 矩阵键盘设计电路图
3.2.3 声音提示电路设计
声音提示电路采用小蜂鸣器提示。蜂鸣器能够根据脉冲信号,以及信号的频率发出各种不同的声音,这样可以根据系统要求在密码出入正确和密码输入错误时发出不同的声音提示,已达到报警的要求。蜂鸣器电路,如图5所示。
图5 蜂鸣器电路
3.2.4 显示模块设计
本设计中,显示电路采用1602A液晶显示器显示。如下所示,图6为1602A液晶显示器的接口示意图,表1为接口说明。
图6 1602A液晶显示器的接口示意图
表1 接口说明
管脚序
名称
电平
功能描述
1
VSS
0V
接地
2
VCC
5.0V
电源输入
3
V0
—
LCD驱动电压输入
4
RS
H/L
寄存器选择:RS=H,选择数据寄存器;
RS=L,选择指令寄存器
5
R/W
H/L
读写信号线:R/W=H,读操作;R/W=L,写操作
6
E
H,H→L
使能信号
7
DB0
H/L
数据线
8
DB1
H/L
数据线
9
DB2
H/L
数据线
10
DB3
H/L
数据线
11
DB4
H/L
数据线
12
DB5
H/L
数据线
13
DB6
H/L
数据线
14
DB7
H/L
数据线
15
BLA
5.0V
背光电源正极
16
BLK
0V
背光电源负极
本设计中液晶串口一共用到11根导线与单片机相连, 具体连接情况如表2显示模块与MCU连接说明所示。
表2 显示模块与MCU连接说明
序号
1602A液晶显示器引脚
ADUC848单片机引脚
1
RS引脚
P2.5(A13)
2
R/W引脚
P2.6(A14)
3
E引脚
P2.7(A15)
4
D0引脚
P0.0(AD0)
5
D1引脚
P0.1(AD1)
6
D2引脚
P0.2(AD2)
7
D3引脚
P0.3(AD3)
8
D4引脚
P0.4(AD4)
9
D5引脚
P0.5(AD5)
10
D6引脚
P0.6(AD6)
11
D7引脚
P0.7(AD7)
四、程序设计与系统仿真
4.1 模块介绍
与硬件电路相关联,本系统软件包括主程序模块、密码比较判断模块、键盘扫描模块、修改密码模块、1602A液晶显示模块等。系统程序流程如图9所示。开始
系统初始化
调用E2PROM密码
显示主界面
输入密码
判断密码
确定密码
开锁模拟声光
锁定键盘
Y
N
返回
Y
判断次数N
超过N次
未超过N次
再次输入密码
密码设置完毕
N
设置新密码
Y
按键
N
判断按键值
返回主界面
N
Y
设置解码次数
产生随机密码
输入密码
N=0x84
N=0x82
N=0x11
图9 系统程序流程图
4.1.1 主程序模块
主程序主要用于定义全局变量,给全局变量赋初值,初始化E2PROM,启动定时器以及从AT24C02中读取密码,为整个程序提供数据;检测按键;调用显示等功能。
4.1.2 密码比较判断模块
该模块的功能是将键盘输入的密码利用if语句与设定的密码进行逐个比较,若密码完全正确则开锁;若不正确,则开启报警电路,复位后重新输入密码。其密码输入和比较判决流程图如图10所示。
输入密码
核对
键盘锁定,开启报警电路
开锁,灯亮,可密码修改
N
Y
复位
图10 密码输入和比较判决流程图
4.1.3 键盘扫描模块
键盘使用矩阵式键盘,由行和列组成,CPU对所有键盘进行监视,从而实现逐列扫描键盘确定被按键的具体位置、判断键盘上有无键按下、消除去抖动、判断闭合的键是否释放等功能。如图11所示,为键盘扫描流程图。
开始
延时去抖动
计算输入键值
键值送入单片机
有按键输入?
有按键输入?
有按键输入?
返回
N
N
Y
Y
图11 键盘扫描流程图
4.1.4 修改密码模块
在密码输入正确情况下,可以按下“重置密码”对密码进行重新设置,每设定一位就将密码送给AT24C02存储起来,当设置6位密码完毕后,系统将自动跳到程序开始,调用新设置的密码。图12为修改密码流程图。
开始
输入原密码
密码比较
比较正确?
输入新密码
再次输入新密码
调用AT24C02中密码
比较正确
修改成功
返回菜单
键盘锁定,启动报警电路
复位
Y
N
图12 修改密码流程图
五、系统功能
本设计中系统可实现功能如下:
(1)通过切换,对输入的1—8位密码进行显示或隐藏;
(2)通过发光二极管和蜂鸣器,对解密正确或者错误进行声光报警;
(3)1—8位密码修改;
(4)在密码遗失情况下,通过初始密码进行密码再设置;
(5)具有1-3次的解锁次数任意设定功能;
六.设计总结
作为一名电子信息工程的学生,通过本次课程设计,我很好的了解了单片机C语言设计的思考模式,熟悉了C语言的编程规则,知道了如何去运用一个完全陌生的单片机,会根据单片机的不同结构来采用不同的命令实现功能。在此次课程设计中我负责的模块出现了问题导致在数码管上显示状态的时候混乱了,主要是因为延时上没有处理好,Aduc848的定时器处理时是要加上一定的延时来防止冲突的。另外还有键盘的防抖模块,一开是没有考虑好,所以键盘按下后显示会乱变。综上而言,通过此次课程设计,我很好的学习了单片机的应用以及其编程。
七、附录:源程序
#include <aduc848.h>
#include<intrins.h>
#define uint unsigned int
#define uchar unsigned char
/*状态声明*/
#define opened 0
#define new1 15
#define new2 16
#define succed 5
#define fanin 10
#define null 11
#define error 12
#define different 13
#define tover 14
/*按键声明*/
#define enter 10
#define back 11
#define trevise 12
#define revise 13
#define cancel 14
#define vain 15
#define off 16
#define end 17
#define finish 18
/*函数声明*/
void p_base();
void p_revise();
void p_new1();
void p_new2();
void p_trevise();
void p_show();
uchar p_scan();
void p_record();
void p_delay(uint f_n);
void p_state(uchar f_s);
uchar p_compare(uchar *f_k1,uchar *f_k2);
void p_copy(uchar *f_s,uchar *f_k);
/*I/O口声明*/
sbit p00=P0^0;
sbit p10=P1^0;
sbit p11=P1^1;
sbit p12=P1^2;
sbit p13=P1^3;
sbit p14=P1^4;
sbit p15=P1^5;
sbit p16=P1^6;
sbit p17=P1^7;
sbit p20=P2^0;
sbit p21=P2^1;
sbit p22=P2^2;
sbit p23=P2^3;
sbit p24=P2^4;
sbit p25=P2^5;
sbit p26=P2^6;
sbit p27=P2^7;
sbit p30=P3^0;
sbit p31=P3^1;
sbit p32=P3^2;
sbit p33=P3^3;
sbit p34=P3^4;
sbit p35=P3^5;
sbit p36=P3^6;
sbit p37=P3^7;
/*变量声明*/
uchar
bot,key1[9],key2[9],save[9],t[4],s,min,h,mino,mint,ho,ht,n1=1,n2=1,lock,minu,sign,tov,state,n,sound,point=1,
showlist[]={0x03,0x9f,0x25,0x0d,0x99,0x49,0x41,0x1f,0x01,0x09,0x71,0xff,0x21,0x85,0xe1,0x7f,0x7d};
uint
ms;
/*主函数*/
void main()
{
EA=1; //开总中断
ET0=1; //开内部中断0
ET1=0; //关内部中断1
TMOD=0x11;
TH0=0xf9; //计数器0高八位
TL0=0xdb; //计数器0低八位
TR0=1; //计数器0开始计数
I2CCON=0xe8;
sound=0;
tov=1; //超时计数置为1
P1=0x00; //P1全置为0作为输入
if(sign!=1)
p_new1(); //调用首次使用判断函数
p_base(); //调用基础解锁函数
}
/*基础解锁函数*/
void p_base()
{
n=0; //密码数组置首位
state=fanin; //指示管置为输入状态fanin
while(1) //无限循环
{
if(n==0)
state=fanin;
else
state=n;
bot=p_scan();
if(n!=0&&tov==0) //判断密码数组是不是在首位,超时计数是否为0
{
p_state(tover);//调用指示管状态闪烁函数,置为用超时状态tover
main(); //调用主函数
}
switch(bot)
{
case vain:
break;
case enter:
key1[n]=end; //把密码后一位置为end
if(p_compare(key1,save)) //判断输入的密码是否与原密码相同
{
p_state(succed); //调用指示管状态闪烁函数,置为成功状态succed
lock=0; //锁定键盘计数置为0
point=1; //多次密码输入错误计数
state=opened; //指示管置为输入状态opened
while(1)
{ bot=p_scan();
switch(bot)
{
case off:
main();
break;
case back:
while(1)
{
bot=p_scan();
switch(bot)
{
case off:
main();
break;
case enter:
main();
break;
}
}
}
}
}
else
{
p_state(error);//调用指示管状态闪烁函数,置为用错误状态error
p_record(); //调用错误计数函数
main(); //调用主函数
}
break;
case cancel:
main();
break;
case back:
if(n!=0) //判断当前密码是否为首位
key1[n--]=end; //当前密码位置end,密码退位
break;
case revise:
p_revise();
break;
case trevise:
p_trevise();
break;
default:
if(n+1==9)
{
p_state(error);
main();
}
key1[n++]=bot;
}
}
}
/*密码修改模块*/
void p_revise()
{
ET0=1;
ET1=0;
n=0;
p_state(fanin);
state=fanin;
while(1)
{
if(n==0)
state=fanin;
else
state=n;
bot=p_scan();
if(tov==0)
{
p_state(tover);
main();
}
switch(bot)
{
case vain:
break;
case enter:
key1[n]=end;
if(p_compare(key1,save))
{
lock=0;
point=1;
p_state(succed);
p_new1();
}
else
{
p_state(error);;
p_record();
main();
}
break;
case cancel:
main();
break;
case back:
if(n!=0)
key1[n--]=end;
break;
case revise:
p_revise();
case off:
break;
case trevise:
p_trevise();
break;
default:
if(n+1==9)
{
p_state(error);
main();
}
key1[n++]=bot;
}
}
}
/*首次使用设置密码*/
void p_new1()
{
n=0;
state=new1;
while(1)
{
if(n==0)
state=new1;
else
state=n;
if((tov==0&&sign==1)||(n!=0&&tov==0))
{
p_state(tover);
main();
}
bot=p_scan();
switch(bot)
{
case vain:
break;
case enter:
key1[n]=end;
p_new2();
break;
case cancel:
main();
break;
case back:
if(n!=0)
key1[n--]=end;
break;
case revise:
if(sign==1)
p_revise();
break;
case trevise:
p_trevise();
break;
default:
if(n+1==9)
{
p_state(error);
main();
}
key1[n++]=bot;
}
}
}
/*首次使用二次确定密码*/
void p_new2()
{
n=0;
state=new2;
while(1)
{
if(n==0)
state=new2;
else
state=n;
bot=p_scan();
if(tov==0)
{
p_state(tover);
main();
}
switch(bot)
{
case vain:
break;
case enter:
key2[n]=end;
if(p_compare(key1,key2))
{
sign=1;
p_copy(save,key2);
lock=0;
p_state(succed);
main();
}
else
{
p_state(different);
main();
}
break;
case cancel:
main();
break;
case back:
if(n!=0)
key2[n--]=end;
break;
case revise:
if(sign==1)
p_revise();
break;
case off:
break;
case trevise:
p_trevise();
break;
default:
if(n+1==9)
{
p_state(error);
main();
}
key2[n++]=bot;
}
}
}
/*键盘模块*/
uchar p_scan()
{
uchar f_s=vain;
P2=P2&0xf0;
if(P1!=0xff)
{
p_delay(10);
if(P1!=0xff)
{
tov=1;
P2=P2&0xf0;
P2=P2|0x07;
switch(P1)
{
case 0xfe:f_s=1;break;
case 0xfd:f_s=4;break;
case 0xfb:f_s=7;break;
case 0xf7:f_s=back;break;
}
while(P1!=0xff)
{
sound=1;
}
sound=0;
P2=P2&0xf0;
P2=P2|0x0b;
switch(P1)
{
case 0xfe:f_s=2;break;
case 0xfd:f_s=5;break;
case 0xfb:f_s=8;break;
case 0xf7:f_s=0;break;
}
while(P1!=0xff)
{
sound=1;
}
sound=0;
P2=P2&0xf0;
P2=P2|0x0d;
switch(P1)
{
case 0xfe:f_s=3;break;
case 0xfd:f_s=6;break;
case 0xfb:f_s=9;break;
case 0xf7:f_s=enter;break;
}
while(P1!=0xff)
{
sound=1;
}
sound=0;
P2=P2&0xf0;
P2=P2|0x0e;
switch(P1)
{
case 0xfe:f_s=cancel;break;
case 0xfd:f_s=off;break;
case 0xfb:f_s=revise;break;
case 0xf7:f_s=trevise;break;
}
while(P1!=0xff)
{
sound=1;
}
sound=0;
p_delay(10);
}
}
return f_s;
}
void time() interrupt 1 //定时器0中断子函数
{
TH0=0xf9;
TL0=0xdb;
TR0=1;
ms++;
ms=ms%1000; //1000毫秒后ms置为0
p_show(); //调用显示函数
if(sound) //判断声音标志sound是否为1
p26=!p26; //蜂鸣器交替 p26取反
if(ms==0) //判断ms是否为0
{
tov++; //超时计数tov加1
tov=tov%11; //11秒后超时计数tov置为0
s++; //秒计数s加1
s=s%60; //60秒后秒计数s置为0
if(s==0) //判断秒计数s是否为0
{
minu++; //键盘解锁计时加1
minu=minu%30; //30分钟后键盘解锁计时
if(minu==0)
lock=0;
min++;
min=min%60;
mino=min%10;
mint=min/10;
if(min==0)
{
h++;
h=h%24;
ho=h%10;
ht=h/10;
}
}
}
}
/*显示模块*/
void p_show()
{
switch(n1)
{
case 1:
P3=0x80;
P0=showlist[ht];
n1++;
break;
case 2:
P3=0x20;
P0=showlist[mint];
n1++;
break;
case 3:
P3=0x00;
p24=1;
P0=showlist[state];
n1++;
p00=point;
break;
case 4:
p24=0;
P3=0x08;
P0=showlist[mino];
n1++;
break;
case 5:
P3=0x40;
P0=showlist[ho];
p00=0;
n1=1;
}
}
/*时间修改模块*/
void p_trevise()
{
n=3;
n2=1;
ET0=0;
ET1=1;
TH1=0xf9;
TL1=0xdb;
TR1=1;
p24=0;
P3=0x80;
P0=showlist[ht];
while(1)
{
if(tov==0)
{
P0=showlist[tover];
P3=0x00;
while(n--)
{
p24=1;
sound=1;
p_delay(99999);
p24=0;
sound=0;
p_delay(99999);
}
main();
}
if(bot!=finish)
bot=p_scan();
switch(bot)
{
case vain:
break;
case finish:
if(t[1]+t[0]*10>=24||t[3]+t[2]*10>=60)
{
P0=showlist[error];
while(n--)
{
p24=1;
sound=1;
p_delay(99999);
p24=0;
sound=0;
p_delay(99999);
}
bot=vain;
p_trevise();
}
ht=t[0];
ho=t[1];
mint=t[2];
mino=t[3];
h=ho+ht*10;
min=mino+mint*10;
s=1;
main();
break;
case enter:
break;
case cancel:
main();
break;
case back:
break;
case revise:
if(sign==1)
p_revise();
break;
case off:
break;
case trevise:
p_trevise();
break;
default:
switch(n2)
{
case 4:
P3=0x08;
P0=showlist[bot];
t[3]=bot;
n2=1;
bot=finish;
p_delay(99999);
P3=0x00;
break;
case 3:
P3=0x20;
P0=showlist[bot];
t[2]=bot;
n2++;
p_delay(99999);
P0=showlist[mino];
P3=0x08;
break;
case 2:
P3=0x40;
P0=showlist[bot];
t[1]=bot;
n2++;
p_delay(99999);
P0=showlist[mint];
P3=0x20;
break;
case 1:
P3=0x80;
P0=showlist[bot];
t[0]=bot;
n2++;
p_delay(99999);
P0=showlist[ho];
P3=0x40;
}
}
}
}
void atime() interrupt 3 //定时器1中断
{
TH1=0xf9;
TL1=0xdb;
TR1=1;
ms++;
if(sound)
p26=!p26;
ms=ms%1000;
if(ms==0)
{
tov++;
tov=tov%11;
}
}
/*错误记录模块*/
void p_record() //三次解锁错误则置状态为error
{
lock++; //键盘锁定计数加1
minu=0; //键盘解锁计时置0
while(lock==3) //当键盘锁定计数为3时循环
{
sound=1;
point=0; //3次错误标志置为0,表示逻辑1
state=error;
p_delay(99999);
state=null;
p_delay(99999);
}
}
/*状态显示模块*/
void p_state(uchar f_s) //指示管状态闪烁函数
{
uchar f_n=3;
while(f_n--)
{
state=f_s;
sound=1;
p_delay(999
展开阅读全文