发作品签到
专业版

电子密码锁

工程标签

409
0
0
0

简介

本设计主要由单片机、矩阵键盘、液晶显示器和密码存储等部分组成。其中矩阵键盘用于输入数字密码和进行各种功能的实现。

简介:本设计主要由单片机、矩阵键盘、液晶显示器和密码存储等部分组成。其中矩阵键盘用于输入数字密码和进行各种功能的实现。
复刻成本:28

开源协议

GPL 3.0

创建时间:2024-12-14 06:53:18更新时间:2024-12-23 02:14:06

描述

电子密码锁使用说明

一、 主要功能:

1.按键设置6位密码,输入密码若密码正确,则锁打开。显示open!
2.密码可以自己修改(6位密码),必须是锁打开时才能改密。为防止误操作,修改密码得输入两次。
3.若密码输入错误次数超过3次,蜂鸣器报警并且锁定键盘。
4.24C02有复位、掉电保存密码功能。
5.液晶屏亮度会随光线自动调整。

二、按键说明:

A B无定义、*号键为取消当前操作、#号键为确认、C键为退格键、D键为修改密码确认键

三、调整说明:

1、 灰度调整:

当焊接好后液晶屏可能会“无字”。这是调节液晶3脚与地之间的电位器阻值应为1.3k左右,液晶3脚与电源之间的电位器阻值应为8.7k左右。(如果是电阻,那就是1与3脚是1.5k,2与3脚是10k的电阻)

2、初始化:

首次使用时输入:131420,对密码进行初始化,当显示:initpassword,证明密码初始化完成,此时的密码为:000000。然后可以改密了。(如密码忘记就再输入131420初始化,然后密码就是000000了,不要告诉别人哦~~~)

3、退出

按下 * 键或复位键,可以关锁并退出。

4、报警

当输入密码错误后,报警并锁定键盘3秒钟。

5、 修改密码:

在开锁状态下,再次输入正确的密码并按下 # 键,此时听到两声提示,输入新的六位密码并按 D 键,再重复输入一次新密码并按 D ,会听到两声提示音,表示重设密码成功,内部保存新密码并存储。如两次输入的新密码不一样,则重设密码失败。

操作视频教程

https://www.bilibili.com/video/BV1RokWYgEHp/

项目功能

本设计从经济实用的角度出发,采用单片机STC89C51低功耗CMOS型E2PROM AT24C02作为主控芯片与数据存储器单元,结合外围的键盘输入、显示、报警、开锁等电路并用C语言的控制程序,研制了一款可以多次更改密码具有报警功能的电子密码锁。设计完全可行可以达到设计目地。使用单片机制作的电子密码锁具有软硬件设计简单,易于开发,成本较低,安全可靠,操作方便等特点,可应用于住宅、办公室的保险箱及档案柜等需要防盗的场所,有一定的实用性。该电路设计还具有按键有效提示,输入错误提示,控制开锁电平,控制报警电路,修改密码等多种功能。可在意外泄密的情况下随时修改密码。保密性强,灵活性高,特别适用于家庭、办公室、学生宿舍及宾馆等场所。

原理解析(硬件说明)

1、 设计原理

本设计主要由单片机、矩阵键盘、液晶显示器和密码存储等部分组成。其中矩阵键盘用于输入数字密码和进行各种功能的实现。由用户通过连接单片机的矩阵键盘输入密码,后经过单片机对用户输入的密码与自己保存的密码进行对比,从而判断密码是否正确,然后控制引脚的高低电平传到开锁电路或者报警电路控制开锁还是报警,实际使用时只要将单片机的负载由继电器换成电子密码锁的电磁铁吸合线圈即可,当然也可以用继电器的常开触点去控制电磁铁吸合线圈。
本系统共有两部分构成,即硬件部分与软件部分。其中硬件部分由电源输入部分、键盘输入部分、密码存储部分、复位部分、晶振部分、显示部分、报警部分、开锁部分组成,软件部分对应的由主程序、初始化程序、LCD显示程序、键盘扫描程序、启动程序、关闭程序、建功能程序、密码设置程序、EEPROM读写程序和延时程序等组成。其原理框图如图1所示。

image.png
1 电子密码锁原理框图

2、 单片机STC89C51简介

STC89C51是一种低功耗、高性能CMOS8位微控制器,具有 4K 在系统可编程Flash 存储器。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得STC89C51为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。 具有以下标准功能: 4k字节Flash,512字节RAM, 32 位I/O 口线,看门狗定时器,2个16 位 定时器/计数器,一个6向量2级中断结构,全双工串行口。另外 STC89C51 可降至0Hz 静态逻辑操作,支持2种软件可选择节电模式。空闲模式下,CPU 停止工作,允许RAM、定时器/计数器、串口、中断继续工作。掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。最高运作频率35Mhz,6T/12T可选。STC89C51主要功能如表1所示,其DIP封装如图2所示

image.png

STC89C52引脚介绍
① 主电源引脚(2根)
VCC(Pin40):电源输入,接+5V电源
GND(Pin20):接地线
②外接晶振引脚(2根)
XTAL1(Pin19):片内振荡电路的输入端
XTAL2(Pin20):片内振荡电路的输出端
③控制引脚(4根)
RST/VPP(Pin9):复位引脚,引脚上出现2个机器周期的高电平将使单片机复位。
ALE/PROG(Pin30):地址锁存允许信号
PSEN(Pin29):外部存储器读选通信号
EA/VPP(Pin31):程序存储器的内外部选通,接低电平从外部程序存储器读指令,如果接高电平则从内部程序存储器读指令。
④可编程输入/输出引脚(32根)
STC89C52单片机有4组8位的可编程I/O口,分别位P0、P1、P2、P3口,每个口有8位(8根引脚),共32根。
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
作频率35Mhz,6T/12T可选。

