找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 54474|回复: 35
收起左侧

基于单片机的电子密码锁设计(终稿)

  [复制链接]
创新看看看看 发表于 2019-1-10 11:01 | 显示全部楼层 |阅读模式
基于单片机的电子密码锁设计
摘要:锁,是指加在门、箱子、抽屉等物体上的封缄器,要用专用的钥匙才能打开。自古以来锁都是人们财产安全乃至生命安全的一种重要保障。伴随着人类历史的发展和人们对自身财产安全和人生安全的重视,各种各样的多功能的锁具也相继出现,人们使用的锁具也由传统的机械式锁逐渐发展为安全性能更好,功能更多的电子密码锁。
在现代社会,电子密码锁已是一个被大家所熟识的名词。相信很多人的小区大门上的锁都用的是电子密码锁。本文中将要介绍的电子密码锁是一种通过判断密码输入是否正确来控制电路或是芯片的工作状态,进而控制锁的打开和闭合,从而完成开锁、闭锁任务的电子锁装置。
本设计是基于AT89C51单片机为控制核心的密码锁设计方案。在本次基于单片机的电子密码锁设计中,将采用AT89C51单片机作为控制核心,配合相应的电路和软件程序,实现密码的输入和修改、信息的显示、键盘的锁定、系统报警、开锁和闭锁等功能。在设计中,利用识别密码是否正确来开锁或报警,通过串行存储器AT24C02来实现密码的修改和存储。
本文设计的密码锁具有安全性高、功耗低、操作简单等优点。

The Design Of Electronic Code Lock Based On Single Chip Microcomputer
AbstractAs sealed device, the lock added to such objects like door, box, drawer etc, can be opened with specified key. Since the ancient time, lock is a kind of security, guaranteeing people’s property safety even life safety. With the development of human history and more attention paid to property safety and life safety, various multi-functional locks can be seen in the world. Traditional mechanical locks people use in life are gradually replaced by electronic combination lock characterized with better safety performance and more functions.
In the modern society, electronic combination lock is a noun familiar to everyone. Usually, the door of community is equipped with electronic combination lock. This paper will introduce an electronic combination lock. This lock controls the electric circuits and the performance of chips by identifying password. Thus, the openness and closeness of the lock can be controlled, and the task of electronic lock device can be completed.
This design is about combination lock, based on the AT89C51 single-chip microcomputer as the core of controlling goals. This design in which the AT89C51 single-chip microcomputer as the core of the controlling goals cooperated with relevant circuits and software programs can achieve such functions like password input and modification, information display, keyboard locking, warning system, and the openness and closeness of the lock etc. In this design, the openness of lock and alarming are according to identifying the password, and password revision and storage is achieved by the function of the AT24C02 serial storage.
Electronic combination lock described in this paper is characterized with high security, low power consumption, and simplicity of operation etc.

目录
第1章 绪论
1.1 电子密码锁简介
1.2 电子密码锁设计的背景及意义
1.3 电子密码锁的现状及发展趋势
第2章 总体设计
2.1 设计分析
2.2 系统结构
第3章 硬件电路设计
3.1 单片机接口分配
3.2 单片机最小系统设计
3.2.1 时钟电路
3.2.2 复位电路
3.2.3 最小系统
3.3 矩阵键盘设计
3.4 LCD显示模块设计
3.5 掉电存储模块
3.5.1 I2C总线
3.5.2 AT24C02简介
3.6 开锁机构
3.7 报警机构
3.8 硬件综合设计
第4章 软件设计
4.1 软件总体设计
4.2 键盘扫描子程序
4.3显示模块子程序
4.4 掉电存储子程序
4.4.1 写操作方式
4.4.2 读操作方式
4.5 定时器中断子程序
4.6 密码输入子程序
4.7 报警子程序
第5章 联合仿真和调试
第6章 实物设计和制作
总结
致谢
参考文献
附录1 设计实物图
附录2 程序源代码
           
第1章 绪论
1.1 电子密码锁简介
什么是电子密码锁?“一种通过密码输入来控制电路或者是芯片工作,从而控制机械开关的闭合,完成开锁、闭锁任务的电子产品。”——百度百科上是这样解释的。简单来说,电子密码锁就是能够实现密码等信息的设置、存贮、识别和显示,以及报警信号的接收和发送等功能的电子器件。
              电子密码锁相对传统机械锁有以下几个优势:
              (1)密码可更改。用户可以随时更改密码,以确保密码锁的安全性和可靠性。可以更改密码这一功能同时也避免了人员的更替而使锁的安全性降低,这是传统钥匙锁所不具备的功能。
              (2)操作简单。无论是开锁还是更改密码,只要识字的人都能够直接使用,使用方法简单而不繁琐。
              (3)能够报警。报警功能无疑更增加了锁的安全性。
              (4)无法“技术”破解。不知道密码就无法打开锁,即使是惯偷也只能望“锁”兴叹。
1.2 电子密码锁设计的背景及意义
在现代文明社会,虽然人们的道德素质越来越高,“梁上君子”、“三只手”等人群相对旧社会大大减少,但是像“某某小区遭入室行窃,居民损失XXXX”之类的新闻我们还是经常能够在报纸上看到的。随着人们生活水平和自身防范意识的提高,个人财产安全和人身安全的问题也越来越受到人们的重视,拥有一把能够有效保证居民财产安全和人身安全的锁具也越发的重要起来。
锁具的起源可以追朔到人类社会财产私有化的出现,锁具从其出现到发展至今天的高科技化、信息化,已经有若干年的历史了。经过若干年的使用和研究,人们对锁具的结构、机理也了解得相当透彻了,因此,不使用钥匙就能将锁打开的方法也层出不穷。特别是传统的机械锁,由于其构造简单,在惯偷面前,甚至能够只用一根铁丝就直接将其打开,使其失去了保障用户个人财产安全的意义。
那么,如何才能提高锁具保障用户财产安全的有效性呢?在信息现代化的今天,锁具也应该向高精度、高安全性的智能化、信息化发展。自20世纪70年代第一块单片机芯片TMS-1000于美国德克萨斯仪器公司面世以来,基于其体积小、价格低廉、个性突出等特点,越来越多的电子产品开始采用单片机芯片作为核心控制部件。在这样的大环境下,基于单片机的电子密码锁也应运而生。这种电子密码锁是以单片机为核心,配以相应的硬件电路和软件程序,实现密码的设置、存贮、识别和显示,以及报警信号的接收和发送等功能,具有操作快、修改密码简单、安全性高、功耗低等优点。基于单片机的电子密码锁的面世使人们的自身财产安全有了更多的保障,同时也促进了安全信息系统的发展,是安全信息系统的一大进步。
基于单片机的电子密码锁的出现,在一定程度上解决了用户私人财产安全的问题。但是,时代在发展,社会在进步,任何事物只有不断地进步才能适应时代发展的需求。电子密码锁虽然有安全性高、操作简单等优点,但是却不如机械锁价格低廉,因此,在市场上的主流产品还是机械锁。电子密码锁要想取代机械锁成为市场上的主流,就必须不断改进,在具有更多功能的同时向更智能化和更低成本化发展。这就需要我们不断研究电子密码锁的设计方法和实现原理。因此,研究基于单片机的电子密码锁的设计是很有必要且具有现实意义的。
1.3 电子密码锁的现状及发展趋势
目前,和西方发达国家相比,我国的电子密码锁技术还相对落后。在西方发达国家,电子密码锁的种类已经很齐全,技术也比较先进,且在各个领域得到了广泛应用。在我国,电子密码锁技术却才相当于国际上七十年代的水平,相对来说还很落后。20世纪80年代以来,随着各种电子集成电路的出现,特别是单片机的面世,电子密码锁得到了很大的发展。相对于笨重而构造简单的传统机械锁来说,电子密码锁具有体积小,可靠性高的优势。但是就目前而言,电子密码锁的价格相对较高且需要有电源提供能量,使得其使用还局限在一定范围,特别是在国内,各种条件的制约使得电子密码锁暂时难以普及。
              尽管电子密码锁还存在着一些缺陷,但是其安全性高、方便易用、能够智能报警的优势却是传统钥匙锁取代不了的,而且随着电子信息技术的发展和各种电子器件的价格的不断降低,电子密码锁也将往低成本、多功能的方向发展。

