带密码锁的电子钟.docx
《带密码锁的电子钟.docx》由会员分享,可在线阅读,更多相关《带密码锁的电子钟.docx(27页珍藏版)》请在冰豆网上搜索。
![带密码锁的电子钟.docx](https://file1.bdocx.com/fileroot1/2022-11/24/dc24f50c-b439-47ff-85df-0c8f8aa39ed8/dc24f50c-b439-47ff-85df-0c8f8aa39ed81.gif)
带密码锁的电子钟
单片机原理课程设计
设
计
说
明
书
设计题目:
智能钟带密码锁程序设计
设计者:
李海军
班级:
自动化121
学号:
***********************
日期:
2014.01.05~2014.01.16
智能钟带密码锁的设计报告
1.设计的目的
1.通过《单片机原理与应用》课程设计,使学生理解单片机系统设计的方法和设计原则及相应的硬件调试、软件编程方法。
进一步理解单片机应用系统的设计和应用。
2.通过本次课程设计达到理论与实践相结合,提高学生分析问题和解决问题的能力。
3.学会使用电子图书馆的数据库资源进行查找相关文献和资料。
4.初步掌握Keil编程软件和Protues软件进行仿真设计,掌握编写设计说明书的基本方法。
2.设计原则和要求
2.1设计原则
1.合理性。
所设计内容应符合国家相关政策和法令,符合现行的行业行规要求。
2.先进性。
杜绝使用落后,淘汰的产品,不使用未经认可的技术,要充分考虑未来发展。
3.实用性。
考虑降低物耗,保护环境,综合利用等因素。
2.2设计要求
1.独立设计原理图及相应的硬件电路。
2.独立焊接电路板并对电路板调试。
3.针对选择的设计题目,设计系统软件。
软件要做到:
操作方便,实用性强,稳定可靠。
4.设计说明书格式规范,层次合理,重点突出。
并附上设计原理图、电路板图及相应的源程序。
3.设计任务
3.1设计思路
通过查找相关资料,复习单片机所学内容做好知识储备,然后根据题目要求利用Protues软件画出合理的仿真图,再利用Keil软件进行编程设计。
多功能数字时钟和电子密码锁先分开仿真分别写程序,确定都仿真成功以后将两个程序合理的进行无缝连接成为一个程序,最后将调试完成的程序写入同一个Protues仿真图中完成仿真。
3.2设计题目要求
1.多功能数字时钟
(1)在显示屏上显示时间。
时间格式:
LED为“时:
分”;LCD为“时:
分:
秒”。
(2)可显示“年、月、日、周”等信息。
(3)可对时钟的“年、月、日、周、时:
分:
秒”进行更改。
(4)增加COM接口程序,实现用计算机软件程序调节时钟及其附加功能。
(5)扩展定时控制功能(选作)。
(6)扩展计时器功能,可实现30个计时记录。
(7)自定义的扩展功能。
2.电子密码锁
(1)上电启动时,即显示输入密码。
如果用数码管做为显示,输入密码为自定义字符(非正常的数字或字符);如果采用LCD做为显示,输入密码为“*”号。
并要求有中文提示。
(2)有修改密码的功能。
但必须输入原密码后验正后才能修改密码。
且修改密码也要两次验证。
密码一旦更改,不再记忆原密码。
密码为6位键盘码。
(3)密码输入正确,显示开锁标志。
否则,提示密码输入错误,并提示重新输入。
当连续输入三次错误时,中止密码输入,并显示报警标志,2个小时后放可进行下一次密码输入。
(4)设置一个万能的通用密码,在必要时使用。
且此密码长度要超过6位。
(5)设置COM的接口程序,达到计算机可实现上述所有功能。
(6)自定义的扩展功能。
3.3设计流程
4.单片机及各模块选择
4.1AT89C51单片机
AT89C51是一种带4K字节闪烁可编程可擦除只读存储器(FPEROM—FalshProgrammableandErasableReadOnlyMemory)的低电压,高性能CMOS8位微处理器,俗称单片机。
单片机的可擦除只读存储器可以反复擦除100次。
该器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。
由于将多功能8位CPU和闪烁存储器组合在单个芯片中,ATMEL的AT89C51是一种高效微控制器。
其主要特性包括:
(1)与MCS-51兼容
(2)4K字节可编程闪烁存储器(3)三级程序存储器锁定(4)寿命:
1000写/擦循环(5)128×8位内部RAM(6)32可编程I/O线(7)两个16位定时器/计数器(8)5个中断源(9)可编程串行通道(10)片内振荡器和时钟电路
4.2矩阵键盘
考虑到单片机的I/O口是十分宝贵的,并且我设计的万年历在进行年月日、时分秒的调节时是按不同的按键从而进入调节模式,这样一共需要6个按键。
同时还有一个六位的密码锁、一个确认输入密码按键、一个密码锁和万年历模式切换按键以及调节万年历的加减按键,这样一共需要16个按键。
如果直接利用I/O口写的话必定会造成I/O口的浪费,这与设计原则不符,所以我决定采用矩阵键盘,这样只用八个I/O口就可以实现16按键控制。
但是加减按键不能加入到矩阵键盘中,需要用两个I/O口单独引出。
因为我写的程序中switch函数是对矩阵键盘的键值进行寻键的,也就是只能在switch函数中执行一种情况。
假使要对年份进行调节,按过年份调节按键后在按加减键是没有任何反应的。
4.3LCD1602
16代表液晶显示器每行可显示16个字符,02表示显示两行,由字符库可看出显示器显示的数字和字母部分的代码,恰好是ASCII码表中的编码。
单片机控制LCD1602显示时,只需将待显示字符部分的ASCII码写入内部的显示用数据存储器(DDRAM),内部控制电路就可将字符在显示器上显示出来。
当向DDRAM的00H~0FH(第一行)、40H~4FH(第二行)地址中的任一处写入数据时LCD将立即显示出来。
LCD是慢显示器件,所以在写每一条命令前,一定要查询忙标志位BF,即LCD1602是否处于“忙”状态。
如果LCD正忙于处理其他命令,就等待;如果不忙,则向LCD写入命令。
LCD的基本操作包括:
(1)初始化LCD
(2)读状态。
检测“忙”标志位BF,如果BF为1说明LCD正处于忙状态,不能对其写命令;如果BF为0,则可以写入命令。
(3)写命令。
(4)写数据。
将要显示字符的ASCII码写入LCD中的数据显示RAM(DDRAM)(5)自动显示。
我之所以用LCD1602是因为用数码管显示万年历和密码锁每次都要清屏(防止残影产生造成显示乱码)并且数码管由于本身的限制无法显示字符,在实际应用的时候没有LCD方便。
同时,如果选用数码管的话必定会导致I/O的浪费,数码管要进行段码和位码的选择,而LCD则会简单很多。
4.4总系统仿真图
5.系统设计框图
6.主要程序介绍
6.1键盘扫描及键值返回程序
1.键盘扫描程序
unsignedcharKeyScan(void)//键盘扫描函数
{
unsignedcharcord_h,cord_l;//行列值中间变量
P1=0x0f;//行线输出全为0
cord_h=P1&0x0f;//读入列线值
if(cord_h!
=0x0f)//先检测有无按键按下
{
delay(10);//去抖
if((P1&0x0f)!
=0x0f)
{
cord_h=P1&0x0f;//读入列线值
P1=cord_h|0xf0;//输出当前列线值
cord_l=P1&0xf0;//读入行线值
while((P1&0xf0)!
=0xf0);//等待松开并输出
return(cord_h+cord_l);//键盘最后组合码值
}
}return(0xff);//返回该值
}
2.键值返回程序
unsignedcharKeyPro(void)
{
switch(KeyScan())
{
case0xee:
returnkeyval=1;break;//0按下相应的键显示相对应的码值
}
}
说明:
case中有很多还有很多情况,此处并未列写,此处返回的键值方便主函数中的switch语句寻键时比较方便。
并且这样在主函数中调用键盘扫描函数时只需写“KeyPro();”即可。
6.2LCD程序
1.判断液晶的忙碌状态函数:
unsignedcharBusyTest(void)
2.将指令写入液晶模块:
voidWriteInstruction(unsignedchardictate)
3.指定字符显示的实际地址:
voidWriteAddress(unsignedcharx)
4.将数据(字符的ASCII码)写入液晶模块:
voidWriteData(unsignedchary)
5.LCD的初始化:
voidLcdInitiate(void)
说明:
将这些LCD的小程序都写成函数的形式,方便其调用,具体的程序会在附录中给出
6.3显示和中断程序
1.将年份、月份、日期、小时、分钟、秒分别做成函数再调用
2.所有的计时共能全部写在中断程序中
6.4密码锁程序
Flag=1;//先把比较位置1
for(j=0;j<8;j++)//循环比较8个数值,
//如果有一个不等则最终Flag值为0
Flag=Flag&&(temp[j]==dofly_DuanMa[password[j]]);
//比较输入值和已有密码
for(j=0;j<8;j++)//清屏
TempData[j]=0;
if(Flag)//如果比较全部相同,标志位置1
{
TempData[0]=0x3f;//"o"
TempData[1]=0x73;//"p"
TempData[2]=0x79;//"E"
TempData[3]=0x54;//"n"
说明:
主程序中密码锁程序的一部分,密码锁的原理是将按键值存在缓存区中然后与之前设置好的密码数组每一位都经行比较,如果每一位都相同的话则Flag为1,经行OPEN的显示。
如果有一位不相同的话则Flag为0,经行LOCK的显示。
7.设计总结
在本次智能钟带密码锁的课程设计中,学习兼理论与实践相结合,初步掌握综合运用所学知识,增强了动手解决实际问题的能力。
学会了踏实的迈开第一步。
我分析应该将两者先分开设计然后再合在一起。
按照这个思路我准备先进行万年历的设计,开始我想用数码管进行设计,因为原来用数码管做过秒表和一个简单的时钟。
我找到自己原来写的程序,在此之上我又加了一个8个显示的数码管进行年份、月份、日期的显示,但是这样只能用下加的小数点来区分年月日。
一共用两个数码管,一个6位一个8位,但是在画图的时候发现这样会浪费非常多的I/O口,即便是用74LS138等译码器也会用到很多端口,而且也不算很好控制,所以我决定还是用LCD1602来显示。
用LCD显示其实比较简单,像LCD初始化,忙位检测,读命令,写命令等都可以写成函数直接调用,把要显示的字符串写成字符串数组的形式然后直接调用就好。
我设计的电子锁并没有实现可以直接按键改动密码的功能,该密码只能通过对程序中的密码进行更改,也没有实现密码三次输入三次锁屏的效果,所以我设计成了只要输入错误则万年历就不会显示的效果,但是你可以通过重新选择解锁模式经行密码的重新输入,只要输入正确即可显示更改万年历。
开始合成两个程序的时候首先要解决定时器的问题,因为两个程序用的定时器不太一样,但是发现其实相差并不算太大,所以我让两者公用一个定时器,即将两者的中断程序写在同一个中断函数中。
又因为两者要有一个传递的关系即解锁才能显示万年历,所以我把密码锁的程序写在显示万年历的switch函数中,让两者呈现出一种传递关系。
通过这次课程设计,综合运用本专业所学课程的理论和生产实际知识智能钟带密码锁相结合,培养并提高了我独立工作的能力,巩固与扩充了课堂所学内容,掌握智能钟设计的方法,步骤及方案了解了智能钟的基本结构,熟悉了规范和标准同时各科相关的知识都有了全面的复习,独立思考的能力也有了提高。
8.参考文献
[1]邱关源.电路[M].(第四版)北京:
高等教育出版社,2006,241-268
[2]张海兵,李敏.protel电路设计实例与分析[M].北京:
人民邮电出版社,2005,119-205
[3]徐晓秋.单片机原理及接口技术[M].北京:
国防工业出版社,2008,102-112
[4]刘迎春.MCS-51单片机原理及应用教程[M].北京:
清华大学出版社,2005,145-157
[5]张奇,朱宁西.单片机应用系统技术设计:
基于C51的proteus仿真[M].(第二版)北京:
电子工业出版社,2009,245-264
[6]兰建军,伦向敏,关硕编著.单片机原理、应用与Proteus仿真[M].北京:
机械工业出版社,2014.
[7]陈洪财主编.单片机原理与应用技术[M].哈尔滨:
哈尔滨工程大学出版社,2014.
[8]楼然苗,胡佳文,李光飞编著.51系列单片机原理及应用[M].北京:
北京航空航天大学出版社,2014.10.
[9]张刚毅主编.单片机原理及应用[M].北京:
高等教育出版社,2012.11.
[10]周润景,张丽娜,丁莉编著.基于PROTEUS的电路及单片机设计与仿真[M].北京:
北京航空航天大学出版社,2010.01.
9.附录
#include
#include
#include
sbitRS=P2^0;//将RS位定义为P2.0
sbitRW=P2^1;//将RW位定义为P2.1
sbitE=P2^2;//将E位定义为P2.2
sbitBF=P0^7;//将BF位定义为P0.7
sbitP25=P2^5;
sbitP24=P2^4;
sbitP23=P2^3;
unsignedcharcodedigit[]={""};
unsignedcharcodestring[]={"Data:
"};
unsignedcharcodestring1[]={"Password:
****"};
unsignedcharcodestring2[]={"Calendar:
OPEN"};
unsignedcharcodestring3[]={"CalendarLOCK"};
unsignedcharcount;//定义变量统计中断累计次数
unsignedchars,m,h,day,mon;//定义变量储存秒、分钟、小时、天和月份
unsignedcharkeyval;//定义寻键变量
unsignedintyear;//定义变量储存年份
voiddelay1ms()
{
unsignedchari,j;
for(i=0;i<10;i++)
for(j=0;j<33;j++);
}
/*****************************************************
函数功能:
延时若干毫秒
***************************************************/
voiddelay(unsignedcharn)
{
unsignedchari;
for(i=0;idelay1ms();
}
/*------------------------------------------------
按键扫描函数,返回扫描键值
------------------------------------------------*/
unsignedcharKeyScan(void)//键盘扫描函数
{
unsignedcharcord_h,cord_l;//行列值中间变量
P1=0x0f;//行线输出全为0
cord_h=P1&0x0f;//读入列线值
if(cord_h!
=0x0f)//先检测有无按键按下
{
delay(10);//去抖
if((P1&0x0f)!
=0x0f)
{
cord_h=P1&0x0f;//读入列线值
P1=cord_h|0xf0;//输出当前列线值
cord_l=P1&0xf0;//读入行线值
while((P1&0xf0)!
=0xf0);//等待松开并输出
return(cord_h+cord_l);//键盘最后组合码值
}
}return(0xff);//返回该值
}
/*****************************************************
函数功能:
判断液晶模块的忙碌状态
返回值:
result。
result=1,忙碌;result=0,不忙
***************************************************/
unsignedcharBusyTest(void)
{
bitresult;
RS=0;//根据规定,RS为低电平,RW为高电平时,可以读状态
RW=1;
E=1;//E=1,才允许读写
_nop_();//空操作
_nop_();
_nop_();
_nop_();//空操作四个机器周期,给硬件反应时间
result=BF;//将忙碌标志电平赋给result
E=0;//将E恢复低电平
returnresult;
}
/*****************************************************
函数功能:
将模式设置指令或显示地址写入液晶模块
***************************************************/
voidWriteInstruction(unsignedchardictate)
{
while(BusyTest()==1);//如果忙就等待
RS=0;//根据规定,RS和R/W同时为低电平时,可以写入指令
RW=0;
E=0;//E置低电平(根据表8-6,写指令时,E为高脉冲,
//就是让E从0到1发生正跳变,所以应先置"0"
_nop_();
_nop_();//空操作两个机器周期,给硬件反应时间
P0=dictate;//将数据送入P0口,即写入指令或地址
_nop_();
_nop_();
_nop_();
_nop_();//空操作四个机器周期,给硬件反应时间
E=1;//E置高电平
_nop_();
_nop_();
_nop_();
_nop_();//空操作四个机器周期,给硬件反应时间
E=0;//当E由高电平跳变成低电平时,液晶模块开始执行命令
}
/*****************************************************
函数功能:
指定字符显示的实际地址
***************************************************/
voidWriteAddress(unsignedcharx)
{
WriteInstruction(x|0x80);
}
/*****************************************************
函数功能:
将数据(字符的标准ASCII码)写入液晶模块
***************************************************/
voidWriteData(unsignedchary)
{
while(BusyTest()==1);
RS=1;//RS为高电平,RW为低电平时,可以写入数据
RW=0;
E=0;//E置低电平(根据表8-6,写指令时,E为高脉冲,
//就是让E从0到1发生正跳变,所以应先置"0"
P0=y;//将数据送入P0口,即将数据写入液晶模块
_nop_();
_nop_();
_nop_();
_nop_();//空操作四个机器周期,给硬件反应时间
E=1;//E置高电平
_nop_();
_nop_();
_nop_();
_nop_();//空操作四个机器周期,给硬件反应时间
E=0;//当E由高电平跳变成低电平时,液晶模块开始执行命令
}
/*****************************************************
函数功能:
对LCD的显示模式进行初始化设置
***************************************************/
voidLcdInitiate(void)
{
delay(15);//延时15ms,首次写指令时应给LCD一段较长的反应时间
WriteInstruction(0x38);//显示模式设置:
16×2显示,5×7点阵,8位数据接口
delay(5);//延时5ms ,给硬件一点反应时间
WriteInstruction(0x38);
delay(5);
WriteInstruction(0x38);//连续三次,确保初始化成功
delay(5);
WriteInstruction(0x0c);//显示模式设置:
显示开,无光标,光标不闪烁
delay(5);
WriteInstruction(0x06);//显示模式设置:
光标右移,字符不移
delay(5);
WriteInstruction(0x01);//清屏幕指令,将以前的显示内容清除
delay(5);
}
/******************************************************************************
函数功能:
显示小时
******************************************************************************/
voidDisplayHour()
{
unsignedchari,j;
i=h/10;//取整运算,求得十位数字
j=h%10;//取余运算,求得各位数字
WriteAddress(0x44);//写显示地址,将十位数字显示在第2行第5列
WriteData(digit[i]);//将十位数字的字符常量写入LCD
WriteData(digit[j]);//将个位数字的字符常量写入LCD
}
/******************************************************************************
函数功能:
显示分钟
******************************************************************************/
voidDisplayMinute()
{
unsignedchari,j;
i=m/10;
j=m%10;
WriteAddress(0x47);//写显示地址,将十位数字显示在第2行第8列
WriteData(digit[i]);
WriteData(digit[j]);