image.png

最小系统包括单片机及其所需的必要的电源、时钟、复位等部件,能使单片机始终处于正常的运行状态。电源、时钟等电路是使单片机能运行的必备条件,可以将最小系统作为应用系统的核心部分,通过对其进行存储器扩展、A/D扩展等,使单片机完成较复杂的功能。
STC89C51是片内有ROM/EPROM的单片机,因此,这种芯片构成的最小系统简单﹑可靠。用STC89C52单片机构成最小应用系统时,只要将单片机接上时钟电路和复位电路即可,结构如图3所示,由于集成度的限制,最小应用系统只能用作一些小型的控制单元。

image.png

(1) 时钟电路

STC89C51单片机的时钟信号通常有两种方式产生:一是内部时钟方式,二是外部时钟方式。内部时钟方式如图2-4所示。在STC89C51单片机内部有一振荡电路,只要在单片机的XTAL1(18)和XTAL2(19)引脚外接石英晶体(简称晶振),就构成了自激振荡器并在单片机内部产生时钟脉冲信号。图中电容C1和C2的作用是稳定频率和快速起振,电容值在5-30pF,典型值为30pF。晶振CYS的振荡频率范围在1.2-12MHz间选择,典型值为12MHz和6MHz。

image.png

(2) 复位电路

当在STC89C51单片机的RST引脚引入高电平并保持2个机器周期时,单片机内部就执行复位操作(若该引脚持续保持高电平,单片机就处于循环复位状态)。
最简单的上电自动复位电路中上电自动复位是通过外部复位电路的电容充放电来实现的。只要Vcc的上升时间不超过1ms,就可以实现自动上电复位。
除了上电复位外,有时还需要按键手动复位。本设计就是用的按键手动复位。按键手动复位有电平方式和脉冲方式两种。其中电平复位是通过RST(9)端与电源Vcc接通而实现的。

image.png

(3) STC89C51中断技术概述

中断技术主要用于实时监测与控制,要求单片机能及时地响应中断请求源提出的服务请求,并作出快速响应、及时处理。这是由片内的中断系统来实现的。当中断请求源发出中断请求时,如果中断请求被允许,单片机暂时中止当前正在执行的主程序,转到中断服务处理程序处理中断服务请求。中断服务处理程序处理完中断服务请求后,再回到原来被中止的程序之处(断点),继续执行被中断的主程序。

image.png

如果单片机没有中断系统,单片机的大量时间可能会浪费在查询是否有服务请求发生的定时查询操作上。采用中断技术完全消除了单片机在查询方式中的等待现象,大大地提高了单片机的工作效率和实时性。

3、 AT24C02存储芯片

AT24C02是美国Atmel公司的低功耗CMOS型E2PROM,内含256×8位存储空间,具有工作电压宽(2.5~5.5 V)、擦写次数多(大于10000次)、写入速度快(小于10 ms)、抗干扰能力强、数据不易丢失、体积小等特点。而且他是采用了I2C总线式进行数据读写的串行器件,占用很少的资源和I/O线,并且支持在线编程,进行数据实时的存取十分方便。AT24C02中带有的片内地址寄存器。每写入或读出一个数据字节后,该地址寄存器自动加1,以实现对下一个存储单元的读写。所有字节均以单一操作方式读取。为降低总的写入时间,一次操作可写入多达8个字节的数据。I2C总线是一种用于IC器件之间连接的二线制总线。他通过SDA(串行数据线)及SCL(串行时钟线)两根线在连到总线上的器件之间传送信息,并根据地址识别每个器件。AT24C02正是运用了I2C规程,使用主/从机双向通信,主机(通常为单片机)和从机(AT24C02)均可工作于接收器和发送器状态。主机产生串行时钟信号(通过SCL引脚)并发出控制字,控制总线的传送方向,并产生开始和停止的条件。无论是主机还是从机,接收到一个字节后必须发出一个确认信号ACK。AT24C02的控制字由8位二进制数构成,在开始信号发出以后,主机便会发出控制字,以选择从机并控制总线传送的方向。

image.png

image.png
图中AT24C02的1、2、3脚是三条地址线,用于确定芯片的硬件地址。第8脚和第4脚分别为正、负电源。第5脚SDA为串行数据输入/输出,数据通过这条双向I2C总线串行传送,第6脚SCL为串行时钟输入线, SDA和SCL都需要和正电源间各接一个10 K的电阻上拉。第7脚需要接地。
24C02中带有片内地址寄存器。每写入或读出一个数据字节后,该地址寄存器自动加1,以实现对下一个存储单元的读写。所有字节均以单一操作方式读取。为降低总的写入时间,一次操作可写入多达8个字节的数据。

4、 LCD显示模块

LCD1602A 是一种工业字符型液晶,能够同时显示16x02 即32个字符。(16列2行)。在日常生活中,我们对液晶显示器并不陌生。液晶显示模块已作为很多电子产品的通过器件,如在计算器、万用表、电子表及很多家用电子产品中都可以看到,显示的主要是数字、专用符号和图形。在单片机的人机交流界面中,一般的输出方式有以下几种:发光管、LED数码管、液晶显示器。发光管和LED数码管比较常用,软硬件都比较简单。
在单片机系统中应用晶液显示器作为输出器件有以下几个优点:
由于液晶显示器每一个点在收到信号后就一直保持那种色彩和亮度,恒定发光,而不像阴极射线管显示器(CRT)那样需要不断刷新新亮点。因此,液晶显示器画质高且不会闪烁。
液晶显示器都是数字式的,和单片机系统的接口更加简单可靠,操作更加方便。
液晶显示器通过显示屏上的电极控制液晶分子状态来达到显示的目的,在重量上比相同显示面积的传统显示器要轻得多。
相对而言,液晶显示器的功耗主要消耗在其内部的电极和驱动IC上,因而耗电量比其它显示器要少得多。