第2章 总体设计2.1 设计分析
一个电子密码锁,应该能实现以下功能:
(1)能够从键盘中输入密码,并相应地在显示器上显示‘*’;
(2)能够判断密码是否正确,正确则开锁,错误则输出相应信息;
(3)能够实现密码的修改;
(4)断电或者单片机复位后能够保存之前的操作,比如密码的修改;
(5)在操作错误达到一定次数后能够报警;
(6)在一定时间内没有任何按键操作则关闭显示器,并锁定键盘,禁止键盘输入(单片机复位后锁定取消);
(7)设置一个备用密码。为了防止用户忘记密码而开不了锁,应该在经常使用的密码外再设置一个备用密码以防万一。此备用密码应该只有少数人知道,比如小区管理员。
根据以上分析,本次电子密码锁设计的主要重点是以下几个部分:4x4矩阵键盘设计、LCD信息显示、密码的掉电存储和密码的比较和处理。当然,除了这几个部分外还有定时器/计数器计时中断和报警等功能模块。
本设计应该按以下步骤去实现:
第一步:选择材料和器件。
第二步:根据单片机原理和模拟电子技术基础等理论知识在PROTEUS
中画出仿真图。
第三步:检查仿真图,确保其能够实现所有功能。
第四步:根据需要实现的功能逻辑画出程序流程图。
第五步:根据程序流程图和PROTEUS仿真图,使用 KEIL软件进行程序的编写和调试。
第六步:将KEIL和PROTEUS联合仿真、调试,查看是否实现所有功能。
第七步:根据PROTEUS仿真图焊接器件。
第八步:实物调试。
基于实际情况,在设计中,用发光二极管代替电磁锁,二极管亮则代表锁开,二极管不亮则代表锁关。
2.2 系统结构
本设计系统主要由单片机芯片、矩阵键盘、LCD显示模块、掉电存储模块、报警机构和开锁机构组成。如图2-1所示。

2-1 系统总体设计结构图
              时钟电路给单片机提供晶振频率,复位电路不但使单片机上电复位,还能在使用过程中通过需要通过按键再次手动复位,矩阵键盘提供按键的输入,LCD模块显示信息,掉电存储负责密码的存储,开锁机构和报警机构分别负责开锁和报警功能。
设计中,单片机选用AT89C51,LCD显示模块选用LCD1602液晶显示器,串行存储器选用电可擦除存储器AT24C02,开锁机构用发光二极管代替,报警机构选用蜂鸣器。

第3章 硬件电路设计
3.1 单片机接口分配
AT89C51单片机在一块芯片上集成了CPU、RAM、ROM、定时器/计数器和多种I/O功能部件,具有一台微型计算机的基本结构,按功能可以将其分成八个组成部分:微处理器(CPU)、数据存储器(RAM)、程序存储器(ROM/EPROM)、特殊功能寄存器(SFR)、I/O口、串行口、定时器/计数器及中断系统。
              在本设计中,单片机的各个接口我是这样分配的:P0口接一个上拉电阻后与LCD1602的8位双向数据端D0~D7相接,P1口用作矩阵键盘接口,P2口的P2.0~P2.2和LCD1602的4~6引脚相接,P2.5和P2.6接串行存储器AT24C02,P3口用作开锁电路和报警电路的接口。
3.2 单片机最小系统设计
单片机最小系统就是指能使单片机工作的最少的器件构成的系统。因为单片机已经包含了数据存储器和程序存储器,所以只要在其外部加上时钟电路和复位电路就可以构成单片机最小系统。
3.2.1 时钟电路
单片机工作需要晶振给CPU提供频率,时钟电路就是给单片机提供晶振频率的电路。图3-1是时钟电路的PROTEUS仿真图。

3-1 时钟电路
单片机允许的振荡晶体可在1.2~24MHz之间选择,一般为11.0592MHz,电容C2,C3的取值对振荡频率输出的稳定性、大小及振荡电路起振速度有一定的影响,可在20~100pF之间选择,典型值位30pF。
3.2.2 复位电路
计算机每次开始工作,CPU和系统中的其他部件都必须要有一个确定的初值,即复位状态。图3-2是单片机复位电路仿真图。

3-2 复位电路
              单片机RST引脚是高电平有效。单片机在上电瞬间C1充电,RST引脚端出现正脉冲,只要RST断保持两个机械周期(大约10ms)以上的高电平,单片机就能复位。在单片机工作后,如果还想再次复位,只需按下开关,单片机就能重新变成复位状态。
              当晶体振荡频率为12MHz时,RC的典型值为C=10μF,R=8.2KΩ。
3.2.3 最小系统
单片机加上时钟电路和复位电路就构成了能使其正常工作的最小系统。
单片机最小系统是单片机正常工作的基础,任何一个单片机系统设计都是基于单片机最小系统的基础上来完成的,而在单片机系统实物设计中,最应该首先解决的也应该是单片机最小系统问题,只有保证了单片机最小系统的正确性,才能保证接下来的其他模块的正确设计。
图3-3是单片机最小系统的完整仿真图。

3-3 单片机最小系统
3.3 矩阵键盘设计
一组键或者一个键盘,需要通过接口电路和CPU相连接,CPU可以采用查询接口或者中断的方式了解有没有键被按下,并检查是哪个键被按下。无论是查询方式还是中断方式都要用到单片机的I/O口。由于单片机I/O口较少的原因,当系统中需要用到较多按键时,为了能够更合理更有效地利用单片机的I/O口,一般采用矩阵键盘的方式来实现多按键的功能。
图3-4是4x4矩阵键盘在PROTEUS中的电路原理仿真图。

3-4 4x4矩阵键盘
矩阵键盘又叫做行列式键盘。行列式键盘的硬件结构比较简单,由行输出口和列输出口构成行列式键盘,按键设置在行、列交点上。图3-4中,P1.0~P1.3是行输出口,P1.4~P1.7是列输出口。行输出口和列输出口不相交,只有当键被按下时相应的行和列才能相连。如此,只要检测行和列是否相连就可以知道是否有键按下。
由于按键设置在行、列线交点上,行、列分别连接到按键开关的两端,平时无键按下时,行线处于高电平,假设列线为低电平,当有键按下时,按下的键就会将相应的行和列连通,使得对应的行线被列线拉低,也变为低电平。这就是识别矩阵键盘是否有键被按下的关键。
当确定有键被按下时,通过逐行扫描,读出I/O口的值可以知道哪一行的值被改变了,被改变了的行即是被按下的按键所在行。同时,由于每个键都有它的行值和列值,行值和列值得组合就是这个按键的编码,当算法一定时,每个按键的编码是固定的,且各个按键的编码互不相同,所有通过读I/O的值还能具体知道是哪一个键被按下,这样就实现了键盘的识别。
3.4 LCD显示模块设计
              在单片机应用系统中,常用的显示设备有单个发光二极管、八段LED显示器、液晶显示器(LCD)、屏幕显示器(CRT)等。在本次设计中,基于设计所要实现的功能和节约成本等实际情况,我采用LCD1602作为本次设计的显示器。
LCD1602是一种字符型液晶显示器,是一种专门用于显示字母、数字、符号等的点阵式液晶显示器。LCD1602的显示容量为16x2个字符(可以显示2行,每行显示16个字符),芯片工作电压为4.5~5.5V,工作电流为2.0mA(5.0V),模块最佳工作电压是5.0V。
LCD1602具有16个引脚,如表3-1所示。在LCD1602的有关设计中,主要是通过编写程序控制LCD1602的4、5、6引脚来实现数据或者指令的写入和执行,再通过数据或者指令的写入和执行来进一步实现LCD1602的显示功能。
              表3-1是LCD1602的16个引脚和引脚对应功能。

3-1 LCD1602引脚说明

引脚号

引脚名  

                  功能

  1

  VSS

电源地

2

  VCC

电源(+5V)

  3

  VEE

对比调整电压

  4

  RS

0:输入指令;1:输入数据

  5

  R/W

0:向LCD写指令或者数据;1:从LCD读取信息

  6

   E

使能信号,1:读取信息,1→0:执行命令

  7

  DB0

数据总线(最低位)

  8

  DB1

数据总线

  9

  DB2

数据总线

  10

  DB3

数据总线

  11

  DB4

数据总线

  12

  DB5

数据总线

  13

  DB6

数据总线

  14

  DB7

