【单片机毕业设计】基于51单片机指纹考勤系统

当前Ic卡考勤系统已被很多企业使用,但利用Ic卡考勤有其固有的缺陷。例如,某员工可以携带别人的Ic卡,代替他人打卡,不能达到考勤的真正目的在计算机串口技术及生物测定技术的基础上,可以利用人身体的生物特征进行身份识别,开发出适于日常管理的考勤软件。因此,生物特征识别应用而生,开始走进我们身边的日常生活中,指纹识别作为生物特征识别的一个典型应用已经得到很广泛的应用和认可,指纹特征具有唯一性,是每个人终生不变的特征之一,并且各个人的各个指纹都不一样。指纹识别考勤系统以指纹模板为标志进行身份验证,利用指纹的唯一性彻底根除考勤中存在的虚假现象,有效地杜绝了考勤管理中的人为因素,充分体现了考勤管理的公正,避免了不必要的人事纠纷。

本系统采用STC89C52单片机作为主控芯片,液晶12864作为人机交互界面,通过矩阵键盘输入学号最后以指纹识别模块作为打卡考勤传感器,主要功能是录入学生学号和指纹信息并保存,通过一个指纹对应一个学号,可以查询迟到和缺勤的人员。当学生考勤时输入指纹,系统便可显示学号并记录已考勤人员,本系统没有时钟电路,故无法自动记录迟到和缺勤人员,本系统采用了手动方式,当上课时间一到,按下对应的按键,之后输入指纹的人员为迟到人员,没有输入指纹的人员为缺勤人员,学生上课考勤信息一目了然,另外本系统保存的信息可随意增加或删除,功能简单却实用。

关键字:考勤系统;单片机STC89C52;指纹识别;

1绪论

实验教学考勤系统(指纹考勤系统)在学校的日常学生管理中至关重要,通过对学生的考勤,可以增强对学生的管理和了解,规范他们平时的出勤并提高学习效率,维护学校的秩序,有益于事业单位的发展。传统考勤一般是点名签到,但是其过程繁琐容易出错而且存在很严重的人情考勤现象,为了节约时间以及资源并避免以上所述的弊端从而达到所需要的考勤效果,人们研究出各种新型的考勤措施如感应卡考勤等。但是即便这些新型的考勤措施问世,考勤还是存在着如感应卡遗失、消耗、人情考勤等诸多问题,需长期定时不定时地投入人力与费用。由于传统的考勤模式由于存在着各种弊病已经不能满足现代事业单位的需求。因此此时就需要一种可以完全避免以上弊端的新型方法和技术来达到考勤的理想效果,所以指纹考勤系统就顺应时代的发展出现了。 

指纹与人体的其他生理特征相比具有采集简单、处理成本低、终身不变、唯一性以及高度的可靠性等优势,同时指纹识别技术是当前国家安全、ID管理、门禁系统以及考勤系统中比较常见的一项生物特征识别技术,对于学生日常考勤这样参与人数多、工作量大的系统,指纹识别技术具有得天独厚的优势并能取得良好的效果。

1 硬件部分设计

1.1 单片机主芯片

89C52单片机是51系列单片机的一个成员,是8051单片机的简化版内部自带2K字节可编程FLASH存储器的低电压、高性能COMS八位微处理器,与IntelMCS-51系列单片机的指令和输出管脚相兼容。由于将多功能八位CPU和闪速存储器结合在单个芯片中,因此,89C52构成的单片机系统是具有结构最简单、造价最低廉、效率最高的微控制系统,省去了外部的RAM、ROM和接口器件,减少了硬件开销,节省了成本,提高了系统的性价比,具有以下主要特点:

1. 增强型8051单片机,6 时钟/机器周期和12 时钟/机器周期可以任意 选择,指令代码完全兼容传统8051

2. 工作电压:5.5V~3.3V(5V单片机)/3.8V~2.0V(3V 单片机)

3.工作频率范围:0~40MHz,相当于普通8051 的0~80MHz,实际工作 频率可达48MHz

4. 用户应用程序空间为8K字节

5. 片上集成512 字节RAM

6. 通用I/O 口(32 个),复位后为:P0/P1/P2/P3 是准双向口/弱上拉, P0 口是漏极开路输出,作为总线扩展用时,不用加上拉电阻,作为 I/O 口用时,需加上拉电阻。

7. ISP(在系统可编程)/IAP(在应用可编程),无需专用编程器,可通过串口(RxD/P3.0,TxD/P3.1)直接下载用户程序,数秒即可完成一片

8. 具有EEPROM 功能

9. 共3 个16 位定时器/计数器。即定时器T0、T1、T2

10.外部中断4 路,下降沿中断或低电平触发电路,Power Down 模式可 由外部中断低电平触发中断方式唤醒

11. 通用异步串行口(UART),还可用定时器软件实现多个UART

12. 工作温度范围:-40~+85℃(工业级)/0~75℃(商业级)

13. PDIP封装

本系统采用的STC89C52RC是STC公司生产的一种低功耗、高性能CMOS8位微控制器,具有 8K字节系统可编程Flash存储器。STC89C52使用经典的MCS-51内核,但做了很多的改进使得芯片具有传统51单片机不具备的功能。在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得STC89C52为众多嵌入式控制应用系统提供高灵活、超有效的解决方案STC89C52RC各个引脚的定义

VCC/GND:供电电源。

P0口:可以被定义为数据/地址的低八位,能够用于外部程序/数据存储器。在FIASH编程时,P0 口作为原码输入口,当FIASH进行校验时,P0输出原码,此时P0外部必须被拉高。
P1口:标准输入输出 I/O,P1口管脚写入1后,被内部上拉为高,可用作输入。在FLASH编程和校验时,P1口作为第八位地址接收。

P2口:既可用于标准输入输出I/O,也可用于外部程序储存器或数据存储器访问时的高八位地址。P2口在FLASH编程和校验时接收高八位地址信号和控制信号。

P3口:既可以作标准输入输出I/O,也可作为一些特殊功能口, 管脚备选功能:
P3.0 RXD(串行输入口)
P3.1 TXD(串行输出口)
P3.2 /INT0(外部中断0)
P3.3 /INT1(外部中断1)
P3.4 T0(记时器0外部输入)
P3.5 T1(记时器1外部输入)
P3.6 /WR(外部数据存储器写选通)
P3.7 /RD(外部数据存储器读选通)
RST:复位当振荡器复位器件时,要保持RST脚两个机器周期的高电平时间。
ALE/PROG:当访问外部储存器时,地址锁存允许的输出电平用于锁存地址的地位字节。在FLASH编程期间,此引脚用于输入编程脉冲。在平时,ALE端以不变的频率周期输出正脉冲信号,此频率为振荡器频率的1/6。

/PSEN:外部程序储存器的选通信号。在由外部程序储存器取指期间,每个机器周期两次/PSEN有效。但在访问外部数据存储器时,这两次有效的/PSEN信号将不出现。
/EA:当/EA保持低电平时,则在此期间外部程序存储器(0000H-FFFFH),不管是否有内部程序存储器。注意加密方式1时,/EA将内部锁定为RESET;当/EA端保持高电平时,此间内部程序存储器。在FLASH编程期间,此引脚也用于施加5V编程电源。
XTAL1:反向振荡放大器的输入及内部时钟工作电路的输入。
XTAL2:来自反向振荡器的输出。

STC89C52RC单片机

1.2单片机最小系统

1.2.1复位电路

复位电路的功能就是对CPU进行实时监测,当CPU落入死循环之后,能及时发现并使整个系统复位。为确保微机系统中电路稳未定可靠工作,复位可靠工作,复位电路是必不可少的一部分,复位电路的第一功能是上电复位。一般微机电路正常工作需要供电电源为4.75〜5.25V。由于微机电路是时序数字电路,它需要稳定的时钟信号,因此在电源上电时,只有当VCC上电时,只有VCC超过4.75V,低于5.25V以及晶体振荡器稳定工作时,复位信号才被撤除,微机电路开始正常工作。

为了确保本设计系统中的电路能够可靠、稳定的工作,复位电路是必不可少的一部分。复位电路是一种能够让电路恢复到起始状态的电路设备,原理类似于计算机的重启按键。如果主板上没有ISA总线,也就是8XX系列芯片组的主板,IDE的复位直接来自于南桥,在两者之间通常也会有一个非门或是反向电子开关,PCI总线的复位就直接来自于南桥,有些主板会在两者之间加一个跟随器,跟随器起缓冲延时作用。且PCI的常态为3.3V 或5V,复位时为0V,AGP总线的复位信号和PCI总线的复位信号是同路产生的。也有的主板AGP总线的复位也是由南桥直接供给的,常态时为高电频,复位时为低电频,对于北桥的复位信号也是和PCI总线的复位信号同时产生,也就是说PCI总线的复位信号,AGP总线的复位信号和北桥的复位信号通常是串在一根线上的,复位信号都相同,对于CPU的复位信号,不同的主板都是由北桥供给,I/O的复位信号是由南桥直接供给,通常是3.3V或5V。一般来说51单片机复位电路电路图如图所示。