(1)引脚说明:

第1脚:VSS为地电源。
第2脚:VDD接5V正电源。
第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。
第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平
R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。
第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
第7~14脚:D0~D7为8位双向数据线。
第15脚:背光源正极。
第16脚:背光源负极。

(2)1602LCD的RAM地址映射以及标准字库表

LCD1602液晶模块内部的字符发生存储器已经存储了160个不同的点阵字符图形,这些字符图有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母。
它的读写操作、屏幕和光标的操作都是通过指令编程来实现的(说明:1为高电平,0为低电平)。
指令1:清显示,指令码01H,光标复位到地址00H位置。
指令2:光标复位,光标返回到地址00H 。
指令3:光标和显示模式设置 I/D:光标移动方向,高电平右移,低电平左移 。S:屏幕上所有文字是否左移或者右移。高电平表示有效,低电平则无效 。
指令4:显示开关控制。 D:控制整体显示的开与关,高电平表示开显示,低电平表示关显示。 C:控制光标的开与关,高电平表示有光标,低电平表示无光标。 B:控制光标是否闪烁,高电平闪烁,低电平不闪烁 。
指令5:光标或显示移位 S/C:高电平时移动显示的文字,低电平时移动光标 。
指令6:功能设置命令 DL:高电平时为4位总线,低电平时为8位总线。 N:低电平时为单行显示,高电平时双行显示。 F:低电平时显示5X7的点阵字符,高电平时显示5x10的点阵字符 (有些模块是 DL:高电平时为8位总线,低电平时为4位总线)。
指令7:字符发生器RAM地址设置 。
指令8:DDRAM地址设置 。
指令9:读出忙信号和光标地址。 BF为忙标志位,高电平表示忙,此时模块不能接收命令或者数据,如果为低电平表示不忙,模块就能接收相应的命令或者数据。
指令10:写数据 。
指令11:读数据 。
液晶显示模块是一个慢显示器件,所以在执行每条指令之前一定要确认模块的忙标志为低电平,表示不忙,否则此指令失效。要显示字符时要先输入显示字符地址,也就是告诉模块在哪里显示字符。
1602 内部显示地址如图9所示:

image.png

例如第二行第一个字符的地址是40H,那么是否直接写入40H 就可以将光标定位在第二行第 一个字符的位置呢?这样不行,因为写入显示地址时要求最高位D7恒定为高电平1,所以实际写入的数据应该是01000000B(40H)+10000000B(80H)=11000000B(C0H) 。在对液晶模块的初始化中要先设置其显示模式,在液晶模块显示字符时光标是自动右移的,无需人工干预。每次输入指令前都要判断液晶模块是否处于忙的状态。1602 液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形,如下图所示,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H 中的点阵字符图形显示出来,我们就能看到字母“A”。
液晶显示的原理是利用液晶的物理特性, 通过电压对其显示区域进行控制,有电就有显示,这样即可以显示出图形。液晶显示器具有厚度薄、适用于大规模集成电路直接驱动、易于实现全彩色显示的特点,目前已经被广泛应用在便携式电脑、数字摄像机、PDA移动通信工具等众多领域。

image.png

5、 键盘设计

本设计就采用行列式键盘,同时也能减少键盘与单片机接口时所占用的I/O线的数目,在按键比较多的时候,通常采用这样方法。
每一条水平(行线)与垂直线(列线)的交叉处不相通,而是通过一个按键来连通,利用这种行列式矩阵结构只需要N条行线和M条列线,即可组成具有N×M个按键的键盘。
在这种行列式矩阵键盘非键盘编码的单片机系统中,键盘处理程序首先执行等待按键并确认有无按键按下的程序段。
4×4矩阵键盘的工作原理
在键盘中按键数量较多时,为了减少I/O口的占用,通常将按键排列成矩阵形式,如图5所示。在矩阵式键盘中,每条水平线和垂直线在交叉处不直接连通,而是通过一个按键加以连接。这样,一个端口(如P1口)就可以构成4*4=16个按键,比之直接将端口线用于键盘多出了一倍,而且线数越多,区别越明显,比如再多加一条线就可以构成20键的键盘,而直接用端口线则只能多出一键(9键)。由此可见,在需要的键数比较多时,采用矩阵法来做键盘是合理的。
扫描原理
把每个键都分成水平和垂直的两端接入,比如说扫描码是从垂直的入,那就代表那一行所接收到的扫描码是同一个bit,而读入扫描码的则是水平,扫描的动作是先输入扫描码,再去读取输入的值,经过比对之后就可知道是哪个键被按下。
比如说扫描码送入01111111,前面的0111是代表此时扫描第一行P1.0列,而后面的1111是让读取的4行接脚先设为VDD,若此时第一行的第三列按键被按下,那读取的结果就会变成01111101(注意1111变成1101),其中LSB的第三个bit会由1变成0,这是因为这个按键被按下之后,会被垂直的扫描码电位short,而把读取的LSB的bit电位拉到0,此即为扫描原理。
由於这种按键是机械式的开关,当按键被按下时,键会震动一小段时间才稳定,为了避免让单片机误判为多次输入同一按键,我们必须在侦测到有按键被按下,就Delay一小段时间,使键盘以达稳定状态,再去判读所按下的键,就可以让键盘的输入稳定。图11为键盘整体模框图:

