指纹考勤机的设计与实现.doc_第1页
指纹考勤机的设计与实现.doc_第2页
指纹考勤机的设计与实现.doc_第3页
指纹考勤机的设计与实现.doc_第4页
指纹考勤机的设计与实现.doc_第5页
免费预览已结束,剩余23页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

指纹考勤机硬件部分完成-小记最初的想法1初步调试:基本资源、128642初步调试:例程的学习及移植3成功移植3程序中比较重要的指令和变量:4Fingerprint.c中模块操作的主要函数:5主函数分析9指纹考勤机的硬件操作规范11二次开发的技术要点及其实现方法12实现了接续录入功能,这是很大的突破12实现了单个指纹的删除16实现了被删除指纹号的优先分配19实现了考勤信息的存储21实现了通信模式下向上位机传输考勤信息22付出多多,收获多多27缺憾272B的错误27待续27最初的想法一直以来都对一些技术感兴趣,比如指纹识别算法、无线通信、红外感应、微处理器体系架构等等,在大二的时候就想做一台指纹考勤机,进门的时候手指按一下,什么都搞定了,不用看着老师被忽悠,我也不解,如果我是一个技术出身的老师,我早就做这件事情了,何必点名,费劲。这段时间,有了空档,我就着手做这件事情。是这样设计的,硬件由指纹模块和控制板,上位机用VB开发。实现的功能大致是:上位机数据库保存人员信息,即指纹号对应学号、姓名等信息,指纹机可以脱机采集到岗信息(指纹号),在通信模式下,与上位机连接,将采集到的指纹号上传,上位机处理这些信息,生成缺勤名单文件(txt/word/excel皆可),并将缺勤名单发到指定的邮箱。硬件平台的选择求助了一下淘宝,可选的不多,一个能存储162枚指纹的指纹模块进入了我的视线,4线,VCC/GND/TXD/RXD,还提供51例程,它用的是国产指纹识别专用DSP,只开放用串口用户命令接口。看到51就知道就知道它跟识别算法之类的没有关系,注定要对着daasheet编写驱动。但可以短时间内达到设计目标并以此为契机继续学习也不错。很明显,需要用VB实现的是:串行通信、数据库编程、文件操作、网络编程。串行通信和数据库编程毕业设计的时候用过,后两者要现学。开始的时候模块资料上写单片机的RXD P3.0和TXD P3.1只能连模块,连max232都不能有,所以至少双串口才可以实现与上位机通信,打算使用双串口的60S2,而且没有用户flsah,只能在录取完信息保持开机状态知道传输完信息给上位机处理。但测试后发现,模块在链接max232的情况下仍能正常工作,而且文档显示它有用户16页共512字节flash可以使用。这样,录入过程中将信息存入flash,在上位机通信模式下,重新初始化串口波特率实现上位机通信,为了提高运行速度,没有使用89C52,还是使用了60S2。这样软硬件都清晰了。任务示意如下:初步调试:基本资源、12864 对于60S2的使用,绝对可以用轻车熟路来形容了。实验室正好有一块51单片机开发板,串口、中断按钮、LED、12864屏都有,就是没有任何资料,管脚信息只能用万用表一点点测量。下载keil、下载STC_ISP,单片机精灵、串口助手、拷贝之前的设计资料,测量端口、用了一天的时间,基本把LED、定时器、串口、按键、外部中断全部搞定了。对于12864的资料网上有很多,找到了一篇很有用的文章非常好-12864带字库液晶学习,跟着上面的步骤,用了一下午时间把LCD显示也搞好了。形成了第一个测试工程代码standard1。12864的端口定义如下:12864顾名思义有128*64个像素点,即是横向128个点,竖向64个点,由于该液晶控制器支持的字符为8*16,汉字为16*16,因此只能显示四行,如果是汉字,为每行显示8个,如果是字符,每行显示16个。 驱动函数一般包括四个函数: 1、写命令函数; 2、写数据函数; 3、读状态函数; 4、读数据函数; 这四个函数并不是必须全部写的,具体要看你实现的功能,如果只是单纯的显示汉字和字符,写命令、写数据、读状态这三个函数就够了,如过你还需要进行一些绘图的操作,那读数据函数也必须书写。 另外关于读状态函数,其实也就是用于判忙操作,我看郭天祥的书里面是这样说的:原则上每次对控制器进行读写操作之前,都必须进行读写检测,由于单片机的操作速度慢于液晶控制器的反应速度,因此可不进行读写检测,或者只进行简短的延时即可。因此,读状态函数也可以不写,只用简短的延时函数替换即可。知道这些信息后,下面就是驱动函数的移植和使用了,在三个基本驱动函数、初始化函数、延时函数的基础上,用户要调用的函数有下面几个:void Set_Cursor(unsigned char x, unsigned char y)/设置光标void Display_Char(unsigned char Alphabet)/(设置光标后)显示单个字符void Lcd_ClrScreen()/清屏void Display_String(unsigned char x,unsigned char y,unsigned char *Alphabet)/在指定位置显示字符串void Display_HZ(unsigned char x,unsigned char y,unsigned char *HZ)/在指定位置显示汉字void Display_HZ_Line(unsigned char x,unsigned char y,unsigned char *HZ)/在制定位置显示汉字,可自动换行初步调试:例程的学习及移植成功移植模块的默认波特率是9600bps,但是开发板用的是12M晶振,产生不了该波特率,误差太大,总是产生握手失败的错误,只能选用11.0592的晶振(从蛟哥的板子上取下),例程中的12864的工作方式是串行工作方式,一直显示不正常,于是我把之前测试通过的并行工作方式代码移植进了例程。又对例程中的端口进行了重定义,对工程代码进行了规范化(函数化、模块化、头文件)处理,将模块TXD连到P3.0,RXD连到P3.1,在接通开发板上的5V。成功运行了例程,实现了:录入指纹、指纹识别、删除全部指纹的基本功能。形成了standard2工程文件包。此时工程文件如下,也是最终的样式。端口重定义如下(io.h):模块通过串口与单片机相连,通过查询的方式发送和接收,没有使用串口中断,串口初始化代码和发送代码在uart.c中。Timer.c中只有一个初始化函数和中断函数,不是很关键,只是为了实现命令发送函数中的一个毫秒级延时。Io.c中有端口定义和外部中外部中断相关的代码。Fingerprint.c中是与模块相关的驱动函数。指纹识别算法是模块内部的DSP实现的,对用户提供了串行命令接口,使用的时候只需要调用这些命令帧,加上校验码就行了,再等待模块响应看是否成功。是用模块的过程是:设备握手、探测手指获取图像、生成特征模版1存入缓冲区、生成特征模版2存入缓冲区、将两个特征文件合成一个指纹模版存入指纹库、从库中进行指纹对比。这些功能都是有具体的指令进行操作的。此外,删除全部指纹、删除单个指纹、也是有专用的命令的。所以指纹模块的文档就显得非常重要。程序中比较重要的指令和变量:Savenumber是当前已经存储的指纹个数;searchnum是识别函数返回的搜索到的指纹号;modeflag是模式标志,例程有:识别模式、录入模式。由于中断中不能写太多的东西,主程序中通过查询clearallflag和changeflag进行删除全部指纹和模式转换的具体操作。外部中断转换模式的代码如下:消抖方式很有特色。FIFO是发送命令后接受模块相应的缓冲区,FIFOnumber是缓冲区中的数据个数。这个数据在命令发送函数中使用。标志为在中断中Fingerprint.c中模块操作的主要函数:一个最最重要的函数是命令发送函数,所有的操作都是通过这个函数。其帧处理方式还是很值得学习的。这个函数的处理过程:发送命令、接收响应帧到FIFO、判断校验码、返回值。bit Command(unsigned char *p,unsigned char MaxTime) /命令解析,给模块发送一个命令 unsigned char count=0,tmpdat=0,temp=0,i=0,package=0,flag=0;unsigned char checksum=0; bit result=0, start=0,stop=0; TxdByte(0xef);/数据包包头识别码 TxdByte(0x01);/数据包包头识别码 i=*p; /数组的第“0”个元素、里面存放了本数组的长度,把这个长度给变量i,方便进行操作 p+; p+; /不知道为什么它命令中定义了一个无效字节,所以挪了两次 for (count=i-1; count!=1;count-) /Sent command String temp=*p+; TxdByte(temp);/将命令发送出去 result=TURE;/发送完成,结果为真 (真为1) FifoNumber=0; for (count=MAX_NUMBER+1; count!=0; count-)/清空所有FIFO数组里面的内容,写入0X00 FIFOcount-1=0x00; if (result) result=FALSE; start =FALSE; stop =FALSE; count=0; clk0=0;/清零CL0计数 do /do的内容/restart0: if (RI=1)/如果接收到数据 tmpdat=SBUF;/先把接收到的数据放到tmpdat中 RI=0;if (tmpdat=0xef)&(start=FALSE)/这个数据为第一个传回来的数据,也就是“指令应答”的第一个字节 count=0; FIFO0=tmpdat;/读入第一个应答字节(0XEF),存在第“0”个元素中 flag=1;goto restart0;/继续接受收 if(flag=1)/第一个字节已经回来,所以flag=1成立 if(tmpdat!=0x01) /接收数据错误,将重新从缓冲区接收数据 flag=0;/接收应答失败result=FALSE; start =FALSE; stop=FALSE; count=0;goto restart0;/如果成功接收到0xef01,可以开始接收数据flag=2;/flag=2;表示应答成功,可以开始接收数据了count+;/现在count=1;FIFOcount=tmpdat;/读入第二个应答字节(0X01),存在第“1”个元素中 start=TURE;/应答成功可以开始接收数据 goto restart0; if(flag=2)&(start=TURE) count+; /数据元素下标 FIFOcount=tmpdat;/依次存入if(count=6)checksum=FIFOcount+checksum; /从这里才是计算校验和,这与帧格式有关if(count=8) package=FIFO7*0X100+FIFO8;/计算包长度,这是帧格式规定的stop= TURE;if(stop)if(count=package+8) /校验不完全正确,因为没有比对第二个校验码checksum=checksum-FIFOcount-1;if(checksum!=FIFOcount&0xff) result=FALSE; /校验失败,置结果标志为0else result=TURE; flag=0;break; /do的内容-结束/while (clk0=MaxTime)&(count=2)/如果不成功,再验证一次,如果两次不成功,返回失败 return(0); void Clear_All(void) /清空所有指纹信息,清空前首先验证和指纹模块通讯是否正常 if( VefPSW() = 1 ) /验证成功 Command(DELE_all,50); /清空指纹库 unsigned char ImgProcess(unsigned char BUFID) /发获取图像并生成特征文件,存入BUFID中/输入参数为缓冲区号,1为生成特征文件1,2是生成特征文件2,这是通过发送GEN1和GEN2命令完成的。在录入指纹的时候需要进行两次采集和成模版再存储。在识别的时候只需要用这个函数采集一个特征文件去指纹库做比对。bit Searchfinger(void)/搜索指纹(发送搜索命令、以及根据返回值确定是否存在) ,应该是默认使用特征文件1进行比对,如果比对成功,则结果放在Searchnum中。unsigned char search(void)/先获取一次特征文件,调用上一个函数,如果比对成功则返回searchnum,如果不成功则返回255。bit savefingure(unsigned char ID)/保存指纹unsigned char enroll(void) /采集两次指纹,生成1个 指纹模板void numshow(unsigned char num)/指纹号显示函数 strnum0= num/100+48; /+48是为了转换在ASCII码 百 strnum1= (num%100)/10+48;/+48是为了转换在ASCII码 十 strnum2= num%10+48; /+48是为了转换在ASCII码 个 Display_HZ_Line(3,0,指纹号:); Display_HZ_Line(3,4,strnum);void modecheck(void)/模式指示函数 if(modeflag=0) green=0;red=1;Display_HZ_Line(1,0, 识别模式 ); else red=0;green=1;Display_HZ_Line(1,0, 录入模式 ); numshow(0);void shakehands(void)/握手函数,其实就是调用了若干次bit VefPSW(void)主函数分析void main(void) timer_init();/定时器初始化 uart_init();/串口初始化 Display_Init();/12864初始化 Lcd_ClrScreen();/清屏 externalint_init();/外部中断初始化 Display_HZ_Line(0,0, 指纹识别系统 ); shakehands();/与模块握手 while(1)/查询四个标志 if( modeflag = 1 )/为录入指纹模式 if(k2=0)/按一次按键,录入一个指纹 delay1ms(10); if(k2=0)/如果仍为低电平,表示按键有效 while(k2=0);/等待松手 if( SaveNumber=1 & searchnum = 162 ) numshow(searchnum);/显示搜索到的指纹 buzzer=0;delay1ms(100);buzzer=1;/蜂鸣器响三声 relay=0;delay1ms(3000); relay=1;/继电器打开3秒 if(searchnum=255)/识别指纹失败 /蜂鸣器响三声 numshow(0); buzzer=0;delay1ms(100);buzzer=1;delay1ms(100); buzzer=0;delay1ms(100);buzzer=1;delay1ms(100); buzzer=0;delay1ms(100);buzzer=1;delay1ms(100); if(clearallflag=1)clearallflag=0;Clear_All(); red=0; /红色灯亮 green=1; /蜂鸣器长响一次,表示清除所有指纹结束modeflag=1;/进入录入指纹模式buzzer=0;delay1ms(800);buzzer=1;SaveNumber=0;numshow(0);modecheck(); if( changeflag = 1 ) modecheck(); changeflag=0; /while(1) end /具体的操作是:开机握手成功后,自动进入识别模式,该模式下可以进行指纹识别,若识别成功,则显示识别成功。按中断1按钮转换到录入模式,有文字提示。在该模式下按下K2键,可以录入一次指纹,录入的方法是,将手指放置,指纹头灯灭,待灯再次亮起,将手指第二次放置,LCD显示录入成功,并显示该指纹的指纹号。按下外部中断0键可以删除所有指纹。这些基本的功能是实现本次设计的基石。指纹考勤机的硬件操作规范在经过二次开发后,硬件的操作规范如下:开机进入识别模式,按中断按钮1进入录入模式,按中断按钮0删除所有指纹(此时有please wait 提示)。定义了K4辅助键。sbit k4=P35;辅助键+外部中断0按钮 清除1个指纹(该指纹为上一次识别的指纹)。辅助键+外部中断1按钮 保存残页,考勤信息满32个(1页)自动保存,而在录入完毕之后,需要这个功能保存残页。录入键+外部中断1按钮 进入上位机通信模式;该模式下串口的波特率被重新初始化,一旦进入则不可与模块再次通信,再次正常工作的方式只有重启。二次开发的技术要点及其实现方法实现了接续录入功能,这是很大的突破例程有一个致命的缺点,那就是每次开机录入总是从001号指纹开始录入,因为savenumber总是初始化为0。这就意味着不允许有人员的新加入,每次继续录入,总会覆盖之前的指纹,这是应用的最大障碍。我觉得一个完善的模块不会有这么大的bug,难道要用户自己外接flash?我查询了一下模块文档,文档上说模块在flash中开辟了512字节的用户记事本,共16页,每页32字节。而且有专门的命令进行写记事本和读记事本操作。这下就豁然开朗了,可以将存入的指纹个数存储到用户记事本中,每次开机将这个数读出,在这个个数的基础上继续录入,当录入、删除(单个、全部)动作时调用写入函数更新指纹数。就解决了这个问题。我就是这么做的,每次录入将指纹个数存储在第0页的首字节。封装了两个函数,指纹数的存储和读出函数。实现过程如下:5standard3这是模块文档上关于写记事本和读记事本的指令说明:我在fingerprint.c中定义了读记事本和写记事本的两个命令帧因为这两个命令的内容不固定,需要填入参数(比如读哪一页?写哪些数据?等等),所以没有定义成code类型,放在了RAM里。我封装的两个函数如下/实现对第0页的首字节的写操作,存储当前指纹数void fpnum_to_flash(unsigned char k)int count=0;int sum=0;int i;WENT10=0;/存放到第0页WENT11=k;/存放的数据是输入参数for(i=6;i8)&0x00ff; /将两个字节校验和存入最后两个字节WENT44=sum&0x00ff; while (1) /发送命令并检验响应是否正确 if(Command(WENT,20) & (FifoNumber=11) & (FIFO9=0x00) break ; count+; if (count=2)/如果不成功,再验证一次,如果两次不成功,返回失败 break ; /实现对第0页的首字节的读操作,读取模块中指纹数unsigned char fpnum_readout(void)int count=0;int sum=0;int i=0;RDNT10=0;for(i=6;i8&0x00ff; /将两个字节校验和存入最后两个字节RDNT12=sum&0x00ff; while (1) if(Command(RDNT,20) & (FifoNumber=43) & (FIFO9=0x00) return FIFO10 ; count+; if (count=2)/如果不成功,再验证一次,如果两次不成功,返回失败 return 0; 这样,我在开机是调用读取指纹数函数赋值给savenumber:在录入时向模块更新savenumber在删除全部时,更新savenumber注意,在删除1次时不用更新savenumber,后边会有陈述,savenumber只是录入指纹的当前上限数,并不代表总数,当有指纹被删除时,再次录入时被删除的指纹号会被优先分配。这样避免浪费指纹号。也就是说savenumber-delete_num是模块中的指纹总数。实现了单个指纹的删除如果有人员移除群体,那么要把全部指纹清楚显然是不合适的,模块文档中有删除某一个指纹的命令,实现删除单个指纹的功能是十分必要的。由于接口不足,我用辅助键+外部中断0键的组合方式实现了对上一次识别成功的指纹进行删除操作。组合键即利用了外部中断相应迅速,又弥补了外部中断只有两路的缺点。6standard4(这个文档中还添加了写flash和读flash的函数)在fingerprint.c中添加删除单个指纹的命令和函数删除的指纹号为searchnum_before,最后一次识别的指纹号在识别模式中赋值在外部中断0中判断组合键,并调用删除一个指纹的函数,给出提示信息。可以想象,无论是接续录入还是考勤信息的保存,对flash的读写操作都是必不可少的,在模版程序4中我还在fingerprint.c中编写了对flash的读写操作函数。实现了被删除指纹号的优先分配已经实现了对最后一次识别的指纹进行删除的操作,但是被删除的指纹号相当于留了一个空档,没办法再次利用的话,会对模块的存储上限162的判断产生干扰,也对人员信息管理产生不便。所以有必要对已经删除的指纹号进行优先分配。当没有可以利用的废止指纹号时,再利用savenumber+作为录入指纹的指纹号。思路是这样的,定义一个数据组delete_num32,用来存储被删除的指纹号,其中delete_num0存储了被删除的指纹号数量,这个数组被存储在15页。当录入模式下使用了废止指纹号、删除一次、删除全部时都要更新这个数组,但是更新的方式不同,用delete_refresh_flag分别标识这三种动作。采用了delete_refresh这个函数来向flash更新数组信息。在有效录入后、删除一次后、删除全部后置标志位,在录入模式、全删除模式下轮询此函数。开机将delete_num数据从15页读出一整页。在录入模式下若delete_num0非零,总是使用delete_num1作为指纹号,delete_refresh_flag=1,更新数组的方式是从delete_num1到delete_numdelete_num0依次左移。然后写入15页。在外部中断0的删除一次操作后,delete_refresh_flag=2,数组的更新方式是将删除的指纹号加到数组的尾部即delete_numdelete_num0+1,并将delete_num0+。然后写入15页。在外部中断0的全部删除的操作后,delete_refresh_flag=3,更新数组的方式是将delete_num数组全部赋值为0。然后写入15页。delete_refresh这个函数在识别模式、录入模式、删除模式下均有轮询执行,所以不用担心数组信息得不到及时的更新。录入模式下:删除一次和删除全部时:delete_refresh()函数的实现:实现了考勤信息的存储这项功能是绝对的重中之重,如果不能存储,就只能在保持开机的状态下向上位机传输,而且有没有那么多的可用RAM还是一回事。在之前的flash读写基础上实现考勤信息的存储与读出并不是难事。我的设计是定义了两个重要的数据变量:checkin_num2和checkin32,前者放在14页,后者放在1页及以后。checkin_num0存储当前到勤的指纹数。checkin_num1存储已经用了的页数。Checkin数组保存已经到勤的指纹号,当有32个数据时,自动将其写到flash中的一页,这样做是因为51的RAM实在是不够用,没有大数组支持,这种大数据量的处理实在是尴尬。当考勤结束时,辅助键+外部中断1按钮,保存残页。实现的方式是:void check_to_flash(unsigned char k)这个函数在识别成功时调用,它对checkin_num和check数组进行更新,并在满页时写入flash。如下:函数体如下:辅助键+外部中断1按钮时,执行的操作是保存残页,即将checkin中的不到32数据写入flash.由于中断中不能写太多的内容,所以采用了标志位的方式,checkin_save=1,再将保存残页的操作放到delete_fresh的函数体中(利用其及时刷新的特性)。执行的时候LCD左上角有“R”提醒。void check_out_flash(void)/将到勤的信息读出来 ,接下来要做读具体信息在通信模式下这个函数将checkin_num读出来,便于下一步将具体的考勤信息读出来。实现了通信模式下向上位机传输考勤信息由于串口波特率要发生变化,将不能与模块再通信,所以在通信之前一定要将所有的考勤信息读出来,但是放置这些考勤信息需要若干个32字节的数组,纵观整个程序,也只有FIFO/delete_num/check_in/WENT这4个满足条件,也就是说虽然模块可以存储162个指纹,但是只能对128个人进行考勤,后来发现疏忽了一个问题,FIFO是响应帧的缓冲区,在读数据的时候也会对数组进行破坏,所以不可用。这样就只能对96个人进行考勤。实在是没有办法的事情。而且用到这些数组之后,就是破坏性的使用,要向正常使用考勤机,就必须传输完之后重启。可以开机就进入通信模式,也可以保存残页后立即进入通信模式。Delete_num保存第一页,checkin保存第二页。WENT保存第三页。录入键+中断1,置位comumunication=1主函数中轮询communication(识别模式、录入模式、删除模式的轮询要&communication=0)查询到之后,读出checkin_num,即到勤总数和页数信息,然后将到勤指纹号读到上面的几个数组中去,然后用InitUART()重新初始化串口为4800的波特率。并提示相关信息。此时,进入while(1)等待上位机发命令,将这些到勤信息发送出去。此时,我定义了一个小协议: 向指纹机发送0x01,则回送checkin_num0 checkin_num 1向指纹机发送0x02,则回送checkin_num0个考勤数据。实现方式是在串口中断中收到1则发送checkin_num,若收到2,则置give_me为1,在通信模式下的while(1)中检测到此标志后,将数据发送,用already_giv

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论