复位电路电路图

本设计中复位电路与单片机的9引脚(RST)相连接,负责单片机的复位工作。本设计中复位电路的供电电压为5V。当给复位电路5V电压时,电容充电,在1K欧姆电阻R1上出现电压,使得单片机复位。几个毫秒之后,电容充满电,1K欧姆电阻R1上的电流为0A,同时电压也为0V,使得单片机进入工作状态。在单片机工作期间,按下开关S1,电容放电,在1K欧姆电阻R1上出现电压,使单片机复位。松开开关S1,电容又开始充电,在几个毫秒之后,单片机进入工作状态。本系统采用上电复位,因此在电路设计过程中没有手动按键复位。

 1.2.2 晶振电路

晶振全名叫做晶体振荡器,它结合内部电路产生所需的时钟频率,晶振所提供的时钟频率越高,单片机运行速度就越快。晶振的作用是提供本的时钟信号,使各部分保持同步进行。晶振通常与锁相环电路互相配合使用,以提供所需的时钟频率。单片机的一切指令的执行都是建立在这个基础上的,晶振的提供的时钟频率越高,那单片机的运行速度也就越快。

振荡电路(时钟电路)的主要任务是为C51单片机正常工作需要的时钟电路提供一个稳定的工作频率。晶振回路由电容和陶瓷谐振器并联组成,作为单片机的时钟源。C51内部有一个用于构成振荡器的高增益反相放大器,此放大器的输入和输出端分别是引脚XTAL1XTAL2,在XTAL1XTAL2端口接上时钟电源即可构成时钟电路。本设计中采用内部时钟产生方式。在XTAL1和XTAL2两端跨接晶振,与内部的反相器构成稳定的自激振荡器。其发出的时钟脉冲直接送入单片机内定时控制部件。电容应尽可能的安装在单片机芯片附近,以减少寄生电容,保证振荡器稳定可靠的工作。

晶振用一种能把电能和机械能相互转化的晶体在共振的状态下工作,以提供稳定,精确的单频振荡。在通常工作条件下,普通的晶振频率绝对精度可达百万分之五十。高级的精度更高。有些晶振还可以由外加电压在一定范围内调整频率,称为压控振荡器(VCO)。晶振的作用是为系统提供基本的时钟信号。晶振通常与锁相环电路配合使用,以提供系统所需的时钟频率。如果不同子系统需要不同频率的时钟信号,可以用与同一个晶振相连的不同锁相环来提供。在本设计中的晶振电路由一个晶振片Y1和两个22pF的电容C1、C2组成,晶振电路与STC89C52单片机的18引脚和19引脚相连接,以便提供单片机工作所需的时钟频率51单片机的晶振多采用12MHZ或者11.0592MHZ的频率。晶振电路电路图如图所示。

晶振电路电路图

1.3 LED电路

发光二极管与普通二极管一样是由一个PN结组成,也具有单向导电性。当给发光二极管加上正向电压后,从P区注入到N区的空穴和由N区注入到P区的电子,在PN结附近数微米内分别与N区的电子和P区的空穴复合,产生自发辐射的荧光。不同的半导体材料中电子和空穴所处的能量状态不同。当电子和空穴复合时释放出的能量多少不同,释放出的能量越多,则发出的光的波长越短。常用的是发红光、绿光或黄光的二极管。发光二极管的反向击穿电压大于5伏。它的正向伏安特性曲线很陡,使用时必须串联限流电阻以控制通过二极管的电流。发光二极管的核心部分是由P型半导和N型半导体组成的晶片,在P型半导体和N型半导体之间有一个过渡层,称为PN结。在某些半导体材料的PN结中,注入的少数载流子与多数载流子复合时会把多余的能量以光的形式释放出来,从而把电能直接转换为光能。PN结加反向电压,少数载流子难以注入,故不发光。当它处于正向工作状态时(即两端加上正向电压),电流从LED阳极流向阴极时,半导体晶体就发出从紫外到红外不同颜色的光线,光的强弱与电流有关。

LED灯的最基本的原理就是半导体二极管,将电转化为光。一般的LED都会采用注式电致原理制作,通常认为是发光二极管,也就是LED灯。

LED灯实物图

LED作为电子电路中最常用的指示作用,LED作为二极管的一个最典型元件,其两端也是有一个压降,一般1.3V左右,根据电子知识,只要在两端上有超过这个的压降,二极管就导通,为了保险起见,可以在单片机的IO口接上一个限流电阻,不至于上电时容易烧坏。例如当单片机的P35电平的时候,LED灯就亮,反过来,当为电平的时候,LED灯就灭。

LED驱动电路

1.4蜂鸣器电路

蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。蜂鸣器在电路中用字母“H”或“HA”(旧标准用“FM”、“ZZG”、“LB”、“JD”等)表示。蜂鸣器的发声原理由振动装置和谐振装置组成,而蜂鸣器又分为无源他激型与有源自激型。首先大家要了解有源和无源这里的“源”不是指电源,而是指震荡源。也就是说,有源蜂鸣器内部带震荡源,所以只要一通电就会叫。而无源内部不带震荡源,所以如果用直流信号无法令其鸣叫。必须用2K~5K的方波去驱动它。有源蜂鸣器往往比无源的贵,就是因为里面多个震荡电路。无源他激型蜂鸣器的工作发声原理是:方波信号输入谐振装置转换为声音信号输出有源自激型蜂鸣器的工作发声原理是:直流电源输入经过振荡系统的放大取样电路在谐振装置作用下产生声音信号

采用的是直流电压5V供电的有源蜂鸣器,但是一般的蜂鸣器在整个系统运行中需要比较大的电流,51单片机的IO口除了P0口外其他的都有一个弱上拉电阻,但IO的驱动电流也是非常低,不加外部的驱动电路是很难直接驱动的。而最常用的就是用普通的三极管来放大电流驱动,该三极管在电路中的作用是开关电路并且放大电流,本设计中采用三极管S8550作为驱动,当P37电平时,三极管导通,蜂鸣器响;当P37高电平时,三极管截止,蜂鸣器不响。报警提醒电路如图所示。

蜂鸣器驱动电路

1.5 矩阵键盘

通常按键所用的开关都是机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上就稳定的接通,在断开时也不会一下子彻底断开,而是在闭合和断开的瞬间伴随了一连串的抖动.按键稳定闭合时间长短是由操作人员决定的,通常都会在100ms以上,刻意快速按的话能达到40-50ms左右,很难再低了。抖动时间是由按键的机械特性决定的,一般都会在10ms以内,为了确保程序对按键的一次闭合或者一次断开只响应一次,必须进行按键的消抖处理。当检测到按键状态变化时,不是立即去响应动作,而是先等待闭合或断开稳定后再进行处理。

矩阵式键盘又叫行列式键盘。用I/O接口线组成行、列结构,键位设置在行、列的交点上。例如本设计4*4的行、列结构可组成16个键盘,比一个键位用一根I/O口线的独立式键盘少了一半的I/O接口线。而且键位越多,情况越明显。因此,在按键比较多时,往往采用矩阵式键盘。矩阵式键盘的按键识别方法 有“行扫描法”。行扫描法又称为逐行(或列)扫描查询法,是一种最常用的按键识别方法,介绍过程如下。  

(1)判断键盘中有无键按下 将全部行线Y0-Y3置低电平,然后检测列线的状态。只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平线与4根行线相交叉的4个按键之中。若所有列线均为高电平,则键盘中无键按下。   

(2)判断闭合键所在的位置 在确认有键按下后,即可进入确定具体闭合键的过程。其方法是:依次将行线置为低电平,即在置某根行线为低电平时,其它线为高电平。在确定某根行线位置为低电平后,再逐行检测各列线的电平状态。若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键。

按键输入电路

1.6 液晶显示电路

液晶显示模块是面向用户界面的,所以需要充分显示所要表达的内容,还要简洁,所以本系统采用的液晶显示模块为QC12864B,这个模块带有中文字库,不需要用户自己定义,只需要定义好位置显示即可,能够显示的汉字有32个,一个汉字相当于两个字符,所以,字符可以显示64个。LCD12864液晶显示模块共有20个引脚,通讯方式有串口和并口两种,串行可以用SID口通讯,并口是有8个数据口,实现8位并口传输。液晶的引脚图如下图所示。液晶的D0~D7是数据引脚,当液晶作为并行通信的时候,单片机要连接这8个数据口,而液晶的PSB引脚是选择并行或者串行的引脚,当低电平时为串行方式,当高电平时为并行方式。由于串口方式接口更简单,和单片机通信只需要2个IO口,在电路图设计中更为方便,所以一般在设计中可选择该方式。液晶还带了一个背光的对比度调节电位器,可以调节字体和背光之间的一个对比度,而现在很多液晶都已经出厂时就把这个电阻值定好了,无需在手动去调节