image.png

6、 声音提示模块

蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。 ;蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。蜂鸣器在电路中用字母“H”或“HA”(旧标准用“FM”、“LB”、“JD”等)表示。1.压电式蜂鸣器 压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。有的压电式蜂鸣器外壳上还装有发光二极管。
多谐振荡器由晶体管或集成电路构成。当接通电源后(1.515V直流工作电压),多谐振荡器起振,输出1.52.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。
压电蜂鸣片由锆钛酸铅或铌镁酸铅压电陶瓷材料制成。在陶瓷片的两面镀上银电极,经极化和老化处理后,再与黄铜片或不锈钢片粘在一起。
电磁式蜂鸣器 电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。
接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互缠绕
蜂鸣器驱动电路一般都包含以下几个部分:一个三极管、一个蜂鸣器、一个限流电阻。
蜂鸣器为发声元件,在其两端施加直流电压(有源蜂鸣器)或者方波(无源蜂鸣器)就可以发声,其主要参数是外形尺寸、发声方向、工作电压、工作频率、工作电流、驱动方式(直流/方波)等。这些都可以根据需要来选择。本设计采用有源蜂鸣器。

image.png

三极管Q1起开关作用,其基极的低电平使三极管饱和导通,使蜂鸣器发声;而基极高电平则使三极管关闭,蜂鸣器停止发声。

7、 继电器控制模块

电磁继电器一般由铁芯、线圈、衔铁、触点簧片等组成的。只要在线圈两端加上一定的电压,线圈中就会流过一定的电流,从而产生电磁效应,衔铁就会在电磁力吸引的作用下克服返回弹簧的拉力吸向铁芯,从而带动衔铁的动触点与静触点(常开触点)吸合。当线圈断电后,电磁的吸力也随之消失,衔铁就会在弹簧的反作用力返回原来的位置,使动触点与原来的静触点(常闭触点)释放。这样吸合、释放,从而达到了在电路中的导通、切断的目的。对于继电器的“常开、常闭”触点,可以这样来区分:继电器线圈未通电时处于断开状态的静触点,称为“常开触点”;处于接通状态的静触点称为“常闭触点”。继电器一般有两股电路,为低压控制电路和高压工作电路。

image.png

电路中继电器室通过PNP型三极管驱动,当阀值超过设定时,单片机会由高电平跳变成低电平,三极管导通继电器吸合,继电器起开关作用,可以驱动负载。

软件代码

#include 
#include
//宏定义
#define LCM_Data  P0		  //将P0口定义为LCM_Data
#define uchar unsigned char 
#define uint  unsigned int
#define w 6				//定义密码位数
//1602的控制脚
sbit lcd1602_rs=P2^7;
sbit lcd1602_rw=P2^6;
sbit lcd1602_en=P2^5;

sbit Scl=P3^4;			//24C02串行时钟
sbit Sda=P3^5;			//24C02串行数据

sbit ALAM = P2^1;		//报警	
sbit KEY = P3^6;		//开锁

sbit open_led=P2^2;		//开锁指示灯(选配)

bit  operation=0;		//操作标志位
bit  pass=0;			//密码正确标志
bit  ReInputEn=0;		//重置输入允许标志	
bit  s3_keydown=0;		//3秒按键标志位
bit  key_disable=0;		//锁定键盘标志

unsigned char countt0,second;	//t0中断计数器,秒计数器

void Delay5Ms(void);		//声明延时函数

unsigned char code a[]={0xFE,0xFD,0xFB,0xF7}; 				//控盘扫描控制表
//液晶显示数据数组
unsigned char code start_line[]	= {"password:       "};
unsigned char code name[] 	 	= { "===Coded Lock==="};	//显示名称
unsigned char code Correct[] 	= {"     correct    "};		//输入正确
unsigned char code Error[]   	= {"      error     "};  	//输入错误
unsigned char code codepass[]	= {"      pass      "}; 
unsigned char code LockOpen[]	= {"      open      "};		//OPEN
unsigned char code SetNew[] 	= {"SetNewWordEnable"};
unsigned char code Input[]   	= {"input:          "};		//INPUT
unsigned char code ResetOK[] 	= {"ResetPasswordOK "};
unsigned char code initword[]	= {"Init password..."};
unsigned char code Er_try[]		= {"error,try again!"};
unsigned char code again[]		= {"input again     "};

unsigned char InputData[6];									//输入密码暂存区
unsigned char CurrentPassword[6]={1,3,1,4,2,0}; 			//管理员密码(只可在程序中修改)
unsigned char TempPassword[6];
unsigned char N=0;				//密码输入位数记数
unsigned char ErrorCont;			//错误次数计数
unsigned char CorrectCont;			//正确输入计数
unsigned char ReInputCont; 			//重新输入计数
unsigned char code initpassword[6]={0,0,0,0,0,0};			//输入管理员密码后将密码初始为000000


//=====================5ms延时==============================
void Delay5Ms(void)
{
	unsigned int TempCyc = 5552;
	while(TempCyc--);
}

//===================400ms延时==============================
void Delay400Ms(void)
{
 unsigned char TempCycA = 5;
while(TempCycA--)
 {
  TempCycB=7269;
  while(TempCycB--);
 }
}

//=============================================================================================
//================================24C02========================================================
//=============================================================================================