数据总线(最高位)

  15

   A

LCD背光电源正极

  16

   K

LCD背光电源负极

作为一个字符型液晶显示器,LCD1602内部自带有一个字符发生存储器,此字符发生存储器就相当于一个字符集。LCD1602的字符集中存有160个不同的字符,这些字符包括了英文大小写字母、阿拉伯数字、标点符号等一些经常用到的字符。字符集中的每一个字符都对应有一个固定的ASCII码值,通过显示ASCII码对应的字符图像就能够实现对应字符的显示。
图3-5是PROTEUS中显示模块的仿真图。
由于LCD要正常工作必须提供足够的电流,因此在实际应用为了保证显示器能够正常工作,应在数据端口接一上拉电阻。图3-5中RP1同时还是P0口的上拉电阻。

3-5 LCD模块仿真图
3.5 掉电存储模块              3.5.1 I2C总线
I2C总线为同步串行数据传输总线,用于单片机的外围扩展。I2C总线上所有的外围器件都有规范的器件地址,器件地址有7位组成,它和1位方向为构成了I2C总线器件的寻址字节。寻址字节格式如下:
3-2  I2C寻址格式

D7

D6

D5

D4

A2

A1

A0

R/

              D7~D4是I2C总线的器件地址,由厂家在器件出厂时给定,对于AT24C系列固定为1010。A2~A0根据电路中A2,A1,A0引脚接电源或者接地而不同,接地则相应位为0,接电源则相应位为1。R/位为I2C总线的数据方向位,决定I2C总线的数据传送方向,高电平为接收,低电平为发送。

图3-6为I2C总线的数据传送时序。

3-6 I2C总线数据传送时序
              起始信号:时钟线SCL为高电平,数据线SDA出现由高向低的负跳变时,启动I2C总线。
              停止信号:时钟线SCL为高电平,数据线SDA出现由低向高的正跳变时,停止I2C总线。
              应答信号位ACK:I2C总线进行数据传送时,每成功传送一个字节的数据后,接收器件都必然产生一个应答信号,即在第9个时钟周期时将SDA线拉低,表示其已经成功接收到一个8个数据。图3-6中的第9个时钟脉冲对应于应答位。应答位对应的数据线SDA上是低电平时为应答信号,是高电平则为非应答信号。为非应答信号时,证明器件没有成功接收到一个8位数据。
              数据传送位:图3-6中的第1~8个时钟脉冲为一个字节的8位数据传送位。脉冲为高电平时,串行传送数据;脉冲为低电平时,不传送数据,允许总线上数据线SDA的电平发生变化。在I2C数据传输过程中,只有当SCL为低电平时才允许SDA变化,当SCL为高电平时,不允许SDA电平改变。当然,起始信号和停止信号是例外。因此,当SCL为高电平时,SDA的变化被看成是起始信号或者停止信号。
3.5.2 AT24C02简介
              AT24C02是Atmel公司生产的AT24CXX系列串行E2PROM中的一种,是具有I2C总线接口功能的电可擦除串行存储器。AT24C02内部含有256个字节,通过I2C总线接口进行操作,有一个专门的写保护功能(WP=1时即为写保护)。
              图3-7是AT24C02的引脚排列图。
              其引脚功能如下:
              A0~A2:器件地址输入端。在本设计中,A0~A2都接地,故其值都为0。
              Vcc:+1.8~6.0V工作电压。
              Vss:地或电源负极。                                                                          3-7 AT24C02引脚
              SCL:串行时钟输入端。数据发送或者接收的时钟从此引脚输入。
              SDA:串行/数据地址线。用于传送地址和发送或者接收数据,是双向传送端口。
              WP:写保护端。WP=1时,只能读出,不能写入;WP=0时,允许正常的读写操作。
              图3-8为PROTEUS中E2PROM的仿真图。

3-8 AT24C02仿真图
              P2.5为串行时钟输入线接口,P2.6为数据线接口。A0,A1,A2接地,所以单片机在读AT24C02时,器件地址为:10100001B=0A1H;在写AT24C02时,器件地址为:10100000B=0A0H。WP=0,允许单片机进行读写操作。
3.6 开锁机构
              在基于单片机的电子密码锁设计中,用户需要输入密码,密码正确则发出开锁信号开锁。
因为在设计中是以发光二极管代替电磁锁,二极管亮代表锁开,因此可以设计一个简单的可以点亮二极管的电路系统代替电磁锁开锁机构。如图3-9所示。

3-9 二极管电路
              由于单片机I/O口默认为高电平,故初始时二极管不亮,代表锁是闭着的。当用户输入密码并验证正确时,发出开锁信号(使P3.6=0)。
3.7 报警机构
在这次基于单片机的电子密码锁设计中,通过控制蜂鸣器的发音来实现系统的报警功能。
              蜂鸣器是一种采用直流电压供电的电子讯响器。图3-10是用蜂鸣器模拟的报警机构仿真图。

3-10 报警机构模拟仿真图

当P3.7口有脉冲信号输入时,蜂鸣器SPEAKER即会发音。通过控制输入脉冲的频率还能控制蜂鸣器的发音频率。
              当用户输入密码错误次数达到预设警告次数时,系统调用报警子程序,使蜂鸣器发出报警音,同时禁止键盘输入。
3.8 硬件综合设计
              根据电路原理,在PROTEUS中画出各功能模块的仿真图,各个功能模块验证正确后,将所有模块集合到一个电路设计图中,画出具有所有功能的总体硬件仿真图。
              图3-11即为本设计的硬件综合设计图。

3-11 基于单片机的电子密码锁设计仿真图
              待程序编写好后,将KEIL和PROTEUS联调,观察此电路设计图可以知道各个功能模块和器件的工作情况。届时,根据实际情况可以适当修改电路图或者程序,以达到设计的目的。

第4章 软件设计4.1 软件总体设计
根据电子密码锁的实际应用要求和其应该具有的功能,本次设计的主程序流程图如图4-1所示。

4-1 主程序流程图
此次基于单片机的电子密码锁设计的软件设计方面的主要问题是如何实现键盘输入、信息显示、密码的掉电存储以及密码的比较和处理。本设计接下来将分步解决这几个问题。
4.2 键盘扫描子程序
矩阵键盘扫描子程序应该具有以下2个基本的功能:(1)能判断是否有键按下;(2)能确定是哪个键被按下。其软件管理主要分为以下三步来完成:
  • 判断整个键盘是否有键按下。
让所有的行为0,然后读列的数值。如果列的数值全部为1,说明没有键被按下,否则说明有键被按下。
  • 识别被按下的键的位置。
采用一行一行的扫描方法,逐行输出0,然后读列的值。如果列的数值全部为1,说明不是这一行的按键被按下,扫描下一行,如果列的数值不全为1,则说明被按下的按键时在这一行。
  • 查键值表,返回键值对应信息,以便确定各按键应该完成的功能。
采用某种算法,将被按下的键所在的行和列的信息合并成为一个信息,该信息即为此键的键值。用相同的方法给每一个键确定键值。在给按键确定键值时必须采用同一种算法,并且计算出来的键值应该是互不相同的。
              本设计中各按键对应键值如表3所示。
4-1 按键键值表

按键名称

键值

按键名称

键值

      1

   0x7e

      9

   0x7d

      2

   0xbe

      0

   0xbb

      3

   0xde

      A

   0xdb

      4

   0xee

      B

   0xeb

      5

   0x7d

      C

   0x77

      6

   0xbd

      D

   0xb7

      7

   0xdd

      E

   0xd7

      8

   0xed

      F

   0xe7


键盘扫描子程序的流程图如图4-2所示:

4-2 键盘扫描程序流程图
从流程图可以看出,此键盘识别程序是通过逐行扫描来确定是否有键按下,当确定某一行有键按下时,再在该行中确定被按下的是哪一个按键。
              在本设计中,P1.0~P1.3为行输出口,P1.4~P1.7为列输出口。初始时将P1.0~P1.3值赋为0,P1.4~P1.7赋为1,CPU始终扫描P1端口,当P1值不为0xf0时,有按键被按下,否责没有按键被按下。
              在实际应用中,为了防止因为按键的抖动而使得一次按键按下被当做2次或者2次以上处理,应该进行按键消抖程序设计,只要方法是当有键闭合时,延时一段时间再确定是否还有键闭合,若有,则为键有效闭合,若无则为键无效闭合,返回重新扫描。
              图4-3为CPU扫描第一行键盘的程序流程图。单行键盘扫描能够准确确定被按下键所在的位置。