12864引脚图

液晶12864可以显示4行,每一行可以显示8个汉字,共可以显示32个汉字,对应这个系统来说,是足够的,一般出现不够行的情况下,大都是采用切换到第二个节目显示的方法;12864液晶跟单片机的通信方式有两种,一种是并行 ,一种是串行,对于串行来说,电路是比较简单不需要那么多的IO口,只需要两个IO就行,一发一收,但速率就比并行比较慢,而并行由于8个IO一起发送数据,因此要比较快,对于显示刷新频率要求不高的话,可以考虑用串行方式,毕竟电路简单,IO少,而本次的设计过程中采用的是串行行方式,跟LCD液晶的通信端只需要用到2个IO口,简单方便。

1.7 指纹模块通信电路

对于本系统指纹识别模块的选用,采用串口一体指纹识别模块。该模块是一个完整的指纹识别模块,能够完成指纹图像的获取、指纹对比匹配和指纹模板的存储功能。模块内部含有高性能的DSP处理器和用于储存的FLASH芯片等,具体能够完成指纹图像处理、模板生成、模板匹配、指纹储存和指纹搜索对比的功能,只需要按照协议进行串口数据的传输就可以实现各种指纹识别功能。

指纹模块由光学指纹传感器、高速DSP处理器、高性能指纹比对算法、大容量FLASH芯片等软硬件构成,集成了采集芯片和指纹算法芯片;性能稳定,结构简单,具有指纹录取、图像处理、指纹对比、指纹搜索和模板存储、手指感应输出等功能;采用8条精简指令,开发更简单。

产品特点:
◆ 功能完善:独立完成指纹采集、指纹登记、指纹比对(1:1)和指纹搜索(1:N)功能。

◆ 体积小巧:产品体积小巧,没有外接算法DSP芯片的电路板,已经集成一体,方便安装,故障少。 
◆ 超低功耗:产品整体功耗极低,适用于低功耗要求的场合。  
◆ 抗静电能力强:具有很强的抗静电能力,抗静电指标达到15KV以上。 
◆ 应用开发简单:开发者可根据提供的控制指令,自行指纹应用产品的开发,无需具备专业的指纹识别知识。 
◆ 安全等级可调:适用于不同的应用场合,安全等级可由用户设定调整。

技术参数:

供电电压:DC 4~6V

供电电流:工作电流:110mA(典型值)峰值电流:140mA 

指纹图像录入时间:<0.3秒