void mDelay(uint t) //延时
{ 
	uchar i;
   	while(t--)
   	{
   		for(i=0;i<125;i++)
   		{;}
   	}
}
   

void Nop(void)		  //空操作
{
 	_nop_();		  //仅作延时用一条语句大约1us
 	_nop_();
	_nop_();
}

/*****24c02程序参照24c02时序图*****/
/*起始条件*/

void Start(void)
{
	Scl=1;
 	Nop();
 	Sda=0;
 	Nop();
}


 /*停止条件*/
void Stop(void)
{
 	Sda=0;
 	Scl=1;
 	Nop();
 	Sda=1;
 	Nop();
}

/*应答位*/
void Ack(void)
{
	Sda=0;
	Nop();
	Scl=1;
	Nop();
	Scl=0;
}

/*反向应答位*/
void NoAck(void)
{
 	Sda=1;
 	Nop();
 	Scl=1;
 	Nop();
 	Scl=0;
}

 /*发送数据子程序,Data为要求发送的数据*/
void Send(uchar Data)
{
   	uchar BitCounter=8;
   	uchar temp;
   	do
   	{
   		temp=Data;					   //将待发送数据暂存temp
   		Scl=0;
   		Nop();
   		if((temp&0x80)==0x80)		   //将读到的数据&0x80
   		Sda=1;
   		else 
   		Sda=0;
   		Scl=1;
   		temp=Data<<1;				   //数据左移
   		Data=temp;					   //数据左移后重新赋值Data
   		BitCounter--;				   //该变量减到0时,数据也就传送完成了
   	}
   	while(BitCounter);				   //判断是否传送完成
   	Scl=0;
}

/*读一字节的数据,并返回该字节值*/
uchar Read(void)
{
    uchar temp=0;
	uchar temp1=0;
	uchar BitCounter=8;
	Sda=1;
	do
	{
		Scl=0;
		Nop();
		Scl=1;
		Nop();
		if(Sda)				   //数据位是否为1
			temp=temp|0x01;	   //为1 temp的最低位为1(|0x01,就是将最低位变为1)
		else				   //如果为0
			temp=temp&0xfe;	   //temp最低位为0(&0xfe(11111110)最低位就是0)
		if(BitCounter-1)	   //BitCounter减1后是否为真
		{
			temp1=temp<<1;	   //temp左移
			temp=temp1;
		}
		BitCounter--;		   //BitCounter减到0时,数据就接收完了
	}
	while(BitCounter);		   //判断是否接收完成
	return(temp);
}

void WrToROM(uchar Data[],uchar Address,uchar Num)
{
  uchar i;
  uchar *PData;
  PData=Data;
  for(i=0;i0;x--)
	 for(y=110;y>0;y--);
}

//--------------------------写指令---------------------------
write_1602com(uchar com)//****液晶写入指令函数****
{
	lcd1602_rs=0;//数据/指令选择置为指令
	lcd1602_rw=0; //读写选择置为写
	P0=com;//送入数据
	delay(1);
	lcd1602_en=1;//拉高使能端,为制造有效的下降沿做准备
	delay(1);
	lcd1602_en=0;//en由高变低,产生下降沿,液晶执行命令
}

//-------------------------写数据-----------------------------
write_1602dat(uchar dat)//***液晶写入数据函数****
{
	lcd1602_rs=1;//数据/指令选择置为数据
	P0=dat;//送入数据
	delay(1);
	lcd1602_en=1; //en置高电平,为制造下降沿做准备
	delay(1);
	lcd1602_en=0; //en由高变低,产生下降沿,液晶执行命令
}

//-------------------------初始化-------------------------
void lcd_init(void)
{
	write_1602com(0x38);//设置液晶工作模式,意思:16*2行显示,5*7点阵,8位数据
	write_1602com(0x0c);//开显示不显示光标
	write_1602com(0x06);//整屏不移动,光标自动右移
	write_1602com(0x01);//清显示
}
//========================================================================================
//=========================================================================================




//==============将按键值编码为数值=========================
unsigned char coding(unsigned char m)	 
{
	unsigned char k;
	
	switch(m)
	{
		case (0x11): k=1;break;
		case (0x21): k=2;break;
		case (0x41): k=3;break;
		case (0x81): k='A';break;
		case (0x12): k=4;break;
		case (0x22): k=5;break;
		case (0x42): k=6;break;
		case (0x82): k='B';break;
		case (0x14): k=7;break;
		case (0x24): k=8;break;
		case (0x44): k=9;break;
		case (0x84): k='C';break;
		case (0x18): k='*';break;
		case (0x28): k=0;break;
		case (0x48): k='#';break;
		case (0x88): k='D';break;
	}
	return(k);
}


//=====================按键检测并返回按键值===============================
unsigned char keynum(void)
{
 	unsigned char row,col,i;
 	P1=0xf0;
 	if((P1&0xf0)!=0xf0)
 	{
	   	Delay5Ms();
        Delay5Ms();
   		if((P1&0xf0)!=0xf0)
		{
    		row=P1^0xf0;          //确定行线
			i=0;
			while(i<4)
			{
	 			if((P1&0xf0)!=0xf0)
	  			{
	   				col=~(P1&0xff);	  //确定列线
	   				break;            //已定位后提前退出   
	  			}
				else 
	  			{
	   				i++;
	   				P1=a[i];
	  			}
			}
		}
		else 
		{
			return 0;
		}
	
		while((P1&0xf0)!=0xf0);

		return (row|col);	 		//行线与列线组合后返回
 	}
 	else return 0;	         		//无键按下时返回0
}




