1、 单片机电子密码锁课程设计 单片机技术及应用综合训练(设计报告) 前言 随着人们生活水平的提高,如何实现家庭防盗这一问题也变得尤为突出。在科学技术不断发展的今天,电子密码防盗锁作为防盗卫士的作用显得日趋重要。 本文从经济实用的角度出发,系统由STC89C52与低功耗CMOS型E²PROM AT24C02作为主控芯片与数据存储器单元,结合外围的键盘输入、LCD显示、报警、开锁等电路模块。它能完
2、成以下功能:正确输入密码前提下,开锁;错误输入密码情况下,报警;密码能够根据用户需要更改。用C语言编写的主控芯片控制程序与EEPROM AT24C02读写程序相结合,并用Keil软件进行编译,设计了一款能够多次更改密码,具有报警功能的电子密码控制系统。 本密码锁具有设计方法合理,简单易行,成本低,安全实用,保密性强,灵活性高等特点,具有一定的推广价值。 关键词:电子密码锁、报警、液晶显示 目录 一、 选题要求·····································1 二、 硬件电路设计·······
3、··························1 2.1 51单片机································2 2.2 键盘电路·································2 2.3 液晶显示电路·····························2 2.4 警报电路·································3 2.5 密码储存电路·····························3 2.6 晶振、复位及关锁·························3 三、 软件设计········
4、·····························4 四、 软硬件调试结果·······························9 4.1 电路总原理图·····························9 4.2 调试结果································10 五、 总结········································11 一、选题要求 本文从经济实用的角度出发,设计采用单片机为主控芯片,结合外围电路,组成电子密码控制系统,密码锁共6位密码,每位的取值范围为0~9,
5、用户能够自行设定和修改密码。用户想要打开锁,必先经过提供的键盘输入正确的密码才能够,密码输入错误有提示,为了提高安全性,当密码输入错误三次将报警,期间输入密码无效,以防窃贼多次试探密码。6位密码同时输入正确,锁才能打开。锁内有备用电池,只有内部上电复位时才能设置或修改密码,因此,仅在门外按键是不能修改或设置密码的,因此保密性强、灵活性高。其特点如下: 1) 保密性好,编码量多,远远大于弹子锁。随机开锁成功率几乎为零。 2) 密码可变,用户能够随时更改密码,防止密码被盗,同时也能够避免因人员的更替而使锁的密级下降。 3) 误码输入保护,当输入密码多次错误时,报警系统自动启动。 4) 无活
6、动零件,不会磨损,寿命长。 5) 使用灵活性好,不像机械锁必须佩带钥匙才能开锁。 6) 电子密码锁操作简单易行,一学即会。 二、 硬件电路设计 下面是整个设计的流程图: 开开始 开始始 初始化 次数加1 输入密码 密码正确? 返返回回 N Y Y 开锁程序 开锁? N 修改密码? 次数>3? 报警程序 修改程序 Y N N Y 开始 返回 2.1 51单片机 这次课程设计采用的是5系列单片机AT89C52。其外部封装如下图所示: AT89C52单片机有4组8位的可编程I/O口,分别位P0、P1、P2、P3口
7、每个口有8位(8根引脚),共32根[8]。 P0口(Pin39~Pin32):8位双向I/O口线,名称为P0.0~P0.7 P1口(Pin1~Pin8):8位准双向I/O口线,名称为P1.0~P1.7 P2口(Pin21~Pin28):8位准双向I/O口线,名称为P2.0~P2.7 P3口(Pin10~Pin17):8位准双向I/O口线,名称为P3.0~P3.7 2.2 键盘电路 本次试验采用的是行列键盘,C语言程序中有相应的驱动程序,其硬件电路如下所示,下右对应的为各个按键所对应的数字及功能。 0 1 2 3 4 5 6 7 8 9 确认
8、 重输 改密 2.3 液晶显示电路 实验中采用的是LM016L液晶显示,其数据端口采集数据经过单片机的P0口,接法如下图所示。不同情况下会有不同的显示,显示内容包括:Welcome、Hello Boss、Wrong、OK等内容。 2.4 警报电路 下图是警报电路连接图,警报触发为:连续三次输错密码,警报触发后会有一个时间延迟,延迟时间内任何操作都是无效的,过后经过关锁按钮可解除。 2.5 密码储存电路 实验中考虑到实用性方面时,就想到了密码储存及修改的问题,于是采用了FM24C02F作为面膜储存模块,电路连接如下所示:
9、
2.6 晶振、复位及关锁
晶振、复位及关锁电路如下所示(作图所示按钮为复位按钮):
三、软件设计
C语言源程序:
#include
10、
sbit baojing=P2^1;//报警器
sbit jdq=P2^0;//继电器
sbit jb=P2^3;//警报灯
sbit close=P2^2;
unsigned char old1,old2,old3,old4,old5,old6; //原始密码000000
unsigned char new1,new2,new3,new4,new5,new6;//代表新密码
void delay1(unsigned int m)
{
unsigned int n;
for(n=0;n 11、
{
unsigned int a;
unsigned char b;
for(a=0;a 12、);
if(baojing==0)
{
baojing=0;
DelayUs2x(256);
}
}
bit LCD_Check_Busy(void)//判忙函数
{
#ifdef CHECK_BUSY
DataPort= 0xFF;
RS=0;
RW=1;
EN=0;
_nop_();
EN=1;
return (bit)(DataPort & 0x80);
#else
return 0;
#endif
}
void LCD_Write_Com(unsigned char com) //写入命令函数
{
13、// while(LCD_Check_Busy()); //忙则等待
DelayMs(5);
RS=0; RW=0; EN=1;
DataPort= com;
_nop_(); EN=0;
}
void LCD_Write_Data(unsigned char Data) //写入数据函数
{
//while(LCD_Check_Busy()); //忙则等待
DelayMs(5);
RS=1; RW=0; EN=1;
DataPort= Data;
_nop_(); EN=0;
}
void LCD_Clear(void)// 14、清屏函数
{
LCD_Write_Com(0x01);
DelayMs(5);
}
void LCD_Write_Char(unsigned char x,unsigned char y,unsigned char Data) //写入字符函数
{
if(y==0)
{ LCD_Write_Com(0x80 + x); }
else
{ LCD_Write_Com(0xC0 + x); }
LCD_Write_Data( Data);
}
void LCD_Write_String(u 15、nsigned char x,unsigned char y,unsigned char *s) //写入字符串函数
{
while(*s)
{
LCD_Write_Char(x,y,*s);
s++; x++;
}
}
void LCD_Init(void) //液晶显示的初始化函数
{
LCD_Write_Com(0x38); //显示模式设置
DelayMs(5);
LCD_Write_Com(0x38);
DelayMs(5);
LCD_Wr 16、ite_Com(0x38);
DelayMs(5);
LCD_Write_Com(0x38);
LCD_Write_Com(0x08); //显示关闭
LCD_Write_Com(0x01); //显示清屏
LCD_Write_Com(0x06); //显示光标移动设置
DelayMs(5);
LCD_Write_Com(0x0C); //显示开及光标设置
}
unsigned char KeyScan(void) //键盘扫描函数,使用行列反转扫描法
{
unsigned char co 17、rd_h,cord_l;//行列值中间变量
KeyPort=0x0f; //行线输出全为0
cord_h=KeyPort&0x0f; //读入列线值
if(cord_h!=0x0f) //先检测有无按键按下
{
DelayMs(10); //去抖
if((KeyPort&0x0f)!=0x0f)
{
cord_h=KeyPort&0x0f; //读入列线值
KeyPort=cord_h|0xf0; //输出当前列线值
cord_l=KeyPort&0xf0; //读入行线值
18、
while((KeyPort&0xf0)!=0xf0);//等待松开并输出
return(cord_h+cord_l);//键盘最后组合码值
}}return(0xff); //返回该值
}
unsigned char KeyPro(void)
{
switch(KeyScan())
{
case 0x7e:return 0;break;//0 按下相应的键显示相对应的码值
case 0x7d:return 1;break;//1
case 0x7b:return 2;break;//2
case 0x77:retu 19、rn 3;break;//3
case 0xbe:return 4;break;//4
case 0xbd:return 5;break;//5
case 0xbb:return 6;break;//6
case 0xb7:return 7;break;//7
case 0xde:return 8;break;//8
case 0xdd:return 9;break;//9
case 0xdb:return 10;break;//10
case 0xd7:return 11;break;//11
case 0xee:return 12;br 20、eak;//12
case 0xed:return 13;break;//13
case 0xeb:return 14;break;//14
case 0xe7:return 15;break;//15
default:return 0xff;break;
}
}
void init() //24c02初始化子程序
{
scl=1;
_nop_();_nop_();
sda=1;
_nop_();_nop_();
}
void respons() //应答
{
unsigned char i;
scl=1;
_nop_( 21、);_nop_();
while((sda==1)&&(i<250))
i++;
scl=0;_nop_();_nop_();
}
void clock() //I2C总线时钟
{
unsigned char i=0;
scl=1;
_nop_();_nop_();
while((sda==1)&&(i<255))
i++;
scl=0;
_nop_();_nop_();
}
void start() //启动I2C总线
{
sda=1;
_nop_();_nop_();
22、scl=1;
_nop_();_nop_();
sda=0;
_nop_();_nop_();
scl=0;
_nop_();_nop_();
}
void stop() //停止I2C总线
{
sda=0;
_nop_();_nop_();
scl=1;
_nop_();_nop_();
sda=1;
_nop_();_nop_();
}
void writebyte(unsigned char a) //写一个字节
{
unsigned char b,tem;
tem=a;
for(b= 23、0;b<8;b++)
{
tem=tem<<1;
scl=0;
_nop_(); _nop_();
sda=CY; //temp左移时,移出的值放入了CY中
_nop_(); _nop_();
scl=1; //待sda线上的数据稳定后,将scl拉高
_nop_(); _nop_();
}
scl=0;
_nop_(); _nop_();
sda=1;
_nop_(); nop_();
}
unsigned char readbyte() //读一个字节
{
24、 unsigned char i,j,k=0;
scl=0;
_nop_();
_nop_();
sda=1;
for(i=0;i<8;i++)
{
_nop_();
_nop_();
scl=1; _nop_();
_nop_();
if(sda==1) j=1;
else j=0;
k=(k<<1)|j;
scl=0;
}
_nop_();
_nop_();
return(k);
}
unsigned char read24 25、c02(unsigned char address)//从24c02的地址address中读取一个字节数据
{
unsigned char date;
start();
writebyte(0xa0);
clock();
writebyte(address);
clock();
start();
writebyte(0xa1);
clock();
date=readbyte();
stop();
delay1(100);
return(date);
}
void write24c02( 26、unsigned char address,unsigned char info) // 向24c02的address地址中写入一字节数据info
{
start();
writebyte(0xa0);
clock();
writebyte(address);
clock();
writebyte(info);
clock();
stop();
delay1(5000); //这个延时一定要足够长,否则会出错。因为24c02在从sda上取得数据后,还需要一定时间的烧录过程。
}
main()
{
unsig 27、ned char num,i,bj,c=0;
unsigned char temp[6];
bit Flag;
init(); //初始化24C02
LCD_Init(); //初始化液晶屏
DelayMs(10); //延时用于稳定,能够去掉
LCD_Clear(); //清屏
LCD_Write_String(0,0,"welcome"); //写入第一行信息
old1=read24c02(110); 28、
old2=read24c02(111);
old3=read24c02(112);
old4=read24c02(113);
old5=read24c02(114);
old6=read24c02(115);
while (1) //主循环
{
num=KeyPro(); //扫描键盘
if(num!=0xff) //如果扫描是按键有效值则进行处理
{
if(i==0) //输入是第一个字符的时候需要把改行清空,方便观看密码
LCD_Write_String(0,1," ");//清除该行
if( 29、i<6) //密码是6位,大于6位时不再输入按键值
{
temp[i]=num;
LCD_Write_Char(i,1,'*');
}
i++; //输入数值累加
if(num==11) //重试键
{i=0;LCD_Write_String(0,1," ");}
}
if(num==12)
{i=0;LCD_Write_String(0,1," ");
if(bj==0)
{
while(i<=6)
{
num=KeyPro(); //扫描键盘
if(num!=0xff) //如 30、果扫描是按键有效值则进行处理
{
if(i==0) //输入是第一个字符的时候需要把改行清空,方便观看密码
LCD_Write_String(0,1," ");//清除该行
if(i<6) //密码是6位,大于6位时不再输入按键值
{
temp[i]=num;
LCD_Write_Char(i,1,'*');
}
i++; //输入数值累加
if(num==11) //重试键
{i=0;LCD_Write_String(0,1," ");}
}
if(num==10)
{
n 31、um=0;
new1=temp[0];new2=temp[1];new3=temp[2];
new4=temp[3];new5=temp[4];new6=temp[5];
old1=new1;old2=new2;old3=new3;
old4=new4;old5=new5;old6=new6; //新密码代替旧密码
write24c02(110,old1);
write24c02(111,old2);
write24c02(112,old3);
write24c02(113,old4);
write24c02(114,old5);
write24c02(115,old6); 32、
LCD_Write_String(0,1,"ok ");
bj=1; DelayMs(220);
DelayMs(220);DelayMs(220);
}
if(close==0)
{
bj=1;i=0;
LCD_Write_String(0,1," ");}
}
}
i=0;
}
if(num==10) //数字10为确认键
{
new1=temp[0];new2=temp[1];
new3=temp[2];new4=temp[3];
new5=temp[4];new6=temp[5];
if(i==7)//6位后的 33、按键输入数值,相当于确认按键(任意按键即可)
{
i=0; //计数器复位
Flag=1;//先把比较位置1
old1=read24c02(110);
old2=read24c02(111);
old3=read24c02(112);
old4=read24c02(113);
old5=read24c02(114);
old6=read24c02(115);
Flag=Flag&(new1==old1)&(new2==old2)&(new3==old3)&(new4==old4)&(new5==old5)&(new6==old6);//比较输入值和已有密码
if(F 34、lag)//如果比较全部相同,标志位置1
{
i=0;
LCD_Write_String(0,1,"Hello Boss! ");//密码正确显示的信息
jdq=0;bj=0;
delay(3000);
jdq=1;
LCD_Write_String(0,1," ");
}
else
{
i=0;c++;
LCD_Write_String(0,1,"error!");//密码错误,提示重新输入
while(c==3)
{
baojingqi();
jb=0;
DelayMs(255);DelayMs(255);
baojin 35、g=1;i=0;jb=1;LCD_Write_String(0,1," ");
if(close==0)
{
i=0;bj=1;jb=1;c=0;baojing=1;LCD_Write_String(0,1," ");
}
}
}
}
else//当密码不是6位数字时按下确认键也算输错密码一次
{
i=0;LCD_Write_String(0,1,"error!");
c++;
while(c==3)
{
baojingqi();
jb=0;
DelayMs(255);DelayMs(255);
36、
if(close==0)
{
i=0;jb=1;bj=1;c=0;baojing=1;LCD_Write_String(0,1," ");
} } } }
if(close==0)
{
i=0;bj=1;jb=1;c=0;
jdq=1;baojing=1;
LCD_Write_String(0,1," ");
jdq=1;
}
}
四、软硬件调试结果
4.1 电路总原理图
电路总原理图如下所示:
4.2 调试结果
各种情况下的的调试结果如下个图所示:依次为开机、开锁、成功修改密码、密码错误。
五、 总结
在这次实验中,用到了单片机和C语言的知识,这两门知识都是非常具有实用性的。在这次实验中再次加深了对此的认识。
首先,在一开始输程序的时候,在程序仿真过程中出现了不能打开头文件的现象,后来经老师知道后知道了头文件的具体作用和使用方式。这些都是以前所没能掌握的。在后面的硬件电路调试过程中也遇到了很多问题,经过不懈努力之后都一一解决了。本次试验为期一周,不得不说,这次试验让我学到了好多东西。也感谢老师的辛勤指导!