匹配方式:比对方式(1:1)搜索方式(1:N

特征文件:256字节

模板文件:512字节

存储容量 400枚

安全等级:3级 

认假率(FAR):<0.0001%

搜索时间:<1.0秒 (1:1000时,均值)

上位机接口:UART 

通讯串口波特率:57600bps

指纹识别模块

指纹模块通信电路图

1.8 DS3231时间电路

DS3231是低成本、高精度I2C实时时钟(RTC),具有集成的温补晶振(TCXO)和晶体。该器件包含电池输入端,断开主电源时仍可保持精确的计时。集成晶振提高了器件的长期精确度,并减少了生产线的元件数量。DS3231提供商用级和工业级温度范围,采用16引脚300mil的SO封装。

DS3231电源控制功能由温度补偿电压基准(VPF)和监视VCC电平的比较器电路提供。当VCC高于VPF时,DS3231由VCC供电,当VCC低于VPF但高于VBAT时,DS3231由VCC供电;当VCC低于VPF并低于VBAT时,DS3231由VBAT供电。为保护电池,VBAT首次加到器件时振荡器并不启动,除非加载VCC,或者向器件写入一个有效的I2C地址。典型的振荡器启动时间在1 s以内。在VCC加电后或者有效的I2C地址写入后大约2 s,器件会测量一次温度,并使用计算的修正值校准振荡器。一旦振荡器运行,只要电源(VCC或者VBAT)有效就会一直保持工作状态。器件每隔64 s进行一次温度测量并校准振荡器频率。

内部结构图

RTC保存秒、分、时、星期、日期、月和年信息。少于31天的月份,将自动调整月末的日期,包括闰年的修正。时钟的工作格式可以是24小时或带/AM/PM指示的12小时格式。提供两个可设置的日历闹钟和一个可设置的方波输出。地址与数据通过I2C双向总线串行传输。精密的、经过温度补偿的电压基准和比较器电路用来监视VCC状态,检测电源故障,提供复位输出,并在必要时自动切换到备份电源。另外,/RST监视引脚可以作为产生复位的手动输入。除计时精度高之外,DS3231还具有一些其它功能,这些功能扩展了系统主机的附加功能和选择范围。该器件内部集成了一个非常精确的数字温度传感器,可通过I2C*接口对其进行访问(如同时间一样)。这个温度传感器的精度为±3°C。片上控制电路可实现自动电源检测,并管理主电源和备用电源(即低压电池)之间的电源切换。如果主电源掉电,该器件仍可继续提供精确的计时和温度,性能不受影响。当主电源重新加电或电压值返回到容许范围内时,片上复位功能可用来重新启动系统微处理器。DS323l的引脚功能

1、32 kHz是32 kHz频率输出;

2、VCC用于主电源的DC引脚;

3、/INT/SQW为低电平有效中断或方波输出:是低电平有效复位引脚;

4~8、N.C.表示无连接,外部必须接地;

13、GND为地;

14、VBAT为备用电源输入;

15、SDA为串行数据输入、输出;

16、SCL为串行时钟输入。

DS3231电路

1.9 AT24C02电路

AT24C02是一个2K位串行CMOS E2PROM, 内部含有256个8位字节,CATALYST公司的先进CMOS技术实质上减少了器件的功耗。AT24C02有一个8字节页写缓冲器。该器件通过IIC总线接口进行操作,有一个专门的写保护功能。AT24C02支持I2C,总线数据传送协议I2C,总线协议规定任何将数据传送到总线的器件作为发送器。任何从总线接收数据的器件为接收器。数据传送是由产生串行时钟和所有起始停止信号的主器件控制的。主器件和从器件都可以作为发送器或接收器,但由主器件控制传送数据(发送或接收)的模式,由于A0、A1和A2可以组成000~111八种情况,即通过器件地址输入端A0、A1和A2可以实现将最多8个AT24C02器件连接到总线上,通过进行不同的配置进行选择器件。

AT24C02的存储容量为2K bit,内容分成32页,每页8Byte,共256Byte,操作时有两种寻址方式:芯片寻址和片内子地址寻址。

(1)芯片寻址:AT24C02的芯片地址为1010,其地址控制字格式为1010A2A1A0R/W。其中A2,A1,A0可编程地址选择位。A2,A1,A0引脚接高、低电平后得到确定的三位编码,与1010形成7位编码,即为该器件的地址码。R/W为芯片读写控制位,该位为0,表示芯片进行写操作。

(2)片内子地址寻址:芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。

AT24C02引脚图

AT24C02通信电路

2 软件部分设计

2.1 Keil软件介绍

Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。Keil提供了包括C编译器、宏汇编、链接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案,通过一个集成开发环境(μVision)将这些部分组合在一起。运行Keil软件需要WIN98、NT、WIN2000、WINXP等操作系统。Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil的优势。

C51工具包的整体结构,μVision与Ishell分别是C51 for Windows 和for Dos 的集成开发环境(IDE),可以完成编辑、编译、连接、调试、仿真等整个开发流程。开发人员可用IDE本身或其它编辑器编辑C或汇编源文件。然后分别由C51及C51编译器编译生成目标文件(.obj)。目标文件可由LIB51 创建生成库文件,也可以与库文件一起经L51 连接定位生成绝对目标文件(.abs)。abs文件由OH51 转换成标准的hex 文件,以供调试器dScope51 或tScope51 使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器如EPROM中。

Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。另外重要的一点,只要看一下编译后生成的汇编代码,就能体会到Keil的优势。C51工具包的整体结构,μVision与Ishell分别是C51 for Windows 和for Dos 的集成开发环境(IDE),可以完成编辑、编译、连接、调试、仿真等整个开发流程。开发人员可用IDE本身或其它编辑器编辑C或汇编源文件。然后分别由C51及C51编译器编译生成目标文件(.obj)。目标文件可由LIB51 创建生成库文件,也可以与库文件一起经L51 连接定位生成绝对目标文件(.abs)。abs文件由OH51 转换成标准的hex 文件,以供调试器dScope51 或tScope51 使用进行源代码级调试,也可由仿真器使用直接对目标板进行调试,也可以直接写入程序存贮器如EPROM中。

2009年2月发布Keil μVision4,Keil μVision4引入灵活的窗口管理系统,使开发人员能够使用多台监视器,并提供了视觉上的表面对窗口位置的完全控制的任何地方。新的用户界面可以更好地利用屏幕空间和更有效地组织多个窗口,提供一个整洁,高效的环境来开发应用程序。新版本支持更多最新的ARM芯片,还添加一些其他新功能。

2011年3月ARM公司发布最新集成开发环境RealView MDK开发工具中集成了最新版本的Keil μVision4,其编译器、调试工具实现与ARM器件的最完美匹配。

Keil4软件开启界面

2.2 Altium Designer软件

Altium Designer 是原Protel软件开发商Altium公司推出的一体化的电子产品开发系统,主要运行在Windows操作系统。这套软件通过把原理图设计、电路仿真、PCB绘制编辑、拓扑逻辑自动布线、信号完整性分析和设计输出等技术的完美融合,为设计者提供了全新的设计解决方案,使设计者可以轻松进行设计,熟练使用这一软件使电路设计的质量和效率大大提高电路设计自动化 EDA(Electronic Design Automation)指的就是将电路设计中各种工作交由计算机来协助完成。如电路原理图(Schematic)的绘制、印刷电路板(PCB)文件的制作、执行电路仿真(Simulation)等设计工作。随着电子科技的蓬勃发展,新型元器件层出不穷,电子线路变得越来越复杂,电路的设计工作已经无法单纯依靠手工来完成,电子线路计算机辅助设计已经成为必然趋势,越来越多的设计人员使用快捷、高效的CAD设计软件来进行辅助电路原理图、印制电路板图的设计,打印各种报表。

Altium Designer 除了全面继承包括Protel 99SE、Protel DXP在内的先前一系列版本的功能和优点外,还增加了许多改进和很多高端功能。该平台拓宽了板级设计的传统界面,全面集成了FPGA设计功能和SOPC设计实现功能,从而允许工程设计人员能将系统设计中的FPGA与PCB设计及嵌入式设计集成在一起。 由于Altium Designer 在继承先前Protel软件功能的基础上,综合了FPGA设计和嵌入式系统软件设计功能,Altium Designer 对计算机的系统需求比先前的版本要高一些。

Altium Designer Summer 09的发布延续了连续不断的新特性和新技术的应用过程。这必将帮助用户更轻松地创建下一代电子设计。同时,我们将令Altium Designer更符合电子设计师的要求。Altium的一体化设计结构将硬件、软件和可编程硬件集合在一个单一的环境中,这将令用户自由地探索新的设计构想。在整个设计构成中,每个人都使用同一个设计界面。 Summer 09版本解决了大量历史遗留的工具问题。其中就包括了增加更多的机械层设置、增强的原理图网络类定义。新版本中更关注于改进测试点的分配和管理、精简嵌入式软件开发、软设计中智能化调试和流畅的License管理等功能。

Altium Designer 09 软件

2.3 STC-ISP下载器

STC-ISP 是一款单片机下载编程烧录软件,是针对STC系列单片机而设计的,可下载STC89系列、12C2052系列和12C5410等系列的STC单片机,使用简便,现已被广泛使用。一般采用以下步骤来下载程序到51单片机:

  1. 选择单片机对应的型号;
  2. 加载要下载程序对应的HEX文件;
  3. 选择硬件串口号;
  4. 设置串口波特率;
  5. 开始下载编程;

STC-ISP软件

2.4系统方案     

以STC89C52单片机为核心器件,结合键盘电路、指纹识别模块、液晶显示屏、声光报警电路、继电器电路、掉电保存电路,利用单片机灵活的编程设计和丰富的I/O端口,及其控制的准确性通过软件程序来控制整个系统实现指纹考勤系统的以下基本功能: 

  1. 默认8位学号,可以添加学号;
  2. 可以添加指纹、识别指纹、删除指纹;
  3. 实现一个学号对应一个指纹;
  4. 可以显示所有缺勤人员的具体学号;
  5. 可以显示所有迟到人员的具体学号;
  6. 可以有简单的声光报警电路;
  7. 指纹签到成功时,液晶显示出具体对应的学号。

系统方框图

2.5工作原理介绍

本文是需要做出一套软件和硬件相结合的软硬件全套系统,而且需要经过编写代码,画电路图,调试,焊接,等一系列过程,最后做出一个成品。所以本次中的软件部分也是其他一个重要点。本文的软件部分只用到单片机的编程,而没有用到其他的数据和上位机程序,所以相对来说比较简单。主要就是对51单片机进行C语言编程。本文中采用的是模块化编程,即是把全部的程序分为几个模块,一个模块对应一个C文件,这样的好处是简洁明了,调试方便,软件部分大概可分为主程序模块、液晶显示模块、指纹模块、按键扫描模块、数据存储模块、时间模块

2.5.1 添加指纹流程

2.5.2 AT24C02写数据流

2.5.3 AT24C02读数据流程

2.5.4 DS3231写数据流程

2.5.5 DS3231读数据流程

2.5.6 按键输入流程

2.5.7 液晶屏流程

液晶显示模块选用QC12864B型号,它是一个完整的模块,只需要对其发布指令便可以完成相应的工作。LCD12864液晶模块内涵字库和处理器,具有自己的一套指令系统,用户只需要根据使用手册给出的指令系统,依照规定的指令格式,给模块发送指令,完成相应的功能。因为模块是带字库的,所以用户不需要自己定义字形,只需要设定好显示字符的坐标,再将字符发送过去即可。发送汉字时,因为是双字节,所以要先发送高字节,再发送低字节。并且当单片机向模块发送指令之前,需要检查模块是否处于忙状态,也就是需要读取BF标志位,当标志位为0时才能够发送新的指令。如果不想判断忙碌状态,就需要在发送指令之前先延时足够长的一段时间,确保上一条指令发送完毕。

3 调试与实现

3.1 调试遇到的问题

板子焊接好后,就要对单片机烧写程序,开始验证各个软件和硬件的部分,而本次中事实上不是一下就成功的,也遇了不少的问题,下面就对在调试过程中遇到的问题阐述说明:

1、无法下载程序

把电路基本焊接完成后,首先接上电源,用万用表量电压正常,接上USB转TTL模块,将电脑端的TXD、RXD和单片机的P31、P30相连,开始下载程序,发现一直没反应,最后发现是没有共地造成的,电脑和单片机板子共地之后正常下载;

2、按键不灵敏

板子上有个独立按键,有各自的功能,在测试中,发现所有的按键都不太灵敏,当按下录入按键时,本来液晶是显示提示正在注册卡片,但结果液晶一直没正常的显示,或者有时显示,硬件实在找不出问题后转向软件问题,最后发现按键消抖延时,长得太离谱,导致要一直按着按键才有反应,改短了时间为10ms,按键也正常反应。

3、液晶不显示

本次采用的是液晶12864作为显示器,采用的是串口接法,只需要用到单片机的2个IO口,方便简单,在调试过程中,发现液晶无法显示出文字信息,只亮了背光,把一样的程序烧到另外一个开发板上面,开发板上面的液晶正常显示,验证了是焊接的板子问题,再次将开发板上面的液晶换到焊接的板子上面,液晶也正常显示,由此可见,就是液晶本身的问题。想起液晶有串行和并行两种接口,咨询了卖家之后才得知,这一块液晶是并口接口的,想要换成串行接法,须将液晶背面的一个电阻焊掉,焊掉该电阻之后,重新插上液晶,正常显示。

4、蜂鸣器不响

刷卡成功通过之后,继电器会闭合,嘀嘀的一声,同时绿灯亮,但是在最后是实物调试中,绿灯亮了但是蜂鸣器没有响,一开始以为蜂鸣器烧坏了,拆了之后重新焊接一个新的,依然还是不工作;检查三极管的极性也是没有接反,最后发现是三极管的型号错了,焊接的是S8050,而S8550是PNP的,S8050是NPN的,按照设计的电路,S8050是无法驱动的,换了三极管型号之后,继电器正常工作。

总结

本设计论文主要介绍了基于单片机的指纹识别考勤系统的软件和硬件为一体的一整套系统,通过搜集资料,购买元器件,编程单片机源程序,设计各个电路模块,到最后对每个模块进行软件和硬件的调试,到最后整个硬件系统的组装,通过自己的努力和同学老师们的帮助,最后完成了这套软件和硬件相结合的实物系统,从我自身的观点去看,还是觉得挺成功的,毕竟从0基础开始学起,包括编写单片机的程序到画电路图,焊接板子,调试板子,查找问题,历经几个月的时间,总算还是有收获的,但是作为这样的一个设计,本身存在的问题也是很多的,在调试的过程中也确实遇到了不少的麻烦,比如一开始程序无法下载,液晶一直无法显示,以及焊接过程中烧坏LED等等,而在本次的设计过程中,对于射频的基本算法了解的还是比较少,没有具体的深入去研究这个方面的理论知识,而是直接从和单片机如何进行通信,如果对卡片进行编程实现的这一方面去着手,所以对于这方面做得还不算很好,还有很多可改进的地方,但是在这过程中,也需要硬件电路还有通信方式以及数据的处理这些,,还是自己可以学的到的,也从这个实践中更能够跟理论知识结合起来。

通过此次毕业设计,锻炼我的动手能力,深切体会到了理论和实际的差距,在通过写程序,还有对程序的编译调试过程中,能够检查差好多错误,通过分析这些错误产生的原因,让我的编程能力得到很大的提高,在解答这些问题之中,可以体会到问题解决的成就感,让我对单片机智能控制这一方面产生了浓厚的兴趣,以后我会继续在这方面学习,下苦功夫,争取在这个方面更上一个台阶。

由于自身水平有限,设计中一定存在很多不足之处,敬请各位老师批评指正

参考文献

[1]康华光.电子技术基础模拟部分[M].(第四版).北京:高等教育出版社,1999.

[2]谭浩强.C语言设计[M].(第三版).清华大学出版社,2005.

[3]郭天祥.新概念51单片机C语言教程[M].电子工业出版社,2009.

[4]谢自美.电子线路设计•实验•测试[M].(第三版).武汉:华中科技大学出版 社,2006.

[5]樊昌信 曹丽娜.通信原理[M].(第六版).北京:国防工业出版社,2009.

[6]全国大学生电子设计竞赛组委会编.全国大学生电子设计竞赛[M].北京理工 大学出 版社,1999.

[7]郁有文 常健 程继红.传感器原理及工程应用[M].(第二版).武汉:西安电子 科技大 学出版社,2006.

[8]崔炳哲.电子控制入门[M].北京:科学出版社,2003.

[9]郁有文 常健 程继红.传感器原理及工程应用[M].(第二版).武汉:西安电子 科技大 学出版社,2006.

[10]郭海英.基于单片机的电子安全密码锁的设计[M].现代电子技术,2005,(13)

致谢

时间不等人,一眨眼的功夫,几个月就过去了,毕业设计和毕业论文也在此刻接近尾声,而这也意味着我的大学生活即将结束,告别学校,走向社会。看着桌面放着的毕业设计,心里还是挺欣慰的,毕竟是自己辛辛苦苦的劳动成果,也是对大学生涯的一个交代,心里不得还是比较开心高兴的。在设计的过程中或多或少肯定遇到了不少的难题,设计到硬件部分还有软件部分,编写程序,画电路图,动手焊接电路板,安装调试。遇到的问题不懂的都是自己先去查找资料,上网百度,上图书馆查阅,向身边的同学,高年级的师兄师姐请教,当然还有向指导老师请教,真的很感谢他们对我的帮助,毫无保留,在这过程中,学到的东西还是很多的,毕竟平时学到的都是一些最基本的理论知识,实际动手做实验还是比较少的,最后在自己和同学们的帮助下还是很惊喜的完成了属于我自己的毕业设计和论文撰写。在这次的设计过程中,从最初对单片机的各种理论知识的简单了解,C语言程序的看不懂,焊接电路的无法上手,各种各样的问题克服,体会到真正动手去实际的意义,以前总是死记硬背的去记各种知识,但是在实际应用中还是比较少的,在实际中去找问题,解决问题,更为实际。而在本次毕设中,也让我懂得了一个道理,凡事都要有耐心,要善于运用学到的知识来验证问题,有问题肯定是正常的,但是在遇到问题时如何去克服,如何去解决那才是关键所在,要用尽自己所知去想,找资料,问同学,问老师,不懂就问,才有可能去解决问题。

大学,就这样临近尾声了,而我也即将离开这个学校,离开我的同学们,离开我敬爱的老师们,心里难免有一种舍不得的情结,这是正常的,但是换一个角度去看,或许这对我们来说都是必须要经历的,说明我们长大了,总要自己去生活,自己去面对各种各样的难题的一天,而我也会在未来的人生中记住这一段友情,师生情。在这里,再次对在此次设计中给予我援助之手的同学们和老师们,说一声,谢谢,真的很感谢你们。也希望你们在未来生活中越来越好。

代码


#include "config.h"	//用户需要添加的头文件

//对应的引脚定义,需要跟原理图一致
sbit BEEP = P3^7;
sbit LEDGREEN = P3^6;
sbit LEDRED = P3^5;

//宏定义,类似替换的功能
#define LEDON 0	 //led灯亮
#define LEDOFF 1 //led灯灭
#define BEEPON 0 //蜂鸣器响
#define BEEPOFF 1 //蜂鸣器不响

xdata uchar QUEQING_BUFF[MAX_YOUXIAO_CART_NUM];//缺勤数组,这个数组对应值为0表示是缺勤,1表示签到
xdata uchar CHIDAO_BUFF[MAX_YOUXIAO_CART_NUM];//迟到数组,这个数组对应值为0表示是迟到,1表示非迟到

uchar xuehao_count=0;//输入学号时,当前输入学号的位数   
uchar xuehao_buff[XUEHAO_MAXLEN];//输入学号时缓存的学号
uchar shangke=0;//上课标志位,0表示还没上课,1表示开始上课了
uchar Que_Num,Chi_Num,Qian_Num=0;//缺勤人数的数量,迟到人数的数量,签到人数的数量
uchar quecount,chicount;//查看名单时,剩余要显示的缺勤数量 迟到数量
uchar yecount;//当前页数,查看缺勤人员、迟到人员,超过3个名单可以切页
uchar nextcount;

uchar SYS_STATUS,pre_sysstatus = 0XFF;//当前系统状态,上一次系统状态
uchar timer_par=0,timer_val[7];//校准时间,timer_par为0~6分别对应校准年月日时分秒星期,timer[]对应的数值
uchar shangke_timer[2]={8,30};//上课时间

code char *NAME[8]={"张三","李四","王五","宋七","陈八"};

/********************************************************************
* 作者 :比酷wyh
* 名称 : 
* 功能 : 
* 输入 : 
* 输出 : 
***********************************************************************/
void DelayMs(uint i)//1ms延时
{
	uint x,j;
	for(j=0;j<i;j++)
	for(x=0;x<=120;x++);	
}

/********************************************************************
* 作者 :比酷wyh
* 名称 : 
* 功能 : 
* 输入 : 
* 输出 : 
***********************************************************************/
void Check_timer()//对比时间,判断是否迟到
{
	uchar num;

	DS3231_Readtime();//读取时间
	
	while(!AT24C0X_RcvByte(250,&num));//读取设置上课时间的时
	shangke_timer[0]=num;//存到shangke_timer数组中的时
	DelayMs(50);
	while(!AT24C0X_RcvByte(251,&num));//读取设置上课时间的分
	shangke_timer[1]=num;//存到shangke_timer数组中的分

	shangke=0;//先设置标志位为0,表示还没有上课,下面就判断时间

	if(Hour > shangke_timer[0])shangke=1;//打卡的时超过设置的时,迟到
	else if(Hour == shangke_timer[0])//同一时,判断分
	{
		if(Min > shangke_timer[1])shangke=1;//打卡的分超过设置的分,迟到
	}	
}
/********************************************************************
* 作者 :比酷wyh
* 名称 : 
* 功能 : 
* 输入 : 
* 输出 : 
***********************************************************************/
void Show_timer()
{
	//	Lcd12864_PutString(0,0,"打卡:"); 15:25:45  12:45
	Lcd12864_PutString(0,0,"");
	Lcd12864_PutData(Hour/10+'0');
	Lcd12864_PutData(Hour%10+'0');
	Lcd12864_PutData(':');
	Lcd12864_PutData(Min/10+'0');
	Lcd12864_PutData(Min%10+'0');
	Lcd12864_PutData(':');
	Lcd12864_PutData(Sec/10+'0');
	Lcd12864_PutData(Sec%10+'0');

//	Lcd12864_PutString(1,0,"考勤:");
	Lcd12864_PutString(0,5,"");
	Lcd12864_PutData(shangke_timer[0]/10+'0');
	Lcd12864_PutData(shangke_timer[0]%10+'0');
	Lcd12864_PutData(':');
	Lcd12864_PutData(shangke_timer[1]/10+'0');
	Lcd12864_PutData(shangke_timer[1]%10+'0');	
}
/********************************************************************
* 作者 :比酷wyh
* 名称 : 
* 功能 : 
* 输入 : 
* 输出 : 
***********************************************************************/
int main(void)
{
	uchar i,j,k=0,num;//中间变量
	uchar hang;//查看缺勤、迟到时,卡号显示在第几行,一页最多显示3行卡号
	uint t;

	LEDRED = LEDOFF;//不亮
	LEDGREEN = LEDOFF;//不亮
	BEEP = BEEPON;//叫
	DelayMs(200);

	UART1_Init();//串口1初始化 对应指纹模块
	EA=1;//开总中断

	Lcd12864_Init();//液晶LCD12864初始化		
	Lcd12864_ClrScreen();//清除屏幕显示内容

	DS3231_Init();//DS3231时间模块初始化

	SYS_STATUS = SYSNORMAL; //默认在主界面
		
	BEEP = BEEPOFF;//不叫

	DelayMs(1000);
	UART1_CLR_Buf();//清除串口1缓存数据

	for(i=0;i<MAX_YOUXIAO_CART_NUM;i++)
	{
		QUEQING_BUFF[i]=1;
		CHIDAO_BUFF[i]=1;	
	}

	t = _Getconlist();
	//从0开始算,判断之前录取过指纹的id,有效的QUEQING_BUFF对应值写入0,表示缺勤
	for(i=0;i<16;i++)//1010111100110110   0X5DBD
	{
		if((t>>i)&0X01==0X01)
		{
			QUEQING_BUFF[i]=0;
		}	
	}
							
	while(1)
	{	
		if(pre_sysstatus != SYS_STATUS) //状态改变一次 液晶清屏一次
		{
			pre_sysstatus = SYS_STATUS;
			Lcd12864_ClrScreen();//清除屏幕显示内容
		}

		Input_key();//获取按键输入值

		switch(SYS_STATUS)
		{
			case SYSNORMAL://菜单界面		
			Lcd12864_PutString(0,0,"A:签到0:恢复出厂");
			Lcd12864_PutString(1,0,"C:查缺勤D:查迟到");
			Lcd12864_PutString(2,0,"*:删学号#:设时间");
			Lcd12864_PutString(3,0,"B:添学号1:调时间");
			break;

			case TIANJIAXUEHAO:	//添加学号
			Lcd12864_PutString(0,1,"请输入学号");
			Lcd12864_PutString(1,0,"A:取消  B:确定");
			Lcd12864_PutString(2,0,"D:后退");
			Lcd12864_PutString(3,0,"学号:");
			for(i=0;i<xuehao_count;i++)
			{
				Lcd12864_PutData(xuehao_buff[i]+'0');
			}
			break;

			case DELXUEHAO:	//删除学号
			Lcd12864_PutString(0,1,"请输入学号");
			Lcd12864_PutString(1,0,"A:取消  B:确定");
			Lcd12864_PutString(2,0,"D:后退");
			Lcd12864_PutString(3,0,"学号:");
			for(i=0;i<xuehao_count;i++)
			{
				Lcd12864_PutData(xuehao_buff[i]+'0');
			}
			break;

			case QIANDAO://签到
			DS3231_Readtime();//读取时间
			Lcd12864_PutString(3,0,"  ");
			Lcd12864_PutData(Hour/10+'0');
			Lcd12864_PutData(Hour%10+'0');
			Lcd12864_PutString(3,2,"时");
			Lcd12864_PutData(Min/10+'0');
			Lcd12864_PutData(Min%10+'0');
			Lcd12864_PutString(3,4,"分");
			Lcd12864_PutData(Sec/10+'0');
			Lcd12864_PutData(Sec%10+'0');
			Lcd12864_PutString(3,6,"秒  "); 		
			Lcd12864_PutString(0,0,"    正在签到    ");
			Lcd12864_PutString(1,0,"    请按手指    ");
			Lcd12864_PutString(2,0,"    A:返回      ");
			num = _Search();//识别指纹
			if(num<=0XF0)//有效指纹最大范围
			{
				LEDGREEN = LEDON;
				Lcd12864_ClrScreen();//清除屏幕显示内容
				Check_timer();//对比时间,判断是否迟到
				if(shangke == 1)//开始上课之后,有效指纹签到会成功,但是是分配到迟到列表
				{
					if(CHIDAO_BUFF[num] == 1)//CHIDAO_BUFF数组默认的值就是1 也就是还没签到的
					{
						CHIDAO_BUFF[num]=0;//CHIDAO_BUFF数值里面的元素值写入0,表示迟到了
						QUEQING_BUFF[num]=1;//QUEQING_BUFF数组里面的元素值写入1,表示有签到了,只是迟到了
						Lcd12864_PutString(1,0,"    你已迟到    ");	
						Show_timer();//显示打卡时间	
						Qian_Num++;//签到人数数量加1										
					}
					else//第二次打卡
					{
						Lcd12864_PutString(0,0,"  已经签到成功  ");
					}
				}
				else //还没开始上课,现在签到成功的,都是非缺勤、非迟到的
				{
					if(QUEQING_BUFF[num] == 0)	//表示该卡还未签到过 现在签到
					{						
						QUEQING_BUFF[num]=1;//QUEQING_BUFF数组里面的对应元素为1 表示签到成功
						CHIDAO_BUFF[num]=2;  //CHIDAO_BUFF数组里面的元素的值写入2,表示已经签到并且非迟到
						Lcd12864_PutString(1,0,"    签到成功    ");
						Show_timer();//显示打卡时间	
						Qian_Num++;//签到人数数量加1											
					}
					else//第二次打卡
					{						
						Lcd12864_PutString(0,0,"  已经签到成功  ");							
					}				
				}					
				Lcd12864_PutString(2, 0, "姓名:");
				Lcd12864_PutString(2, 3, NAME[num]); //将对应人员的姓名显示出来				
				while(!AT24C0X_RcvStr(num*INDEX,xuehao_buff,XUEHAO_MAXLEN));//获取对应的学号
				Lcd12864_PutString(3,0,"学号:");
				for(i=0;i<XUEHAO_MAXLEN;i++)Lcd12864_PutData(xuehao_buff[i]+'0');//显示学号出来	
				BEEP = BEEPON;
				DelayMs(100);
				BEEP = BEEPOFF;
				DelayMs(100);
				BEEP = BEEPON;
				DelayMs(100);
				BEEP = BEEPOFF;
				LEDGREEN = LEDOFF;						
				DelayMs(4000);
			}
			else if(num == 0XFF)//该指纹无效不存在
			{
				LEDRED = LEDON;
				BEEP = BEEPON;
				Lcd12864_ClrScreen();//清除屏幕显示内容		
				Lcd12864_PutString(1,1,"指纹识别失败");	
				DelayMs(1000);
				Lcd12864_ClrScreen();//清除屏幕显示内容
				LEDRED = LEDOFF;
				BEEP = BEEPOFF;
			}
			break;

			case LURUZW://录入指纹
			Lcd12864_PutString(0,1,"正在添加指纹");
			Lcd12864_PutString(1,2,"请按手指");
			Lcd12864_PutString(2,0,"学号:");
			for(i=0;i<XUEHAO_MAXLEN;i++)Lcd12864_PutData(xuehao_buff[i]+'0');//显示学号出来			
			if(_Enroll()==1)//录指纹成功
			{
				Lcd12864_PutString(3,1,"");
				Lcd12864_PutData(Finger_id/10+'0');
				Lcd12864_PutData(Finger_id%10+'0');
				Lcd12864_PutString(3,2,"号添加成功  ");
				QUEQING_BUFF[Finger_id%10]=0;
				while(!AT24C0X_SendStr(Finger_id*INDEX,xuehao_buff,XUEHAO_MAXLEN));//写学号到AT24C02
				DelayMs(1000);
				Lcd12864_PutString(3,0,"                ");
				SYS_STATUS = SYSNORMAL;
			} 	
			break;

			case QUEQING://查看缺勤
			num = _GetNum();//读取总人数
			Lcd12864_PutString(0,0,"缺勤人员");
			Lcd12864_PutData('(');
			Lcd12864_PutData(num%10+'0');
			Lcd12864_PutData(',');  
			Lcd12864_PutData(Qian_Num%10+'0'); 
			Lcd12864_PutData(',');
			Lcd12864_PutData(Que_Num%10+'0');
			Lcd12864_PutData(')');
			hang=1;//学号从第二行开始显示
			if(quecount != 0)//必须有人缺勤才有显示缺勤的人员学号
			{
				for(j=yecount;j<16;j++)
				{
					if(QUEQING_BUFF[j]==0)
					{
						while(!AT24C0X_RcvStr(j*INDEX,xuehao_buff,XUEHAO_MAXLEN));
						Lcd12864_PutString(hang,0,"");
						for(i=0;i<XUEHAO_MAXLEN;i++)//液晶显示出迟到的学号出来
						{
							Lcd12864_PutData(xuehao_buff[i]+'0');
						}
						Lcd12864_PutString(hang,4,NAME[j]);//显示姓名
						hang++;
						if(hang>3)//当缺勤的人数超过3,按键B是切页
						{ 
							nextcount=j+1;
							break;
						}
					}
				}
			}	   		
			break;	

			case CHIDAO:  //查看迟到
			num = _GetNum();//读取总人数
			Lcd12864_PutString(0,0,"迟到人员");
			Lcd12864_PutData('(');
			Lcd12864_PutData(num%10+'0');
			Lcd12864_PutData(',');  
			Lcd12864_PutData(Qian_Num%10+'0'); 
			Lcd12864_PutData(',');
			Lcd12864_PutData(Chi_Num%10+'0');
			Lcd12864_PutData(')');
			hang=1;//学号从第二行开始显示
			if(chicount != 0) //必须有人迟到才有显示迟到的人员学号
			{
				for(j=yecount;j<16;j++)
				{
					if(CHIDAO_BUFF[j]==0)
					{
						while(!AT24C0X_RcvStr(j*INDEX,xuehao_buff,XUEHAO_MAXLEN));//将保存的学号读出来
						Lcd12864_PutString(hang,0,"");
						for(i=0;i<XUEHAO_MAXLEN;i++)//液晶显示出迟到的学号出来
						{
							Lcd12864_PutData(xuehao_buff[i]+'0');
						}
						Lcd12864_PutString(hang,4,NAME[j]);//显示姓名
						hang++;
						if(hang>3)//当迟到的人数超过3,按键B是切页
						{ 
							nextcount=j+1;
							break;
						}
					}
				}
			}			
			break;	

			case JIAOZHUN://校准时间
			Lcd12864_PutString(0,2,"校准时间");
			Lcd12864_PutString(3,0,"A:-> B:+ C:- D:");
			Lcd12864_PutData(16);
			//上面一行是校准年月日
			Lcd12864_PutString(1,1,"");
			Lcd12864_PutData(timer_val[0]/10+'0');
			Lcd12864_PutData(timer_val[0]%10+'0');
			Lcd12864_PutString(1,3,"");
			Lcd12864_PutData(timer_val[1]/10+'0');
			Lcd12864_PutData(timer_val[1]%10+'0');
			Lcd12864_PutString(1,5,"");
			Lcd12864_PutData(timer_val[2]/10+'0');
			Lcd12864_PutData(timer_val[2]%10+'0');
			//下面一行是校准时分秒
			Lcd12864_PutString(2,1,"");
			Lcd12864_PutData(timer_val[3]/10+'0');
			Lcd12864_PutData(timer_val[3]%10+'0');
			Lcd12864_PutString(2,3,"");
			Lcd12864_PutData(timer_val[4]/10+'0');
			Lcd12864_PutData(timer_val[4]%10+'0');
			Lcd12864_PutString(2,5,"");
			Lcd12864_PutData(timer_val[5]/10+'0');
			Lcd12864_PutData(timer_val[5]%10+'0');
			//显示出对应位置的箭头符号
			if(timer_par==0)Lcd12864_PutString(1,0," >");
			else if(timer_par==1)Lcd12864_PutString(1,2," >");
			else if(timer_par==2)Lcd12864_PutString(1,4," >");
			else if(timer_par==3)Lcd12864_PutString(2,0," >");
			else if(timer_par==4)Lcd12864_PutString(2,2," >");
			else if(timer_par==5)Lcd12864_PutString(2,4," >");
			break;

			case SETTING://设置上课时间
			Lcd12864_PutString(0,1,"设置考勤时间");
			Lcd12864_PutString(3,0,"A:-> B:+ C:- D:");
			Lcd12864_PutData(16);		
			//时分
			Lcd12864_PutString(1,1,"");
			Lcd12864_PutData(shangke_timer[0]/10+'0');
			Lcd12864_PutData(shangke_timer[0]%10+'0');
			Lcd12864_PutString(1,2,"时");
			Lcd12864_PutString(1,4,"");
			Lcd12864_PutData(shangke_timer[1]/10+'0');
			Lcd12864_PutData(shangke_timer[1]%10+'0');
			Lcd12864_PutString(1,5,"分");
			//显示出对应位置的箭头符号		
			if(timer_par==0)Lcd12864_PutString(1,0," >");
			else if(timer_par==1)Lcd12864_PutString(1,3," >");
			break;

			case RESET://恢复出厂设置
			Lcd12864_PutString(1,0,"是否恢复出厂设置");
			Lcd12864_PutString(3,0,"A:取消    B:确定");
			break;
		}	
	}
	return 1;
}
/********************************************************************
* 作者 :比酷wyh
* 名称 : 
* 功能 : 
* 输入 : 
* 输出 : 
***********************************************************************/
bit Input_key()
{
	uchar i,j,num,keyvalue=0;
	uchar buff[XUEHAO_MAXLEN];
	keyvalue = 	KeyScan();//获取键盘键值

	switch(keyvalue)
	{
		case KEY_0:
		case KEY_1:
		case KEY_2:
		case KEY_3:
		case KEY_4:
		case KEY_5:
		case KEY_6:
		case KEY_7:
		case KEY_8:
		case KEY_9:
				if(SYS_STATUS==TIANJIAXUEHAO||SYS_STATUS==DELXUEHAO)//添加学号界面下作为学号数字
				{
					if(xuehao_count < XUEHAO_MAXLEN) 
					{
						xuehao_buff[xuehao_count] = keyvalue;//缓存输入的学号
						xuehao_count++;//学号位数加1				
					}		
				}
				else if(SYS_STATUS==SETTING&&keyvalue==KEY_0)//设置打卡时间界面下,按0退出
				{
					SYS_STATUS = SYSNORMAL;		
				}
				else if(SYS_STATUS==SYSNORMAL&&keyvalue==KEY_0)//菜单界面下,按0恢复出厂
				{				
					SYS_STATUS = RESET;//进入恢复出厂设置界面		
				}
				else if(SYS_STATUS==SYSNORMAL&&keyvalue==KEY_1)//菜单界面下,按1校准时间	
				{	 
					SYS_STATUS = JIAOZHUN;//进入校准时间界面
					//将当前值读到对应的timer_val中	
					DS3231_Readtime();//读取时间 	
					timer_val[0]=Year;
					timer_val[1]=Month;
					timer_val[2]=Date;
					timer_val[3]=Hour;
					timer_val[4]=Min;
					timer_val[5]=Sec;
					timer_val[6]=Day;
					timer_par=0;	
				}
				else if(SYS_STATUS==JIAOZHUN&&keyvalue==KEY_1)//在校准时间界面下,按1返回
				{
					SYS_STATUS = SYSNORMAL;//返回到功能界面	
				}		
		break;


		case KEY_JING: //按键#
		 			if(SYS_STATUS == SYSNORMAL)//菜单界面下,按#设置打卡时间
					{										
						SYS_STATUS = SETTING;
						timer_par=0;
						while(!AT24C0X_RcvByte(250,&num));//读取设置上课时间的时
						shangke_timer[0]=num;//存到shangke_timer数组中的时
						DelayMs(50);
						while(!AT24C0X_RcvByte(251,&num));//读取设置上课时间的分
						shangke_timer[1]=num;//存到shangke_timer数组中的分						
					}
					else if(SYS_STATUS==SETTING)//在设置打卡时间界面下,按#返回
					{
						SYS_STATUS = SYSNORMAL;//返回到功能界面	
					}					
		break;

		case KEY_MI: //按键*
					if(SYS_STATUS == SYSNORMAL) //菜单界面下作为删除学号功能
					{
						SYS_STATUS = DELXUEHAO;//进入删除学号界面
						xuehao_count = 0;
					}
		break;

		case KEY_A:											
					if(SYS_STATUS == SYSNORMAL)//菜单界面下作为签到功能
					{	
						SYS_STATUS = QIANDAO; 					
					}
					else if(SYS_STATUS==QIANDAO||SYS_STATUS==TIANJIAXUEHAO||SYS_STATUS==QUEQING \
					       ||SYS_STATUS==CHIDAO||SYS_STATUS==DELXUEHAO||SYS_STATUS==RESET)//返回到主界面
					{	
						SYS_STATUS = SYSNORMAL; 
					}					
					else if(SYS_STATUS==JIAOZHUN)//校准界面下,A按键作为切换
					{
						timer_par++;
						if(timer_par==6)timer_par=0;//星期不修改,所以到6就行,要改星期就到7
						Lcd12864_ClrScreen();//清除屏幕显示内容
					}
					else if(SYS_STATUS==SETTING)//设置考勤时间界面下,A按键作为切换
					{
						timer_par++;
						if(timer_par==2)timer_par=0;
						Lcd12864_ClrScreen();//清除屏幕显示内容	
					}								
		break;

		case KEY_B:	
					if(SYS_STATUS == SYSNORMAL)//菜单界面下作为添加学号功能
					{	
						SYS_STATUS = TIANJIAXUEHAO;//进入添加学号界面
						xuehao_count = 0; 						
					}
					else if(SYS_STATUS==RESET)//恢复出厂
					{				
						if(_Empty())//清空指纹库全部模板
						{
							Lcd12864_ClrScreen();//清除屏幕显示内容
							Lcd12864_PutString(1,1,"请稍后");						
							for(i=0;i<MAX_YOUXIAO_CART_NUM;i++)
							{
								QUEQING_BUFF[i]=1;
								CHIDAO_BUFF[i]=1;	
							}
							for(i=0;i<255;i++)
							{
								while(!AT24C0X_SendByte(i,' ')); //将学号也清空
								DelayMs(20);
							}
							Lcd12864_PutString(2,1,"恢复出厂成功");
							DelayMs(1000);
							Lcd12864_ClrScreen();//清除屏幕显示内容
							SYS_STATUS = SYSNORMAL; 
						}		
					}
					else if(SYS_STATUS == TIANJIAXUEHAO)//添加学号
					{	
						if(xuehao_count == XUEHAO_MAXLEN)//学号够数,按B确定
						{											
							SYS_STATUS = LURUZW;//输入的学号不存在,有效,进入添加指纹
							//先判断当前输入的学号是否存在,不能重复添加
							for(i=0;i<16;i++)
							{
								DelayMs(50);
								while(!AT24C0X_RcvStr(i*INDEX,buff,XUEHAO_MAXLEN));
								for(j=0;j<XUEHAO_MAXLEN;j++)//对比xuehao_buff和buff,判断学号存不存在
								{
									if(xuehao_buff[j]!=buff[j])
									{
										j=0;
										break;	
									}
								}																	
								if(j==8)//当前输入的学号是存在的
								{
									Lcd12864_ClrScreen();//清除屏幕显示内容
									Lcd12864_PutString(2,1,"该学号已存在");
									DelayMs(1000);
									Lcd12864_ClrScreen();//清除屏幕显示内容
									xuehao_count = 0; 
									SYS_STATUS = TIANJIAXUEHAO;//还是回到添加学号的界面										
									break;
								}
							}													
						} 
					}
					else if(SYS_STATUS == DELXUEHAO)//删除学号
					{	
						if(xuehao_count == XUEHAO_MAXLEN)//学号够数,按B确定
						{											
							Lcd12864_ClrScreen();//清除屏幕显示内容
							//先判断当前输入的学号是否存在,存在才能删除
							for(i=0;i<16;i++)
							{
								DelayMs(50);
								while(!AT24C0X_RcvStr(i*INDEX,buff,XUEHAO_MAXLEN));//将每个学号挨个挨个读出来
								for(j=0;j<XUEHAO_MAXLEN;j++)//对比xuehao_buff和buff,判断学号存不存在
								{
									if(xuehao_buff[j]!=buff[j])
									{
										j=0;
										break;	
									}
								}		
								if(j==8)//当前输入的学号是存在的,所以执行删除
								{
									if(_Del(i)==1)//将对应的指纹删除
									{
										QUEQING_BUFF[i]=1;//删除了,就按照已签到人员处理,
										memset(xuehao_buff,' ',XUEHAO_MAXLEN);
										while(!AT24C0X_SendStr(i*INDEX,xuehao_buff,XUEHAO_MAXLEN));//将对应的学号删除																			
										Lcd12864_PutString(2,1,"学号删除成功");
										DelayMs(1000);
										Lcd12864_ClrScreen();//清除屏幕显示内容
										xuehao_count = 0; 
										SYS_STATUS = SYSNORMAL;//回到主界面
										break;
									}										
								}
							}
							if(i==16)//当前输入的学号不存在
							{
								Lcd12864_PutString(2,1,"该学号不存在");
								DelayMs(1000);
								Lcd12864_ClrScreen();//清除屏幕显示内容
								xuehao_count = 0; 	
							}														
						} 
					}
					else if(SYS_STATUS == QUEQING)//缺勤页面 A退出 B切页
					{	
						if(quecount > 3)//人数数量超过3个,需要切页
						{
							quecount -= 3;//一页最多显示3个卡号,所以减去3
							yecount = nextcount;//每页最多显示3个卡号,按一次,相当于翻一页,即已经显示过3行了
							Lcd12864_PutString(1,0,"                ");
							Lcd12864_PutString(2,0,"                ");
							Lcd12864_PutString(3,0,"                ");
						}
					}
					else if(SYS_STATUS == CHIDAO)//迟到页面 A退出 B切页
					{	
						if(chicount > 3)
						{
							chicount -= 3;//一页最多显示3个卡号,所以减去3
							yecount = nextcount;//每页最多显示3个卡号,按一次,相当于翻一页,即已经显示过3行了
							Lcd12864_PutString(1,0,"                ");
							Lcd12864_PutString(2,0,"                ");
							Lcd12864_PutString(3,0,"                ");
						}
					}
					else if(SYS_STATUS == JIAOZHUN)//校准时间界面,B按键是+1
					{
						 timer_val[timer_par]++;//对应的值加1
						 if(timer_val[timer_par]==60)timer_val[timer_par]=0;//最大值只能到59,60就跳到0					
					}
					else if(SYS_STATUS == SETTING)//设置考勤时间界面,B按键是+1
					{
						 shangke_timer[timer_par]++;//对应的值加1
						 if(shangke_timer[timer_par]==60)shangke_timer[timer_par]=0;//最大值只能到59,60就跳到0					
					}									
		break;

		case KEY_C:	  					
					if(SYS_STATUS == SYSNORMAL)//菜单界面下作为查看缺勤功能
					{
//						Lcd12864_ClrScreen();//清除屏幕显示谳					
//						Lcd12864_PutString(0,0,"");
//						for(i=0;i<16;i++)
//						{
//							Lcd12864_PutData(QUEQING_BUFF[i]%10+'0');
//						}
//						DelayMs(1000);
//						Lcd12864_ClrScreen();//清除屏幕显示谳
						
						SYS_STATUS = QUEQING;
						Que_Num=quecount=0;//缺勤人数数量,先清0
						yecount=0;//页数数量,先清0
						for(i=0;i<16;i++)
						{
							if(QUEQING_BUFF[i]==0)//判断是否为0,0表示缺勤,缺勤人数+1
							{
								quecount++;//得到缺勤人数
								Que_Num++;									
							}
						}
					}
					else if(SYS_STATUS == JIAOZHUN)//校准时间界面,C按键是-1
					{
						 if(timer_val[timer_par]>0)timer_val[timer_par]--;//对应的值减1					
					} 
					else if(SYS_STATUS == SETTING)//设置考勤时间界面,C按键是-1
					{
						 if(shangke_timer[timer_par]>0)shangke_timer[timer_par]--;//对应的值减1						
					}					
		break;


		case KEY_D:	
					if(SYS_STATUS==TIANJIAXUEHAO||SYS_STATUS==DELXUEHAO||SYS_STATUS==DELXUEHAO)//输入学号时,按D是后退一格
					{						
						if(xuehao_count > 0)//学号后退一位
						{
							xuehao_count--;							
							Lcd12864_PutString(3,2,"            ");						
						} 
					} 
					else if(SYS_STATUS == SYSNORMAL)//菜单界面下作为查看迟到功能
					{
						SYS_STATUS = CHIDAO;
						Chi_Num=chicount=0;//迟到人数数量,先清0
						yecount=0;//页数数量,先清0
						for(i=0;i<16;i++)
						{
							if(CHIDAO_BUFF[i]==0)//判断是否为0,0表示迟到,迟到人数+1
							{
								chicount++;//得到迟到人数
								Chi_Num++;									
							}
						}
					} 
					else if(SYS_STATUS == JIAOZHUN)//校准时间界面下,按D是确认修改时间
					{
						DS3231_ModifyTime(timer_val[0],timer_val[1],timer_val[2],timer_val[3],timer_val[4],timer_val[5],timer_val[6]);
						SYS_STATUS = SYSNORMAL;//返回到功能界面	
						Lcd12864_ClrScreen();//清除屏幕显示内容
						Lcd12864_PutString(1,2,"校准成功");
						DelayMs(1000);	
						Lcd12864_ClrScreen();//清除屏幕显示内容			
					} 
					else if(SYS_STATUS == SETTING)//设置考勤时间界面,按D是确认设置时间
					{
					 	while(!AT24C0X_SendByte(250,shangke_timer[0]));//写入时
					 	DelayMs(50);
					 	while(!AT24C0X_SendByte(251,shangke_timer[1]));//写入分
					 	SYS_STATUS = SYSNORMAL;//返回到功能界面	
						Lcd12864_ClrScreen();//清除屏幕显示内容
						Lcd12864_PutString(1,2,"设置成功");
						DelayMs(1000);	
						Lcd12864_ClrScreen();//清除屏幕显示内容				
					}													
		break;				
	}

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值