//=======================一声提示音,表示有效输入========================
void OneAlam(void)
{
	ALAM=0;
	Delay5Ms();
    ALAM=1;
}

//========================二声提示音,表示操作成功========================
void TwoAlam(void)
{
	ALAM=0;
	Delay5Ms();
    ALAM=1;
    Delay5Ms();
	ALAM=0;
	Delay5Ms();
    ALAM=1;
}

//========================三声提示音,表示错误========================
void ThreeAlam(void)
{
	ALAM=0;
	Delay5Ms();
	ALAM=0;
	Delay5Ms();
    ALAM=1;
    Delay5Ms();
	ALAM=0;
	Delay5Ms();
    ALAM=1;

}

//=====================显示输入的N个数字,用H代替以便隐藏============================
void DisplayOne(void)
{
//	DisplayOneChar(9+N,1,'*');
	write_1602com(yi+5+N);
	write_1602dat('*');
}

//=======================显示提示输入=========================
void DisplayChar(void)
{
	if(pass==1)
	{
		//DisplayListChar(0,1,LockOpen);
		write_1602com(er);				   //在二行开始显示
		for(i=0;i<16;i++)
		{
			write_1602dat(LockOpen[i]);	   //显示open 开锁成功
		}
	}
	else
	{
		if(N==0)
		{
			//DisplayListChar(0,1,Error);
			for(i=0;i<16;i++)
			{
				write_1602dat(Error[i]);	//显示错误
			}
		}
		else
		{
			//DisplayListChar(0,1,start_line);	
			write_1602com(er);
			for(i=0;i<16;i++)
			{
				write_1602dat(start_line[i]);//显示开始输入	
			}
		}
	}
}

void DisplayInput(void)
{
	unsigned char i;
	if(CorrectCont==1)
	{
		//DisplayListChar(0,0,Input);
		write_1602com(er);
		for(i=0;i<16;i++)
		{
			write_1602dat(Input[i]);	//显示Input
		}
	}
}


//========================重置密码==================================================
//==================================================================================
void ResetPassword(void)
{
	unsigned char i;	
	unsigned char j;
	if(pass==0)
	{
		pass=0;			   
		DisplayChar();	   //显示错误
		ThreeAlam();	   //没开锁时按下重置密码报警3声
	}
	else				   //开锁状态下才能进行密码重置程序
	{
    	if(ReInputEn==1)   //开锁状态下,ReInputEn置1,重置密码允许
		{
			if(N==6)	   //输入6位密码
			{
				ReInputCont++;			//密码次数计数	
				if(ReInputCont==2)		//输入两次密码
				{
					for(i=0;i<6;)
					{
						if(TempPassword[i]==InputData[i])	//将两次输入的新密码作对比
							i++;
						else								//如果两次的密码不同
						{
							//DisplayListChar(0,1,Error);
							for(j=0;j<16;j++)
							{
								write_1602dat(Error[j]);	//显示错误Error
							}
							ThreeAlam();			//错误提示	
							pass=0;					//关锁
							ReInputEn=0;			//关闭重置功能,
							ReInputCont=0;
							DisplayChar();
							break;
						}
					} 
					if(i==6)
					{
						//DisplayListChar(0,1,ResetOK);
						write_1602com(er);
						for(j=0;j<16;j++)
						{
							write_1602dat(ResetOK[j]);	  //密码修改成功,显示
						}

						TwoAlam();				//操作成功提示
					 	WrToROM(TempPassword,0,6);		//将新密码写入24C02存储
						ReInputEn=0;
					}
					ReInputCont=0;
				}
				else					  //输入一次密码时
				{
					OneAlam();
					//DisplayListChar(0, 1, again); 		//显示再次输入一次
					write_1602com(er);
					for(j=0;j<16;j++)
					{
						write_1602dat(again[j]);			//显示再输入一次
					}					
					for(i=0;i<6;i++)
					{
						TempPassword[i]=InputData[i];		//将第一次输入的数据暂存起来						
					}
				}

			N=0;						//输入数据位数计数器清零
		   }
	    }
	}

}



//=======================输入密码错误超过三过,报警并锁死键盘======================
void Alam_KeyUnable(void)
{
	P1=0x00;
	{
		ALAM=~ALAM;				 //蜂鸣器一直闪烁鸣响
	}
}


//=======================取消所有操作============================================
void Cancel(void)
{	
	unsigned char i;
	unsigned char j;
	//DisplayListChar(0, 1, start_line); 
	write_1602com(er);
	for(j=0;j<16;j++)
	{
		write_1602dat(start_line[j]);	  //显示开机输入密码界面
	}
	TwoAlam();				//提示音
	for(i=0;i<6;i++)
	{
		InputData[i]=0;		//将输入密码清零
	}
	KEY=1;					//关闭锁
	ALAM=1;					//报警关
	operation=0;			//操作标志位清零
	pass=0;					//密码正确标志清零
	ReInputEn=0;			//重置输入充许标志清零
	ErrorCont=0;			//密码错误输入次数清零
	CorrectCont=0;			//密码正确输入次数清零
	ReInputCont=0;			//重置密码输入次数清零 
	open_led=1;				//开锁LED关闭 (选配)
	s3_keydown=0;
	key_disable=0;			//锁定键盘标志清零
	N=0;					//输入位数计数器清零
}