4-3 单行键盘扫描流程图
              在本设计中,数字键0~9对应输入数字0~9,功能键A键是确定键,B键是取消键,C键是改密码键,D键位闭锁键,E键和F键闲置不用。
4.3显示模块子程序
              由于设计中要求能够显示密码输入界面、密码输入信息、密码正确后提示界面、密码错误后提示界面和修改密码相关界面等信息,故要用到很多个显示子程序来显示不同的内容。虽然显示子程序很多,但是由于其显示原理都一样,所以我就不一一介绍各个子程序了,只要能够熟练使用LCD1602各个指令,这些大同小异的子程序也就不在话下了。
              下面为LCD1602写命令子函数和写数据子函数。
//************************************************
//函数:写命令函数
//功能:调用该函数可能定义1602液晶显示器的各种命令
//************************************************
void write_1602com(uchar com)
{
while(lcdbusy());  //lcdbusy()为检测LCD忙标志函数,lcdbusy()返回值
              e=0;                                          //为1,则说明LCD正在工作
              rw=0;
              rs=0;                                          //rs=0时为写命令
              P0=com;                            //待写命令赋P0
              delay(1);
              e=1;                                          //e由1→0跳变时为执行命令
              delay(1);
              e=0;
}
//*************************************************
//函数:写数据函数
//功能:调入该函数可以向1602液晶显示器输入数据
//*************************************************
void write_1602dat(uchar dat)
{
              while(lcdbusy());
              e=0;
              rw=0;
              rs=1;                            //rs=1时为写数据
              P0=dat;
              delay(1);
              e=1;
              delay(1);
              e=0;
}
以下是LCD1602从第一行第一列开始显示N(0<N≤16)个字符的程序流程图。

4-4 显示字符程序流程图
              显示模块子程序主要指根据LCD1602的命令和引脚功能来编写,程序逻辑相当简单。
4.4 掉电存储子程序
              掉电存储子程序就是将初始密码写进AT24C02,单片机每次复位后从AT24C02中读取密码用来和输入的密码进行比较,以判断输入的密码的正确性。当密码修改成功后,将新密码写入AT24C02,以便单片机下次复位后使用。
              掉电存储模块子程序主要涉及AT24C02的写操作方式和读操作方式。AT24C02有2种不同的写操作形式和3种不同的读操作方式。
              4.4.1 写操作方式
两种写操作方式为:字节写和页写。
(1)字节写。字节写模式下,主机发送(R/位置为0)起始命令和器件地址信息,主机在收到AT24C02的应答信号后,发送1~8位字节地址,写入AT24C02的地址指针中。对于高于8位的地址,主机连续发送两个8位字节地址写入AT24C02中,主机在收到AT24C02的另外一个应答信号后再发送数据到被寻址的存储单元,AT24C02再次应答,并在主机发出停止信号后开始内部数据的擦写。AT24C02在内部擦写过程中不响应主机的任何请求,因此在两次写操作之间应该留有足够的反应时间。
              字节写的时序是这样的:
              地址只有8位:开始→器件地址→响应→8位字节地址→响应→数据→响应→停止
              地址高于8位:开始→器件地址→响应→高8位字节地址→响应→低8位字节地址→响应→数据→响应→停止
              本次设计中采用的是字节写方式。图4-5为字节写的程序流程图。

4-5 将字节写入AT24C02的程序流程图
(2)页写。页写和字节写所不同的是:字节写一次只能写入一个字节数据,页写一次可以写入8个或16个字节数据。
页写的时序是这样的:
              地址只有8位:开始→器件地址→响应→8位字节地址→响应→数据1→响应→……→数据N→响应→停止。
              地址高于8位:开始→器件地址→响应→高8位字节地址→响应→低8位字节地址→响应→数据1→响应→……→数据N→响应→停止。
4.4.2 读操作方式
              三种不同的读操作方式为:读当前地址内容(立即地址读取)、读随机地址内容(随机地址读取)和读顺序地址内容(顺序地址读取)。
              (1)读当前地址内容。AT24C02的地址计数器内容为最后操作字节的地址加1,所有如果上次读写操作的地址为N,采用读当前地址内容方式读地址应该从N+1地址处开始。AT24C02接收到器件地址信号并且I2C总线允许接收数据(R/=1),则首先发送一个应答信号然后输出数据。数据输出完毕后,主机发送停止信号,读操作完毕。
              (2)读随机地址内容。这种读操作方式允许主机读出AT24C02的任意字节。主机置通过R/位为0,发送开始信号、AT24C02地址和欲读取的字节数据地址来执行一次伪操作,在AT24C02应答后,主机再一次发送开始信号和AT24C02的地址,此时R/位置1,AT24C02响应并应答信号,然后输出字节数据,最后主机以一个停止信号结束数据的读取。
              (3)读顺序地址内容。读顺序地址内容操作方式通过立即读或随机地址读操作来启动,主机在AT24C02发送完一个8位数据后产生一个应答信号,告知AT24C02主机要求更多数据。AT24C02收到主机的应答信号后继续发送数据,直到主机不发送应答信号响应而发送停止信号后操作结束。
              下面是三种不同的读操作方式时序对比:
              读当前地址内容:开始→读器件地址→响应→数据→无响应→停止。
              读随机地址内容:开始→写器件地址→响应→要读的字节地址→响应→开始→读器件地址→响应→数据→无响应→停止。
              读顺序地址内容:开始→写器件地址→响应→要读的字节地址→响应→开始→读器件地址→响应→数据1→响应→……→数据N→无响应→停止。
              本次设计中采用的是读随机地址内容操作方式。

程序流程图如图4-6所示。

4-6 AT24C02相应地址内容的程序流程图
              掉电存储功能就是通过调用向AT24C02写字节数据的程序和从AT24C02读字节数据的程序来实现的。由于AT24C02在内部擦写过程中不会应答任何来自主机的请求,所以当向AT24C02连续写多个字节数据时有可能不成功,这可以通过对同一字节数据写多次的方法来实现。
4.5 定时器中断子程序
              为了防止户主以外的“借用”户主的密码,当没有任何按键被按下的状态持续一段时间后(比如20S内),应该马上关闭显示器屏幕,同时禁止按键的输入。这样做可以避免因为户主忘记退出系统而使他人有机可乘。
图4-7为实现此功能的程序流程图。

4-7 定时关闭屏幕和锁定键盘输入程序流程图
              程序中,使定时器0每50毫秒产生一次中断,因此中断每产生20次为1秒,当秒数为20时(每次有键输入时都执行50毫秒数和秒数清0操作),关闭屏幕,并禁止键盘输入。
              当定时/计数器工作在定时方式时,定时时间的计算公式为:
定时时间=(216 —计数初值)×定时周期
              晶振频率为12MHz时,定时/计数器的定时周期为1μs,所以定时50ms的定时/计数器初值为:
计数初值=216 —50000
4.6 密码输入子程序
              当从键盘输入密码时,应当将输入的密码存放,以便用做密码的相关操作(判断,修改和保存等),同时每输入一位密码应相应地在显示器上显示一个“*”号。图4-8是密码输入子程序的流程图。

4-8 密码输入程序流程图

输入密码时,当输入的密码位数小于6位的时候,每按下一次数字键,就将此数字存入数组。当输入的密码位数不小于6位时,再次按下数字键,程序就不进行任何处理,继续扫描键盘,此时只有按下确定键或者取消键程序才做出相应反应。取消键的功能是退格。当不小心输错密码时,可以退格将输错的位清除,退格一次密码的位数减一位,同时显示器上的“*”个数也减1。当输入的密码位数变为0时,取消键不再起作用。
4.7 报警子程序
              报警子程序的原理很简单,即当输入密码错误次数超过规定的最高允许次数时,不断给蜂鸣器脉冲,使其不断发音。同时,由于CPU一直在给蜂鸣器提供脉冲,故无暇处理诸如密码扫描等事件,也就是说,在蜂鸣器报警的同时也屏蔽了键盘的输入。
              本次设计中,密码输入错误次数不得高于3次。图4-9为报警子程序流程图。

4-9 报警子程序流程图