//==========================确认键,并通过相应标志位执行相应功能===============================
void Ensure(void)
{	
	unsigned char i,j;
	RdFromROM(CurrentPassword,0,6); 					//从24C02里读出存储密码
    if(N==6)
	{
	    if(ReInputEn==0)							//重置密码功能未开启
		{
			for(i=0;i<6;)
   			{					
				if(CurrentPassword[i]==InputData[i])	//判断输入密码和24c02中的密码是否相同
				{
				}
				else 									//如果有密码不同
				{										
					ErrorCont++;						//错误次数++
					if(ErrorCont==3)			//错误输入计数达三次时,报警并锁定键盘
					{
						write_1602com(er);
						for(i=0;i<16;i++)
						{
							write_1602dat(Error[i]);	
						}
						do
						Alam_KeyUnable();
						while(1);
					}
					else					 //错误次数小于3次时,锁死键盘3秒,然后重新可以输入
					{
						TR0=1;				//开启定时
						key_disable=1;			//锁定键盘
						pass=0;					//pass位清零
						break;					//跳出
					}
				}
			}

			if(i==6)					 //密码输入对时
			{
				CorrectCont++;					//输入正确变量++
				if(CorrectCont==1)				//正确输入计数,当只有一次正确输入时,开锁
				{
					write_1602com(er);
					for(j=0;j<16;j++)
					{
						write_1602dat(LockOpen[j]);		  //显示open开锁画面
					}
					TwoAlam();			//操作成功提示音
					KEY=0;											//开锁
					pass=1;											//置正确标志位
					TR0=1;											//开启定时
					open_led=0;										//开锁指示灯亮(选配)
					for(j=0;j<6;j++)								//将输入清除
					{
						InputData[i]=0;								//开锁后将输入位清零
					}
				}	
				else												//当两次正确输入时,开启重置密码功能
				{
					//DisplayListChar(0,1,SetNew);
					write_1602com(er);
					for(j=0;j<16;j++)
					{
						write_1602dat(SetNew[j]);					//显示重置密码界面
					}
					TwoAlam();									    //操作成功提示
					CorrectCont=0;									//正确计数器清零
				}
	  		}
	
			else			//=========================当第一次使用或忘记密码时可以用131420对其密码初始化============
			{
				if((InputData[0]==1)&&(InputData[1]==3)&&(InputData[2]==1)&&(InputData[3]==4)&&(InputData[4]==2)&&(InputData[5]==0))
		  	 	{
					WrToROM(initpassword,0,6); 				//强制将初始密码写入24C02存储
					//DisplayListChar(0,1,initword);			//显示初始化密码
					write_1602com(er);
					for(j=0;j<16;j++)
					{
						write_1602dat(initword[j]);				//显示初始化密码
					}
					TwoAlam();									//成功提示音
					Delay400Ms();								//延时40					TwoAlam();									//成功提示音
					N=0;										//输入位数计数器清零
				}
				else											//密码输入错误
				{
					//DisplayListChar(0,1,Error);
					write_1602com(er);
					for(j=0;j<16;j++)
					{
						write_1602dat(Error[j]);				 //显示错误信息
					}
 					ThreeAlam();										//错误提示音
					pass=0;	
				}
			}
		}

		else											//当已经开启重置密码功能时,而按下开锁键,
		{
			//DisplayListChar(0,1,Er_try);
			write_1602com(er);
			for(j=0;j<16;j++)
			{
				write_1602dat(Er_try[j]);			  //错误,请重新输入
			}
			ThreeAlam();							  //错误提示音
		}
	}

	else				   //密码没有输入到6位时,按下确认键时
	{
		//DisplayListChar(0,1,Error);
		write_1602com(er);		{
			write_1602dat(Error[j]);		 //显示错误
		}

 		ThreeAlam();										//错误提示音
		pass=0;	
	}
	
	N=0;													//将输入数据计数器清零,为下一次输入作准备

	operation=1;
}


//==============================主函数===============================
void main(void)
{
 	unsigned char KEY,NUM;
	unsigned char i,j;
 	P1=0xFF; 				   //P1口复位
	TMOD=0x11;				   //定义工作方式
 	TL0=0xB0;
	EA=1;					   //打开中断总开关
 	ET0=1;					   //打开中断允许开关
 	TR0=0;					   //打开定时器开关
 	Delay400Ms(); 	//启动等待,等LCM讲入工作状态
 	lcd_init(); 	//LCD初始化
	write_1602com(yi);//日历显示固定符号从第一行第0个位置之后开始显示
	for(i=0;i<16;i++)
	{
		write_1602dat(name[i]);//向液晶屏写开机画面
	}
	write_1602com(er);
	for(i=0;i<16;i++)
	{
		write_1602dat(start_line[i]);//写输入密码等待界面
	}
	write_1602com(er+9);	//设置光标位置
	write_1602com(0x0f);	//设置光标为闪烁
 	Delay5Ms(); //延时片刻(可不要)

						//初始化数据输入位数
 	while(1)		 //进入循环
 	{
		if(key_disable==1)						//锁定键盘标志为1时
			Alam_KeyUnable();					//报警键盘锁
		else
			ALAM=1;								//关报警

		KEY=keynum();							//读按键的位置码
		if(KEY!=0)								//当有按键按下时
		{	
			if(key_disable==1)					//锁定键盘标志为1时
			{
				second=0;						//秒清零
			}
			else								//没有锁定键盘时
			{
				NUM=coding(KEY);				//根据按键的位置将其编码,编码值赋值给NUM
				{
					switch(NUM)					//判断按键值
					{
						case ('B'):		;     				break;
						case ('C'): 	; 					break;		//ABC是无定义按键
						case ('D'): ResetPassword();		break;      //重新设置密码
						case ('*'): Cancel();				break;      //取消当前输入
						case ('#'): Ensure(); 				break;   	//确认键,
						default: 										//如果不是功能键按下时,就是数字键按下
						{	
							//DisplayListChar(0,1,Input);
							write_1602com(er);
							for(i=0;i<16;i++)
							{
								write_1602dat(Input[i]);				//显示输入画面
							}
						    operation=0;								//操作标志清零
							if(N<6)                   					//当输入的密码少于6位时,接受输入并保存,大于6位时则无效。
							{  
			//按键提示音						
								//DisplayOneChar(6+N,1,'*');
						 		for(j=0;j<=N;j++)
								{
									write_1602com(er+6+j);				//显示位数随输入增加而增加
									write_1602dat('*');					//但不显示实际数字,用*代替
								}
								InputData[N]=NUM;						//将数字键的码赋值给InputData[]数组暂存
								N++;									//密码位数加
							}
							else										//输入数据位数大于6后,忽略输入
							{
								N=6;									//密码输入大于6
							}
						}
					}
				}
			}
	 	} 
	}
}