第5章 联合仿真和调试
在PROTEUS中画出仿真电路图和在KEIL中编写出程序后,需要验证其正确性和可行性,最好的办法就是就它们联合起来仿真和调试。
              KEIL和PROTEUS都是单片机爱好者和单片机相关工作者经常用到的具有相当好的辅助功能的软件工具。KEIL是一款具有强悍功能的51系列兼容单片机C语言软件开发系统,为用户提供丰富的库函数和功能强大的集成开发调试工具,其生成目标代码的效率非常之高,且多数语句生成的汇编代码很紧凑,容易理解。PROTEUS是目前世界上唯一一款将电路仿真软件、PCB设计软件和虚拟模型仿真软件三合一的设计平台。PROTEUS不仅具有其它电子设计自动化工具软件的仿真功能,还能仿真单片机及外围器件,是目前最好的仿真单片机及外围器件的工具。
              KEIL和PROTEUS都是单片机相关设计中经常用到的软件,KEIL和PROTEUS的联合仿真和调试能够相当清晰地反映系统的各个功能模块和器件的工作情况。通过KEIL和PROTEUS的联合调试,单步执行程序或者在程序中设置断点,可以有效地查看各语句的执行情况和各变量的值,从而找到程序中的错误。
              KEIL和PROTEUS的联合调试和仿真步骤如下:
(1)在KEIL中建立工程,将编写好的程序添加到工程中。
(2)在KEIL中为工程设置选项。为了实现和PROTEUS的联合调试,除了一般工程选项的设置外,还要设置DUBUG项。具体方法为:单击“工程菜单/为目标‘目标1’设置选项”选项,弹出窗口,点击“Debug”按钮。在出现的对话框里在右栏上部的下拉菜单里选中“Proteus VSM Monitor一51 Driver”。并且还要点击一下“Use”前面表明选中的小圆点。再点击“Setting”按钮,设置通信接口,在“Host”后面添上“127.0.0.1”,如果使用的不是同一台电脑,则需要在这里添上另一台电脑的IP地址(另一台电脑也应安装Proteus)。在“Port”后面添加“8000”,然后点击“OK”按钮。最后将工程编译,进入调试状态,并运行。设置完之后,重新编译、链接、生成可执行文件。
(3)PROTEUS设置。进入Proteus的ISIS,鼠标左键点击菜单“Debug”, 选中“使用远程调试监控”。
              (4)将可执行文件加到单片机中。打开PROTEUS仿真图,双击单片机,将KEIL产生的“*.HEX”文件添加到单片机中。
              (5)KEIL与PROTEUS连接仿真调试。点击PROTEUS的开始仿真按钮即可开始KEIL与PROTEUS的联合仿真和调试。
              本次设计的联合仿真结果如下图所示。

5-1 密码正确后的仿真图
              密码输入正确后,点亮二极管,显示器显示欢迎回家画面。通过观察键盘引脚的电平,还能知道CPU正在扫描键盘第四行,等待功能键的输入。
              本设计中键盘第四行只用到了前两个按键,后两个按键没有被使用。第一个按键为改密码键,通过按键此键可以进入修改密码功能界面;第二个键位闭锁键,通过按下此键可以将锁闭合,在设计中的直接表现是发光二极管熄灭。

第6章 实物设计和制作
              联合仿真调试通过后,本次设计业就进入了最后一步,但也是最关键的一步——实物设计和制作。
              由于电路板空间有限,所有在进行实物设计之前应该根据系统的仿真电路图做好各功能模块的整体布局,这样既可以使得实物设计的顺利进行,也可以最大化的令做出来的实物看起来简洁而美观。
              在本次设计中,实物设计是按以下步骤完成的:
              (1)单片机最小系统的电路焊接。只有保证了单片机最小系统的正确性,才能在接下来的其他模块设计和焊接出现错误时能够方便地检查出原因,因此单片机最小系统是必须首先完成的。
              (2)下载口电路的焊接。单片机最小系统电路焊接好后,需要检查其正确性,这就需要往单片机中下载一个简单的程序,看其是否能正常工作和复位,这就需要焊接一个下载口电路,此下载口电路还可以当做电源口使用,通过数据线连接电源即可给单片机供电。
              图6-1为单片机下载口电路接线图。
6-1 下载口电路接线图
              其中MOSI接口接单片机的P1.5口,RST接单片机的复位端口,SCK接单片机的P1.7口,MISO接单片机的P1.6口。下载口的2端口接电源,4、6、8、10端口接地。在下载口接好后,应用单片机程序烧写软件通过下载口将测试程序下载进入单片机测试最小系统是否焊接正确。一般使用的测试方法是在单片机端口接一个发光二极管,通过程序控制二极管发光,若能点亮二极管则最小系统焊接正确。在本次设计中使用的单片机程序烧写软件为AVR_fighter。
              (3)液晶显示器及电路焊接。按照仿真电路原理图和液晶显示器接口使用说明焊接好液晶显示器,将显示子程序下载入单片机,若显示器能够按照要求显示字符则焊接正确。
              (4)键盘模块焊接。按照键盘模块仿真图焊接按键,之后将测试程序导入单片机中检验按键是否正确焊接成功。本设计中检验按键的程序功能是当有按键按下时,将所按下的键值在液晶显示器中显示出来。
              (5)E2PROM存储器的焊接。根据AT24C02的引脚介绍,将AT24C02接到单片机上。测试AT24C02是否焊接正确的程序功能是将一个数组中的数写入AT24C02,再将这一个数组中的数从AT24C02中读出放入另一个数组中,比较两个数组中的数是否相同,若相同则AT24C02的电路焊接正确,掉电存储功能模块功能实现。
              (6)将蜂鸣器和发光二极管焊接到单片机系统中,将本次设计的源程序下载进入单片机中,看是否所有功能都实现了。若能够实现所有功能则本次设计也就宣告完成,若不能实现,则根据出现的问题检查线路,找出原因,直至能够实现所有功能。
              在实物制作过程中,应该注意几个问题:第一,根据仿真图焊接电路,但不能尽信仿真图。仿真图始终不能代替实物调试,在实物制作过程中应根据实际情况调试电路;第二,调试和查找错误过程中,如非必要应尽量不要带电操作,以免损坏器件;第三,电路布局应尽量工整。工整简洁的电路布局不但看着美观,而且能够更方便地查找电路错误。
              本次设计的实物制作,由于经验不足的原因,许多焊接点焊得不够美观,但是整体布局还是够简洁工整的。这也是设计中虽然经验不足,制作过程中出现了各种各样的问题,但是进展却很顺利的原因,简洁工整的布局使得查找问题变得更加容易,能够方便的理清电路,找出错误并加以改正。

总结
本次设计经过近两个月时间忙碌终于完成了,所有设计要求具备的功能都得到了实现,具体如下:
(1)加电后,单片机自动复位,LCD显示提示输入密码的信息;
(2)输入密码时,只逐位显示“*”,以防止密码泄漏;
(3)在按键输入的过程中,如果不小心输错,可以清除所输入的错误内容,然后继续输入;
(4)当密码输入完毕并按下确认键后,单片机将输入的密码与设定的密码比较,若密码正确,则打开密码锁;若密码不正确,则无法打开密码锁;
(5)在密码输入错误次数达到预设值时,启动报警程序报警;
(6)可以在开锁后进行密码的修改,但需要两次输入确认;
(7)修改的密码存入AT24C02,不会因为掉电的原因而丢失;
(8)在长时间没有按键输入时,系统锁定显示器,并禁止按键的输入;
(9)设定一个备用密码,防止常用密码忘记时无法开门。
由于设计水平有限,此次设计还是存在一些小缺陷的,比如报警系统的报警音量不够大,虽然加了一个三极管作为驱动,使得问题得到了改善,但是问题还是没有得到完美的解决。
另外,通过这几个月对电子密码锁的研究和学习,我认为本次设计中使用的这种键盘输入密码的方式可以进行改革。在越来越高科技化的今天,遥控控制显的愈发重要,今后的电子密码锁应该具有以红外技术或无线电技术为辅助的密码按键输入远程交互技术,这样就能远程输入密码完成操作。也可以放弃传统的按键输入密码模式,借助传感器技术运用声控来实现密码输入,又或者使用人脸识别技术,或者用户指纹识别技术代替传统的按键输入,这些都可以使开锁的时间更短更方便,同时是锁更安全。

致谢
在老师的耐心指导和同学们的热情帮助下,经过近三个月的不懈努力,本设计终于基本完成。在做设计的这段时间里,老师给我提供了极大的帮助和指导。从设计方向的分析到开题答辩报告的撰写,从具体程序的设计到器件的选择,老师都给了我很多很有用的建议。另外,老师亲切和善、能和学生们打成一片的工作态度和认真负责的治学作风也给了我很深的印象。在此,对王老师表达最真诚的谢意和最崇高的敬意。
在做设计的过程中,自动化班的同学们也给我提供了很大的帮助,他们的关心和爱护不但帮助我更好地完成设计,更使我感受到了这个集体里家一样的温暖。非常感激自动化班的同学们。
同时,在即将离开校园的此时,衷心感谢每一位教导过我的老师和一直默默支持和鼓励我的家人。
最后,我要向百忙之中抽出时间对本文进行审阅、评议和参与本人论文答辩的各位老师表示感谢。

附录1 设计实物图
附图1 实物正面图

附图2 实物反面图
附录2 程序源代码
  1. #include <reg51.h>
  2. #include <string.h>
  3. #include <intrins.h>
  4. #include<stdio.h>
  5. #define uchar unsigned char
  6. #define uint unsigned int
  7. #define NOP3() _nop_();_nop_();_nop_()
  8. uchar time_1s=0,i=0;
  9. uchar time_50ms=0;
  10. sbit rs=P2^2;
  11. sbit rw=P2^1;
  12. sbit e=P2^0;
  13. sbit SCL=P2^5;
  14. sbit SDA=P2^6;
  15. sbit P35=P3^5;
  16. sbit P36=P3^6;
  17. sbit P37=P3^7;
  18. uchar data kong[7]={'\0','\0','\0','\0','\0','\0',0};
  19. uchar data mima[7]={'1','2','3','4','5','6',0};
  20. uchar data mima0[7]={'\0','\0','\0','\0','\0','\0',0};
  21. uchar data mima1[7]={'\0','\0','\0','\0','\0','\0',0};
  22. uchar data mima2[7]={'\0','\0','\0','\0','\0','\0',0};
  23. uchar data mima3[7]={'\0','\0','\0','\0','\0','\0',0};
  24. uchar data table1[7]={'1','9','9','0','1','0',0};
  25. uchar code table2[15]="Enter Password:";
  26. uchar code table4[16]="Password Error!";
  27. uchar code table5[14]="Old Password:";
  28. uchar code table6[14]="New Password:";
  29. uchar code table7[12]="Enter Again:";
  30. uchar code table8[12]="Enter Error!";
  31. uchar code table9[15]="Modify Success!";
  32. uchar code table10[13]="Welcome back!";
  33. //***********************************
  34. //相关函数的声明
  35. //***********************************
  36. void delay1ms();
  37. void delay(uchar n);
  38. void write_1602com(uchar com);
  39. void write_1602dat(uchar dat);
  40. void initinal(void);
  41. bit lcdbusy();
  42. uchar key_scan();
  43. void enter_password(void);              //功能:显示输入密码画面
  44. void password_error(void);//功能:显示输入密码错误后的画面
  45. void ok(void);//功能:显示输入密码正确后的画面
  46. void new_password(void);//功能:显示输入新密码的画面
  47. void enter_again(void);//功能:显示再次输入新密码的画面
  48. void enter_error(void);//功能:显示两次输入的密码不一样后的画面
  49. void modify_success(void);//功能:显示更改成功后的画面
  50. void welcome(void);  //功能:显示欢迎画面
  51. void wtite_1602com(uchar com);
  52. void write_1602dat(uchar dat);
  53. /*****************************************************
  54. 函数功能:延时若干毫秒
  55. 入口参数:n
  56. ***************************************************/
  57. void delay(uchar n)
  58. {              unsigned char i,x,y;
  59.               for(i=0;i<n;i++){for(x=0;x<10;x++)for(y=0;y<33;y++); } }
  60. /*****************************************************
  61. 函数功能:延时1ms
  62. ***************************************************/
  63. void delay1(uchar n){   unsigned char i,j;            
  64.               for(i=0;i<n;i++){  for(j=0;j<10;j++)delay(10);}              }
  65. /*****************************************************
  66. 函数功能:定时器子函数,调用该函数可以计时
  67. ***************************************************/
  68. void time( ){              TMOD = 0x01;                //定时器0,工作方式1
  69.                                 TH0 = (65536-50000)/256;
  70.                                 TL0 = (65536-50000)%256;              //50ms中断一次
  71.                                 EA = 1;ET0 = 1;TR0 = 1;}
  72.   /*****************************************************
  73. 函数功能:定时函数,在一段时间内没有键按下则关闭显示器
  74. ***************************************************/
  75. void time_1( ) interrupt 1{
  76.    TH0 = (65536-65536)/256;TL0 = (65536-65536)%256;time_50ms++;
  77.               if(time_50ms==20){time_1s++;time_50ms=0;}
  78.               if(time_1s==20){write_1602com(0x01);write_1602com(0x0c);
  79.                                           time_1s=0; while(1);}}
  80. //***********************************
  81. //功能:使蜂鸣器发音
  82. //***********************************
  83. void warn(){P37=1;NOP3();NOP3();NOP3();
  84.                             P37=0;NOP3();NOP3();NOP3(); }
  85. //***********************************
  86. //函数名:bit lcdbusy()
  87. //功能:检测忙标志
  88. //***********************************
  89. bit lcdbusy(){bit result;              rs=0;rw=1;e=1;
  90.               delay(1);
  91.               result=(bit)(P0&0x80);e=0;
  92.               return result;}
  93. //************************************************
  94. //函数名:写命令函数
  95. //功能:调用该函数可能定义1602液晶显示器的各种命令
  96. //************************************************
  97. void write_1602com(uchar com){
  98.               while(lcdbusy());
  99.               e=0;              rw=0;              rs=0;
  100.               P0=com;              delay(1);
  101.               e=1;              delay(1);e=0;}
  102. //*************************************************
  103. //函数名:写数据函数
  104. //功能:调入该函数可以向1602液晶显示器输入数据
  105. //*************************************************
  106. void write_1602dat(uchar dat){
  107.               while(lcdbusy());e=0;rw=0;rs=1;
  108.               P0=dat;delay(1);e=1;delay(1);e=0;}
  109. //**************************************************
  110. //函数名:初始化函数
  111. //功能:对1602液晶显示器进行初始化
  112. //**************************************************
  113. void initinal(void){
  114.               rs=0;e=0;
  115.               write_1602com(0x38);     //数据总线为8位,显示2行,0=5×7点阵/每字符
  116.               write_1602com(0x0f);     //显示功能开,有光标,光标不闪烁
  117.               write_1602com(0x06);    //显示光标
  118.               write_1602com(0x01);    //清屏
  119.               write_1602com(0x80);}
  120. //*********************************************
  121. //函数名:显示函数
  122. //功能:显示输入密码
  123. //*********************************************
  124. void enter_password(void){ uchar j;
  125.               initinal();write_1602com(0x80);
  126.               for(j=0;j<15;j++){write_1602dat(table2[j]);delay(10);}
  127.               write_1602com(0xc0+0x0a);}
  128. //********************************************
  129. //函数名:显示函数
  130. //功能:显示输入密码错误后的画面
  131. //********************************************
  132. void password_error(void){uchar j;
  133.               write_1602com(0x0c); //显示功能开,无光标
  134.               write_1602com(0x80); //显示位置第一行第一列
  135.               write_1602com(0x01);
  136.               for(j=0;j<15;j++){write_1602dat(table4[j]);delay(10);}}
  137. //******************************************
  138. //函数名:显示函数
  139. //功能:显示输入旧密码的画面
  140. //******************************************
  141. void old_password(void){uchar j;
  142.               initinal();write_1602com(0x80);
  143.               for(j=0;j<14;j++){write_1602dat(table5[j]);delay(10);}
  144.               write_1602com(0xc0+0x0a);}
  145. //******************************************
  146. //函数名:显示函数
  147. //功能:显示输入新密码的画面
  148. //******************************************
  149. void new_password(void){uchar j;initinal();
  150.               write_1602com(0x80);
  151.               for(j=0;j<14;j++){write_1602dat(table6[j]);delay(10);}
  152.               write_1602com(0xc0+0x0a);}
  153. //******************************************
  154. //函数名:显示函数
  155. //功能:显示再次输入新密码的画面
  156. //******************************************
  157. void enter_again(void){uchar j;initinal();
  158.               write_1602com(0x80);
  159.               for(j=0;j<12;j++){write_1602dat(table7[j]);delay(10);}
  160.               write_1602com(0xc0+0x0a);}
  161. //********************************************
  162. //函数名:显示函数
  163. //功能:显示两次输入的密码不一样后的画面
  164. //********************************************
  165. void enter_error(void){uchar j;
  166.               write_1602com(0x0c); //显示功能开,无光标
  167.               write_1602com(0x80); //显示位置第一行第一列
  168.               write_1602com(0x01);
  169.               for(j=0;j<12;j++){write_1602dat(table8[j]);delay(10);}}
  170. //******************************************
  171. //函数名:显示函数
  172. //功能:显示更改成功之后的画面
  173. //******************************************
  174. void modify_success(void){uchar j;write_1602com(0x0c);
  175.               write_1602com(0x80);write_1602com(0x01);
  176.               for(j=0;j<15;j++){write_1602dat(table9[j]);delay(10);}}
  177. //******************************************
  178. //函数名:显示函数
  179. //功能:显示欢迎画面
  180. //******************************************
  181. void welcome(void){
  182.               uchar j;
  183.               write_1602com(0x0c); //显示功能开,无光标
  184.               write_1602com(0x80); //显示位置第一行第4列
  185.               write_1602com(0x01);
  186.               write_1602com(0x80);
  187.               for(j=0;j<13;j++){write_1602dat(table10[j]);delay(10);}}
  188. //**************************************************
  189. //函数名:键盘识别函数
  190. //功能:通过扫描对键盘进行识别
  191. //**************************************************
  192. uchar key_num(){uint temp,b;
  193. while(1){P1=0xfe;             //开始扫描第一行
  194.                             temp=P1;
  195.                             temp=temp&0xf0;
  196.                             if(temp!=0xf0){delay(1);temp=P1;b=temp;temp=temp&0xf0;
  197.                                           if(temp!=0xf0){
  198.                                           while(temp!=0xf0){temp=P1;temp=temp&0xf0;}
  199.                                           switch(b){case 0xee: return '4';break;
  200.                                                         case 0xde: return '3';break;
  201.                                                         case 0xbe: return '2';break;
  202.                                                         case 0x7e: return '1';break;}}}
  203.                             P1=0xfd;             //扫描第二行
  204.                             temp=P1;
  205.                             temp=temp&0xf0;
  206.                             if(temp!=0xf0){delay(1);temp=P1;b=temp;temp=temp&0xf0;
  207.                                           if(temp!=0xf0){
  208.                                           while(temp!=0xf0){temp=P1;temp=temp&0xf0;}
  209.                                           switch(b){case 0xed: return '8';break;
  210.                                                         case 0xdd: return '7';break;
  211.                                                         case 0xbd: return '6';break;
  212.                                                         case 0x7d: return '5';break;}}}
  213.                             P1=0xfb;             //扫描第三行
  214.                             temp=P1;
  215.                             temp=temp&0xf0;
  216.                             if(temp!=0xf0){delay(1);temp=P1;b=temp;temp=temp&0xf0;
  217.                                           if(temp!=0xf0){
  218.                                                         while(temp!=0xf0){temp=P1;temp=temp&0xf0;}
  219.                                                         switch(b){ case 0xeb: return 'b';break;
  220.                                                                       case 0xdb: return 'a';break;
  221.                                                                       case 0xbb: return '0';break;
  222.                                                                       case 0x7b: return '9';break;}}}}}
  223. /****************************************
  224. 只扫描第四行,禁止数字键输入
  225. ****************************************/
  226. uchar key_gn(){uint temp,b;
  227. while(1){P1=0xf7;             //扫描第四行
  228.                             temp=P1;
  229.                             temp=temp&0xf0;
  230.                             if(temp!=0xf0){delay(1);temp=P1;b=temp;temp=temp&0xf0;
  231.                                           if(temp!=0xf0){
  232.                                           while(temp!=0xf0){ temp=P1;temp=temp&0xf0;}
  233.                                                         switch(b){case 0xe7: return 'f';break;
  234.                                                                       case 0xd7: return 'e';break;
  235.                                                                       case 0xb7: return 'd';break;
  236.                                                                       case 0x77: return 'c';break;}}} }}
  237. /****************************************
  238. 密码函数:将输入的密码写放进数组
  239. ****************************************/
  240. uchar enter(uchar password[]){uchar m=0,j,back=0;
  241.               for(m=0;;){j=key_num();time_50ms=0;time_1s=0;
  242.                             if(j>='0'&&j<='9'&&m<6){write_1602com(0x06);password[m]=j;              m++;
  243.                                           write_1602dat('*');}
  244.                             if(j=='a'){password[m+1]='\0';return 1;}              if(j=='b'){if(m>0){write_1602com(0x11);write_1602dat(0x20);
  245.                                           write_1602com(0x11);password[m]='\0';m--;}} }}
  246. /******************************************
  247. 启动I2C总线函数
  248. ******************************************/
  249. void Start(){              SCL=1;
  250.                                           SDA=1;NOP3();
  251.                                           SDA=0;NOP3();
  252.                                           SCL=0;}
  253. /******************************************
  254. I2C停止
  255. ******************************************/
  256. void Stop(){              SDA=0;
  257.                                           SCL=1;NOP3();
  258.                                           SDA=1;NOP3();}
  259. /******************************************
  260. 发送应答位函数
  261. ******************************************/
  262. void CHECK_ACK(){              SDA=0;NOP3();
  263.                                                         SCL=1;NOP3();
  264.                                                         SCL=0;NOP3();
  265.                                                         SDA=1;}
  266. /******************************************
  267. 发送非应答位函数
  268. ******************************************/
  269. void CHECK_NACK(){              SDA=1;NOP3();
  270.                                                         SCL=1;NOP3();
  271.                                                         SCL=0;NOP3();
  272.                                                         SDA=0;}

  273. /******************************************
  274. I2C初始化
  275. ******************************************/
  276. void Init_24cxx(){                            SDA=1;NOP3();
  277.                                                         SCL=1;NOP3();}
  278. /******************************************
  279. 读I2C函数
  280. ******************************************/
  281. uchar I2C_Read(){
  282.               uchar i,temp;temp=0;
  283.               SDA=1;SCL=0;
  284.               for(i=0;i<8;i++){            
  285.                             temp=temp<<1;SCL=1;NOP3();
  286.                             if(SDA==1){temp=temp+1;}NOP3();
  287.                             SCL=0;                            }
  288.                             return temp;}
  289. /******************************************
  290. 写I2C函数
  291. ******************************************/
  292. void I2C_Write(uchar date1){
  293.               uchar i,temp;temp=date1;
  294.               SCL=0;
  295.               for(i=0;i<8;i++){
  296.                             SDA=(bit)(temp&0x80);
  297.                             SCL=1;NOP3();SCL=0;
  298.                             temp=temp<<1;}NOP3();}
  299. /******************************************
  300. 写相应地址的数据
  301. ******************************************/
  302. void Write_add(uint address,uchar date1){
  303.               uchar addrh,addrl;
  304.               addrh=address>>8;
  305.               addrl=address%256;
  306.               EA=0;
  307.               Start();
  308.               I2C_Write(0xa0);//发送命令字+芯片编号+P0+W
  309.               CHECK_ACK();
  310.               I2C_Write(addrh);//发高位地址
  311.               CHECK_ACK();
  312.               I2C_Write(addrl);//发低位地址
  313.               CHECK_ACK();
  314.               I2C_Write(date1);//发送数据
  315.               CHECK_ACK();
  316.               Stop();//停止
  317.               EA=1;}
  318. /******************************************
  319. 读相应地址的数据
  320. ******************************************/
  321. uchar Read_add(uint address){
  322.               uchar addrh,addrl,temp;
  323.               addrh=address>>8;
  324.               addrl=address%256;
  325.               EA=0;
  326.               Start();
  327.               I2C_Write(0xa0);
  328.               CHECK_ACK();
  329.               I2C_Write(addrh);//发高位地址
  330.               CHECK_ACK();
  331.               I2C_Write(addrl);//发低位地址
  332.               CHECK_ACK();
  333.               Start();
  334.               I2C_Write(0xa1);
  335.               CHECK_ACK();
  336.               temp=I2C_Read();
  337.               CHECK_NACK();
  338.               Stop();
  339.               EA=1;
  340.               return temp;
  341. }
  342. /******************************************
  343. 写数据函数:将数组写入AT24C02
  344. ******************************************/
  345. ……………………

  346. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