//*********************************中断服务函数
void  time0_int(void) interrupt 1 		   //定时器T0
{
 	TL0=0xB0;
 	TH0=0x3C;		  //定时器重新赋初值
 	//TR0=1;
 	countt0++;			   //计时变量加,加1次时50ms
  	if(countt0==20)		   //加到20次就是1s
   	{
		countt0=0;		   //变量清零
		second++;		   //秒加
		if(pass==1)		   //开锁状态时
		{
			if(second==1)			//秒加到1s时
			{
				open_led=1;			//关指示灯(选配)
				TL0=0xB0;
 				TH0=0x3C;			//再次赋初值
				second=0;			//秒清零
			}
		}
		else						//不在开锁状态时
		{
			if(second==3)			//秒加到3时
			{
				TR0=0;				//关闭定时器
				second=0;			//秒清零
				key_disable=0;		//锁定键盘清零
				s3_keydown=0;	
				TH0=0x3C;			//重新赋初值
			}
			else
			    TR0=1;				//打开定时器
		}
			
   	}
}


注意事项

焊接注意事项:

LCD的注意事项:
  1. 采用焊接方法将金属基PIN安装于LCD时,从玻璃末端到PCB的焊接位置的长度至少5mm,焊接温度必须在260℃以下,且焊接时间必须在10秒以内,以免焊接过程中对装置的损坏,确保焊接性能。在230℃,30秒条件下,90%的焊料须紧密附着于PIN上。
  2. 对LCD基板或基PIN焊接位置的调整必须在安装前完成。严禁猛烈移动基PIN,否则会机械地破坏LCD屏与基PIN之间的连接点。焊接时平放LCD,尽量不让LCD受力。
  3. 焊接LCD基板时,将其小心、平衡地插入PCB插槽,以避免损坏基PIN或LCD基板。
  4. 焊接时,LCD基板不应长时间置于焊锡蒸汽中,清洗PCB时,必须避免污染LCD基板,否则可能损坏显示器表面的偏光片及封口胶。
  5. 显示器表面的保护膜直到焊接完成才可揭掉,禁止手指及其它硬物接触偏光片,禁止水和其他化学物质沾污装置,因为这些物质会污染显示器表面。
单片机焊接注意事项:

80s51单片机与其它单片机,dsp,arm芯片相比较而言是脆弱的,如果焊接时不小心就很可能把芯片损坏!建议用比较好的恒温可调的烙铁,功率在30瓦左右,温度控制在260到300度,建议采用点焊而不用拉焊,焊接时烙铁放在每个引脚的时间不能太长(最好低于3秒)。上边提到的方法还不能确保芯片完好,对芯片有危害的还有带电烙铁的静电感应!烙铁质量不是太好的,焊接时可以把烙铁电源插头拔掉。

其他的电子器件焊接须知:

一般分四步骤进行。①准备焊接:清洁被焊元件处的积尘及油污,再将被焊元器件周围的元器件左右掰一掰,让电烙铁头可以触到被焊元器件的焊锡处,以免烙铁头伸向焊接处时烫坏其他元器件。焊接新的元器件时,应对元器件的引线镀锡。②加热焊接:将沾有少许焊锡和松香的电烙铁头接触被焊元器件约几秒钟。若是要拆下印刷板上的元器件,则待烙铁头加热后,用手或银子轻轻拉动元器件,看是否可以取下。③清理焊接面:若所焊部位焊锡过多,可将烙铁头上的焊锡甩掉(注意不要烫伤皮肤,也不要甩到印刷电路板上!),用光烙锡头"沾"些焊锡出来。若焊点焊锡过少、不圆滑时,可以用电烙铁头"蘸"些焊锡对焊点进行补焊。④检查焊点:看焊点是否圆润、光亮、牢固,是否有与周围元器件连焊的现象。

实物图

IMG_20241221_100610.jpg
图1:空板正面
IMG_20241221_100620.jpg
图2:空板反面

IMG_20241221_100631.jpg
图3:元器件焊接与液晶屏

IMG_20241221_100638.jpg
图4:安装液晶屏

IMG_20241221_101248.jpg
图5:上电

IMG_20241221_101240.jpg
图6:开锁

设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

附件

序号文件名称下载次数
1
电子密码锁使用说明.doc
5
2
密码锁元件清单.xlsx
4
3
源程序.zip
10
克隆工程
添加到专辑
0
0
分享
侵权投诉

评论

全部评论(1)
按时间排序|按热度排序
粉丝0|获赞0
相关工程
暂无相关工程

底部导航