完整的Word格式文档51黑下载地址:
基于单片机的电子密码锁设计(终稿).doc (2.77 MB, 下载次数: 685)

评分

参与人数 2黑币 +60 收起 理由
雨停江南 + 10 赞一个!
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

好帅啊 发表于 2019-3-5 08:28 来自手机 | 显示全部楼层
代码有问题
回复

使用道具 举报

落叶无情。。 发表于 2019-4-17 23:05 | 显示全部楼层
为什么代码运行不了呀
回复

使用道具 举报

严重 发表于 2019-5-2 16:05 | 显示全部楼层
落叶无情。。 发表于 2019-4-17 23:05
为什么代码运行不了呀

下载下来的代码运行不了吗?
还是文章中直接复制的?
回复

使用道具 举报

lixiang5501 发表于 2019-6-12 13:06 | 显示全部楼层
代码用不了啊
回复

使用道具 举报

归铉 发表于 2019-6-13 09:23 | 显示全部楼层
严重 发表于 2019-5-2 16:05
下载下来的代码运行不了吗?
还是文章中直接复制的?

下载的可以用,复制的不全
回复

使用道具 举报

beisheng 发表于 2019-11-12 14:22 | 显示全部楼层
学习了,这种东西多分享,有收获
回复

使用道具 举报

酷酷阿琪 发表于 2019-11-19 13:50 | 显示全部楼层
正需要这个资料呢
回复

使用道具 举报

nyy_ 发表于 2019-12-3 17:27 | 显示全部楼层
我想问下,如果我用的是AT24C04B的话,要怎么修改代码呢?因为我试着把上面的程序下下去,显示有问题
回复

使用道具 举报

qwertyuiokk 发表于 2019-12-5 16:51 来自手机 | 显示全部楼层
严重 发表于 2019-5-2 16:05
下载下来的代码运行不了吗?
还是文章中直接复制的?

运行不了,有错误
回复

使用道具 举报

wxl123123123 发表于 2019-12-10 20:25 | 显示全部楼层
初始密码是多少啊?

回复

使用道具 举报

lihuaizhi 发表于 2019-12-12 11:24 来自手机 | 显示全部楼层
厉害,这回可以过了
回复

使用道具 举报

emlink 发表于 2019-12-29 11:40 | 显示全部楼层

正需要这个资料呢
回复

使用道具 举报

gql666 发表于 2019-12-29 17:23 来自手机 | 显示全部楼层
怎么下载啊
回复

使用道具 举报

且听且吟 发表于 2019-12-30 13:08 | 显示全部楼层
学习了,这种东西多分享,有收获
回复

使用道具 举报

Hzyking 发表于 2020-2-17 16:54 | 显示全部楼层
资料很好谢谢楼主
回复

使用道具 举报

zyb296016 发表于 2020-3-12 18:39 来自手机 | 显示全部楼层
归铉 发表于 2019-6-13 09:23
下载的可以用,复制的不全

在哪里下载
回复

使用道具 举报

Lprong 发表于 2020-3-13 17:47 | 显示全部楼层
资料很棒,感谢楼主
回复

使用道具 举报

hzlbza 发表于 2020-3-28 17:47 | 显示全部楼层
wxl123123123 发表于 2019-12-10 20:25
初始密码是多少啊?

199010
回复

使用道具 举报

hzlbza 发表于 2020-3-28 17:48 | 显示全部楼层
初始密码199010
回复

使用道具 举报

子不语乖乖 发表于 2020-3-29 15:40 | 显示全部楼层
为什么我复制不上啊,好想用这个
回复

使用道具 举报

子不语乖乖 发表于 2020-4-3 15:48 来自手机 | 显示全部楼层
楼主,求助键盘锁定之后,想解除锁定怎么办啊
回复

使用道具 举报

2301638437 发表于 2020-4-8 14:32 | 显示全部楼层
qwertyuiokk 发表于 2019-12-5 16:51
运行不了,有错误

请问是下载的用不了吗
回复

使用道具 举报

sheji 发表于 2020-4-24 14:43 | 显示全部楼层
过程很详细
回复

使用道具 举报

Lxy541432 发表于 2020-5-25 13:49 | 显示全部楼层
归铉 发表于 2019-6-13 09:23
下载的可以用,复制的不全

我下载了,还是不对
回复

使用道具 举报

KHYLTZ 发表于 2020-6-5 22:41 | 显示全部楼层
落叶无情。。 发表于 2019-4-17 23:05
为什么代码运行不了呀

代码不全,需要花钱
回复

使用道具 举报

gzz123 发表于 2020-6-6 10:41 | 显示全部楼层
归铉 发表于 2019-6-13 09:23
下载的可以用,复制的不全

可以给我发一下代码吗
回复

使用道具 举报

ff666 发表于 2020-6-8 20:32 | 显示全部楼层
楼主,请问这涉及到i2c技术了吗
回复

使用道具 举报

黑衣天王J 发表于 2020-6-10 10:36 | 显示全部楼层
在学习,好东西
回复

使用道具 举报

wyb1712 发表于 2020-6-10 18:46 | 显示全部楼层
子不语乖乖 发表于 2020-4-3 15:48
楼主,求助键盘锁定之后,想解除锁定怎么办啊

加一怎么解除锁定啊
回复

使用道具 举报

鸠了了 发表于 2020-6-28 22:31 | 显示全部楼层
断电不能记忆密码呀
回复

使用道具 举报

fine......... 发表于 2020-7-4 12:18 | 显示全部楼层
Lxy541432 发表于 2020-5-25 13:49
我下载了,还是不对

我也是  为什么 而且看不懂哪里不对
回复

使用道具 举报

fine......... 发表于 2020-7-4 12:45 | 显示全部楼层
Build target 'Target 1'
compiling mian.c...
linking...
BL51 BANKED LINKER/LOCATER V6.22 - SN: Eval Version
COPYRIGHT KEIL ELEKTRONIK GmbH 1987 - 2009
"mian.obj"
TO "mian"
*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS
    SEGMENT: ?PR?INIT_24CXX?MIAN
*** WARNING L15: MULTIPLE CALL TO SEGMENT
    SEGMENT: ?PR?LCDBUSY?MIAN
    CALLER1: ?PR?TIME_1?MIAN
    CALLER2: ?C_C51STARTUP
******************************************************************************
* RESTRICTED VERSION WITH 0800H BYTE CODE SIZE LIMIT; USED: 0924H BYTE (114%) *
******************************************************************************
Program Size: data=73.2 xdata=0 code=2999
LINK/LOCATE RUN COMPLETE.  2 WARNING(S),  0 ERROR(S)
*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS
    SEGMENT: ?PR?INIT_24CXX?MIAN
*** WARNING L15: MULTIPLE CALL TO SEGMENT
    SEGMENT: ?PR?LCDBUSY?MIAN
    CALLER1: ?PR?TIME_1?MIAN
    CALLER2: ?C_C51STARTUP
*** FATAL ERROR L250: CODE SIZE LIMIT IN RESTRICTED VERSION EXCEEDED
    MODULE:  D:\KEIL4\C51\LIB\C51S.LIB (-----)
    LIMIT:   0800H BYTES
Target not created
出现了这些错是为什么
回复

使用道具 举报

wyj36853 发表于 2020-7-11 00:40 | 显示全部楼层
wxl123123123 发表于 2019-12-10 20:25
初始密码是多少啊?

199010
回复

使用道具 举报

fff666 发表于 2020-9-10 19:40 | 显示全部楼层
我想请问一下,电子密码锁这个有没有实物接线图啊?
回复

使用道具 举报

fff666 发表于 2020-9-11 10:29 | 显示全部楼层
fff666 发表于 2020-9-10 19:40
我想请问一下,电子密码锁这个有没有实物接线图啊?

我找到了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表