任务

设计要求和参数
1. 设计要求:
(1) 能够根据RFID和指纹对学生的身份进行识别(识别成功显示所在院系、班级、专业、学号等);
(2) 能够利用矩阵按键输入管理员密码后添加新的指纹认证信息。
(3) 具有学籍状况查询功能:考勤查询、进出校园记录、操行记录及安全追踪等功能。
(4) 能利用按键查询信息,并在LCD上显示。
2. 设计参数:
(1) RFID:工作频率:13.56MHz;通信速率:106Kbps;读写距离:≤10cm;传递数据时间小于200ms;
(2) 指纹模块:工作电压:3.3V;分辨率:500dpi;录入时间:0.1S;可录入指纹数:300;
(3) 系统电源5V,具备电压转换功能,提供3.3V电源输出接口。
(4) 主控单片机:工作电压5V,采用STC(宏晶)公司新型的8051单片机。

设计架构:

本系统的设计包括了研发系统的软件和硬件设计两方面。其中系统硬件主要包括:STC8A单片机、无线射频频RFID识别模块、指纹识别模块、薄膜矩阵按键模块、LCD液晶显示模块、时钟模块、电源模块和报警模块,通过对各模块的学习设计和连接最终开发出硬件电路总系统。软件设计则是首先对每一个注册的RFID卡进行编码和匹配学生信息,当有RFID卡靠近系统硬件的RFID识别模块时,系统会读取ID卡号,并且对传递过来的ID序列号与原有注册并存储的ID序列号进行对比来核对卡号是否正确,如果ID信息匹配成功,则LCD显示屏就会出现该ID对应的学生的信息,同时绿灯也会变亮,门禁打开,但是一旦ID匹配失败,则LED就会变成红灯亮,同时蜂鸣器也会发出错误警报声音,若识别错误连续次数达到3次,则会导致系统锁定1分钟(为了便于演示,本系统暂设为15S),锁定期间蜂鸣器一直响,红灯一直亮,用于警示识别错误无效ID人员来避免该人员闯入。指纹识别和提示、报警灯于RFID流程基本一致。

校园门禁软件总体架构 校园智能门禁系统设计_指纹识别

实物

实物整体

校园门禁软件总体架构 校园智能门禁系统设计_RFID识别_02

系统上电界面逻辑

本系统设置了多个界面逻辑,有正常检测界面、管理员身份登录界面、指纹添加界面、指纹删除界面、历史信息查看界面等。系统的界面设置有一定的直观性、提示性能够方便使用者快速知道当前界面是干什么的且能够根据提示知道如何操作等。本系统上电后会首先显示设计人的信息,3秒延时后进行指纹模块的检测,当检测到指纹模块后进入到指纹模块的检测,当检测指纹模块正常时会显示加载成功并进入到EEPROM检测读写界面,因本系统需要存储学生的门禁出入信息,故而进行新旧单片机的判断,如果是系统首次上电即单片机是新的则将EEPROM进行格式化,并设置标志位写入来方便下次判定单片机是经过存储的。在下次上电的时候以便能够读出存储的学生信息。界面显示2秒后则进入到正常的检测界面。

校园门禁软件总体架构 校园智能门禁系统设计_RFID识别_03


校园门禁软件总体架构 校园智能门禁系统设计_指纹识别_04


校园门禁软件总体架构 校园智能门禁系统设计_RFID识别_05

系统管理界面逻辑

本系统在正常检测界面可以通过薄膜矩阵键盘的A键进入后台管理界面,在此界面下需要输入管理员密码,默认密码输入下是 * 号进行隐藏,也可以选择按下薄膜矩阵键盘的 * 键选择明码。当输入管理员密码正确后可以进入到后台进行指纹的添加、删除操作,也可以进入查阅界面进入历史数据查询等。

校园门禁软件总体架构 校园智能门禁系统设计_stm32_06


校园门禁软件总体架构 校园智能门禁系统设计_校园门禁软件总体架构_07

指纹功能界面逻辑

添加指纹:

当系统输入密码成功进入管理界面后台后,可以选择按下薄膜按键的A键进入系统的指纹更新界面,在此界面下可以选择添加指纹、删除指纹、搜索指纹、返回上级选项。本次通过按下薄膜按键的A键进入添加指纹选型,通过屏幕提示按下薄膜按键相应的按键进行ID号的更改、确认、返回等操作,在确定ID号后进入到添加指纹界面进行指纹的录入,经过两次采集手指图像进行存储。添加指纹的功能界面设计如图5.6所示,实物调试如图5.7所示。

校园门禁软件总体架构 校园智能门禁系统设计_51单片机_08


校园门禁软件总体架构 校园智能门禁系统设计_51单片机_09

删除指纹

本系统通过按键的选择进入到指纹删除界面,在此功能下,通过确定可以选择删除存储内的所有指纹信息,删除完成后自动返回到指纹功能总界面。本次设计的界面和实物调试如图5.8所示。

校园门禁软件总体架构 校园智能门禁系统设计_指纹识别_10

搜索指纹界面

本系统当添加完指纹后,可以通过搜索指纹功能进行测试,来确保指纹添加成功,当在搜索指纹时,如果搜索成功则返回指纹ID,识别则LCD显示对应的提示。如图5.9所示为指纹搜索界面设计,图5.10所示为实物调试界面。

校园门禁软件总体架构 校园智能门禁系统设计_校园门禁软件总体架构_11


校园门禁软件总体架构 校园智能门禁系统设计_stm32_12


校园门禁软件总体架构 校园智能门禁系统设计_RFID识别_13

历史数据查询界面逻辑

本系统能够查询历史的门禁出入记录。在管理界面通过选择B键进入查阅记录界面,在该界面下通过B、C键可更改查阅的人员,按下D键进入对应人员的查阅界面,如查阅李四的进出记录。在查阅界面下通过B、C键进行查阅记录的翻页操作,总共记录了9页历史出入信息,每页记录两次。本系统的查阅界面设计如图5.11所示,实物调试效果如图5.12所示。

校园门禁软件总体架构 校园智能门禁系统设计_校园门禁软件总体架构_14


校园门禁软件总体架构 校园智能门禁系统设计_校园门禁软件总体架构_15

指纹和RFID门禁系统解锁测试

如图5.13所示为正常检测界面,在此界面下,可以通过指纹或者RFID进行门禁系统的解锁,如果指纹检测ID匹配学生信息成功或者RFID读到学生ID卡且该卡存在于数据库中则系统会进行门禁解锁,同时LCD屏幕显示解锁学生的信息,5秒后系统LCD屏幕返回正常检测界面信息显示。若解锁失败则系统会提示相应信息,若超过3次解锁失败则系统进入长报警界面。如图5.14所示为指纹解锁成功,如图5.15为RFID解锁成功,如图5.16所示为指纹解锁失败,如图5.17为RFID解锁失败,如图5.18为多次解锁失败系统锁定。

校园门禁软件总体架构 校园智能门禁系统设计_51单片机_16


校园门禁软件总体架构 校园智能门禁系统设计_stm32_17


校园门禁软件总体架构 校园智能门禁系统设计_51单片机_18


校园门禁软件总体架构 校园智能门禁系统设计_stm32_19


校园门禁软件总体架构 校园智能门禁系统设计_指纹识别_20


校园门禁软件总体架构 校园智能门禁系统设计_指纹识别_21

本系统的各个界面的设计相对复杂了许多,本系统要设计多个界面,需要在各个界面之间相互跳转,且在各个界面下矩阵按键的各个键都有不同的使用功能,故本次着重介绍了实物调试过程中进行界面的设计、划分和跳转。

原理图

校园门禁软件总体架构 校园智能门禁系统设计_51单片机_22

资源使用

校园门禁软件总体架构 校园智能门禁系统设计_stm32_23

源程序

系统设计流程

校园门禁软件总体架构 校园智能门禁系统设计_指纹识别_24

主函数

/*******************************************************************************

\* 文件名称:基于51单片机的学生管理门禁系统设计

\* 实验目的:1.

\* 2.

\* 程序说明:完整程序Q:277 227 2579;@: itworkstation@ hotmail.com

\* 日期版本:本项目分享关键细节,熟悉使用单片机的可做参考代码。完整讲解+源代码工程可联系获取,可定制。

*******************************************************************************/
#include "config.h"

#include "led.h"            //调用LED灯头文件
#include "beep.h"
#include "relay.h"
#include "12864_serial.h"  
#include "keyBoard.h"
#include "RC522.h"
#include "Uart4_Timer4.h"
#include "FPM10A.h"
#include "ds1302.h"
#include "eeprom.h"
#include "Uart1_Timer2.h"
/*************************************************************************
                               主函数
**************************************************************************/
#define DISMAXBUFF 30
char disBuff[DISMAXBUFF];

#define EEPROM_STORE 0x0400   //STC下载器设置EEPROM最少为2K !!!!! 注意的是:新程序下载后EEPROM是被下载器清空了的····下载器选项:下次下载用户程序时擦除用户EEPEOM区

unsigned char xdata g_ucTempbuf[20];
uchar xdata Xuhao_Panduan[4];
uchar code Xuhao_SQL[]={
0x14,0X59,0X0A,0X6F,	// 蓝色卡1
0xDB,0X67,0X89,0X1B,	// 蓝色卡2
0XF3,0XEA,0X67,0X00,   // 白色卡1
//0XC3,0XE0,0X19,0X19,   // 白色卡2	
};
#define NONEClient 0
#define NONECARD   0xff
bit flag_rcReady= FALSE;
uchar Client=NONEClient;	
uchar Xuhao_Check(void);
uchar rc522Read(void);

void Timer0Init(void);		//5毫秒@22.1184MHz

uchar date1302[]={22,4,19,17,26,30};  //初始化时间

uint IdNumber=NONESTORE;   //指纹ID

typedef struct{
	uchar dateStore_Stu[18][6];
}STRUCTSTU;
STRUCTSTU stuTimStore[4];
char page_count=0;

char code StuName[4][15]={
"  张三",
"  李四",
"  王五",
"  赵六",	
};
char code StuNumber[4][15]={
"201802010001",
"201802010002",
"201802010003",
"201802010004",	
};
typedef enum{
	NormalMenu,
	InputMenu,
	ChooseMenu,
    ZhiWenMenu,
	ViewHistoryMenu,
	StuHistoryMenu,
	UnlockErrorMenu,
}ENUMDISMENU;
ENUMDISMENU disMenu = NormalMenu;
char ViewId =0;
char ViewPage=0;

void KeyPress_InputMenu(uchar keycode);
const unsigned char muntochar[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};//液晶数字转字符
unsigned char in_password_mun = 255;		//声明1个变量,用来记录密码输入位数.
unsigned char password_bj[6] = {20,20,20,20,20,20};	//密码比较,密码输入存储
unsigned char code password[6] = {1,2,3,4,5,6};	//定义初始密码.
bit password_dis = TRUE;					//声明1个变量,用来记录是显示密码还是隐藏密码. true:隐藏状态,显示*
bit password_ok_open = FALSE;				//定义1个变量,用来记录密码输入正确标志,0表示不成功.1表示成功.
unsigned char password_err_mun = 0;	//声明1个变量用来记录密码输入错误次数.
#define TIMEERROR 15
int TimeErrorInput = TIMEERROR;
bit flag_inputError = 0;
bit flagTimer0_1S= FALSE;
uint timer0Count=0;
bit flag_ReceiveCard = FALSE;
int unlockErrorTimes=0,errorTimesCount = 15;
#define UNLOCKERRORTIMES_MAX  3

extern uchar Flag_Receive_Ok;
extern uchar  Buffer_UART1[Buffer_UART1Length];
void main (void)
{	
	uchar keyNum = KEY_NoTOUCH;	
	uchar is_FingerTouched=0;
	uchar i=0,j=0,k=0;
	uint p=0;
	
	LED_Init();		//LED灯函数初始化
	LED_R = LED_STATE(OFF);
	LED_G = LED_STATE(OFF);	
	
	LCD_Init();
	
	UART4_Init();
	delay_ms(1000);
	Device_Check();		   	//校对指纹模块是否接入正确,液晶做出相应的提示
	
	write_string(3,0,"    存储模块    ");
	write_string(4,0,"正在加载        ");	
	SendCMD(0x98+4);
	for(i=0;i<8;i++)						   //进度条式更新,看起来美观
	{
		SendDat(42);	                       //42对应ASIC码的 *
		delay_ms(300);						           //控制进度条速度
	}	
	for(k=0;k<4;k++)
	{
		for(i=0;i<18;i++)  //清空数组
		{
			for(j=0;j<6;j++)
			{
				stuTimStore[k].dateStore_Stu[i][j]=0;
			}
		}
	}	
		// 设计标定初值,用于新旧芯片判断  --------------------------------  
	if(IapRead(EEROM_USEADDR) == EEROM_CHECK_VALUE)  // 不是新单片机,内部存有数据
	{
		EA = 0;		//禁止中断
		p = 0;
		for(k=0;k<4;k++)
		{
			for(i=0;i<18;i++)  //清空数组
			{
				for(j=0;j<6;j++)
				{
					stuTimStore[k].dateStore_Stu[i][j] = IapRead(EEPROM_STORE+p);				
					p++;
					delay_ms(5);  //还是需要延时的!
				}
			}
		}	
		EA = 1;		//重新允许中断
		LED_G = LED_STATE(ON);
		write_string(4,0,"    EEPROM读出!");
	}
	else
	{		
		EA = 0;		//禁止中断
		IapErase(EEROM_USEADDR); //擦除扇区内容才能重新写入
		IapProgram(EEROM_USEADDR, EEROM_CHECK_VALUE);
		
		IapErase(EEPROM_STORE); //擦除扇区内容才能重新写入
		p = 0;
		for(k=0;k<4;k++)
		{
			for(i=0;i<18;i++)  
			{
				for(j=0;j<6;j++)
				{
					IapProgram(EEPROM_STORE+p,stuTimStore[k].dateStore_Stu[i][j]);  //+p!!!必须为uint型,不然地址不符合!!
					p++;
					delay_ms(5);    //还是需要延时的!
				}
			}
		}	
		EA = 1;		//重新允许中断
		LED_R = LED_STATE(ON);
		write_string(4,0,"    EEPROM写入!");
	}
	delay_ms(2000);
	
	BEEP_Init();
	BEEP_Pin = BEEP_STATE(OFF);
	RELAY_Init();
	RELAY_Pin = RELAY_STATE(OFF);
	
	LED_R = LED_STATE(OFF);
	LED_G = LED_STATE(OFF);	
	
	Init_Ds1302(date1302);
	SendCMD(0x01);//清除显示
	write_string(1,0," 2022 - 04 - 19 ");
	write_string(2,0,"   12 : 02 : 30 ");
	write_string(3,0,"请使用RFID卡验证");
	write_string(4,0,"  或使用指纹验证");
	Read_NowTime_Ds1302(date1302);   // 时钟不能时刻读取,读取速度太快,影响其它进程,尤其是RFID!!!
	memset(disBuff,0,DISMAXBUFF);
	sprintf(disBuff," 20%02d - %02d - %02d ",(int)date1302[0],(int)date1302[1],(int)date1302[2]);   // 51程序中,格式化输入个数不能大于3,否则就出错,很无语!
	write_string(1,0,disBuff);	
	memset(disBuff,0,DISMAXBUFF);
	sprintf(disBuff,"   %02d : %02d : %02d ",(int)date1302[3],(int)date1302[4],(int)date1302[5]);
	write_string(2,0,disBuff);
	
	KEYBOARD_Init();
	
	InitRC522();
	
	Timer0Init();
	
	UART1_Init();
	UART1_SendStr("Please enter the changed time format, such as $20220423162537@\r\n");
	while (1)       //主循环
	{	
		if(disMenu == NormalMenu)
		{
			for(i = 0;i < 6;i++)								//循环6次
				password_bj[i] = 20;							//置密码比较数组里的数还原到20.
			in_password_mun = 255;								//记录密码输入位数还原.
			
			keyNum = Getkeyboard();		   //按键判断函数
			if(keyNum != KEY_NoTOUCH)
			{
				Buzz_Times(1);	
				if(keyNum==10)
				{
					disMenu = InputMenu;
					SendCMD(0x01);//清除显示				
					write_string(1,0,"请输入管理员密码");
					write_string(2,0,"密码:          ");				
					write_string(3,0,"*:明码    #:删除");
					write_string(4,0,"A:返回    D:确认");
					continue;  //从while重新开始,不再运行下面程序
				}
			}		
			if(flagTimer0_1S == TRUE)
			{
				flagTimer0_1S = FALSE;
				
				if(Flag_Receive_Ok==1)
				{
					Flag_Receive_Ok=0;
					date1302[0] = (Buffer_UART1[3]-0x30)*10+(Buffer_UART1[4]-0x30);//$20220423162537@  年月日时分秒
					date1302[1] = (Buffer_UART1[5]-0x30)*10+(Buffer_UART1[6]-0x30);
					date1302[2] = (Buffer_UART1[7]-0x30)*10+(Buffer_UART1[8]-0x30);
					date1302[3] = (Buffer_UART1[9]-0x30)*10+(Buffer_UART1[10]-0x30);
					date1302[4] = (Buffer_UART1[11]-0x30)*10+(Buffer_UART1[12]-0x30);
					date1302[5] = (Buffer_UART1[13]-0x30)*10+(Buffer_UART1[14]-0x30);
					Set_Ds1302(date1302);
					
					UART1_SendStr("Time changed successfully\r\n");
				}
				else if(Flag_Receive_Ok == 2)
				{
					Flag_Receive_Ok=0;
					UART1_SendStr("Time format setting error, such as $20220423162537@\r\n");
				}
				Read_NowTime_Ds1302(date1302);   // 时钟不能时刻读取,读取速度太快,影响其它进程,尤其是RFID!!!
				
				memset(disBuff,0,DISMAXBUFF);
				sprintf(disBuff," 20%02d - %02d - %02d ",(int)date1302[0],(int)date1302[1],(int)date1302[2]);   // 51程序中,格式化输入个数不能大于3,否则就出错,很无语!
				write_string(1,0,disBuff);	
				memset(disBuff,0,DISMAXBUFF);
				sprintf(disBuff,"   %02d : %02d : %02d ",(int)date1302[3],(int)date1302[4],(int)date1302[5]);
				write_string(2,0,disBuff);	
			}
						
			if(Finger_Touch)  //检测到有手指按下,也跳转到指纹检测
			{
				is_FingerTouched = FPM10A_Find_Fingerprint_Touch(&IdNumber);		
				if(is_FingerTouched == 1) //有指纹按下,则屏幕内容变化,需恢复屏幕信息
				{
					if(IdNumber != NONESTORE && IdNumber<4)  //0-3
					{
						unlockErrorTimes = 0; 
						
						LED_G = LED_STATE(ON);
						RELAY_Pin = RELAY_STATE(ON);	
						LED_R = LED_STATE(OFF);
						BEEP_Pin = BEEP_STATE(OFF);
												
						SendCMD(0x01);//清除显示					
						memset(disBuff,0,DISMAXBUFF);
						sprintf(disBuff,"姓名:    %s",StuName[IdNumber]);
						write_string(1,0,disBuff);	
						memset(disBuff,0,DISMAXBUFF);
						sprintf(disBuff,"学号%s",StuNumber[IdNumber]);
						write_string(2,0,disBuff);	
						write_string(3,0,"电气工程及自动化");
						write_string(4,0,"电气与电子工程系");
						for(i=17;i>0;i--)  //数组移动,记录时间数组内后移
						{
							for(j=0;j<6;j++)
							{
								stuTimStore[IdNumber].dateStore_Stu[i][j]=stuTimStore[IdNumber].dateStore_Stu[i-1][j];
							}
						}
						for(j=0;j<6;j++)  //数组0位置存储最近一次记录时间
						{
							stuTimStore[IdNumber].dateStore_Stu[0][j]=date1302[j];
						}
						
						EA = 0;		//禁止中断
						IapErase(EEPROM_STORE); //擦除扇区内容才能重新写入
						p = 0;
						for(k=0;k<4;k++)
						{
							for(i=0;i<18;i++)  
							{
								for(j=0;j<6;j++)
								{
									IapProgram(EEPROM_STORE+p,stuTimStore[k].dateStore_Stu[i][j]);
									p++;
//									write_string(4,0,"    EEPROM写入!");
									delay_ms(2);
								}
							}
						}
						EA = 1;		//重新允许中断								
					}
					else
					{
						LED_R = LED_STATE(ON);
						RELAY_Pin = RELAY_STATE(OFF);	
						LED_G = LED_STATE(OFF);
						SendCMD(0x01);//清除显示
						write_string(2,0,"  指纹验证失败!");
						write_string(3,0,"    请重新尝试!");	
						
						Buzz_Times(5);	
						unlockErrorTimes ++;
						if(unlockErrorTimes >= UNLOCKERRORTIMES_MAX) //错误次数达到n次
						{
							unlockErrorTimes = 0;
							BEEP_Pin = BEEP_STATE(ON);
							LED_R = LED_STATE(ON);
							LED_G = LED_STATE(OFF);
							RELAY_Pin = RELAY_STATE(OFF);	
							
							disMenu = UnlockErrorMenu;	
							SendCMD(0x01);//清除显示
							write_string(2,0,"验证失败错误次数");
							write_string(3,0,"过多!请等待15秒");
							
							timer0Count=0;
							TR0 = 1;
							continue;
						}
					}
					delay_ms(5000);
					RELAY_Pin = RELAY_STATE(OFF);
					LED_G = LED_STATE(OFF);
					LED_R = LED_STATE(OFF);
					BEEP_Pin = BEEP_STATE(OFF);
					SendCMD(0x01);//清除显示
					write_string(1,0," 2021 - 12 - 27 ");
					write_string(2,0,"   12 : 02 : 30 ");
					write_string(3,0,"请使用RFID卡验证");
					write_string(4,0,"  或使用指纹验证");
				}				
			}	
			Client = rc522Read();   //指纹读取最好在所有检测的最后。!
			if(flag_ReceiveCard ==  TRUE)
			{
				flag_ReceiveCard = FALSE;
				if(Client != NONEClient)
				{
					unlockErrorTimes = 0;
					
					SendCMD(0x01);//清除显示
					RELAY_Pin = RELAY_STATE(ON);	
					LED_R = LED_STATE(OFF);
					BEEP_Pin = BEEP_STATE(OFF);
					LED_G = LED_STATE(ON);
					
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"姓名:    %s",StuName[Client-1]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"学号%s",StuNumber[Client-1]);
					write_string(2,0,disBuff);	
					write_string(3,0,"电气工程及自动化");
					write_string(4,0,"电气与电子工程系");		

					for(i=17;i>0;i--)  //数组移动,记录时间数组内后移
					{
						for(j=0;j<6;j++)
						{
							stuTimStore[Client-1].dateStore_Stu[i][j]=stuTimStore[Client-1].dateStore_Stu[i-1][j];
						}
					}
					for(j=0;j<6;j++)  //数组0位置存储最近一次记录时间
					{
						stuTimStore[Client-1].dateStore_Stu[0][j]=date1302[j];
					}
					
					EA = 0;		//禁止中断
					IapErase(EEPROM_STORE); //擦除扇区内容才能重新写入
					p = 0;
					for(k=0;k<4;k++)
					{
						for(i=0;i<18;i++)  
						{
							for(j=0;j<6;j++)
							{
								IapProgram(EEPROM_STORE+p,stuTimStore[k].dateStore_Stu[i][j]);
								p++;
//									write_string(4,0,"    EEPROM写入!");
								delay_ms(5);
							}
						}
					}
					EA = 1;		//重新允许中断
				}
				else
				{
					LED_R = LED_STATE(ON);
					RELAY_Pin = RELAY_STATE(OFF);	
					LED_G = LED_STATE(OFF);
					SendCMD(0x01);//清除显示
					write_string(2,0,"  RFID验证失败!");
					write_string(3,0,"    请重新尝试!");
					
					Buzz_Times(5);
					
					unlockErrorTimes ++;
					if(unlockErrorTimes >= UNLOCKERRORTIMES_MAX) //错误次数达到n次
					{
						unlockErrorTimes = 0;
						BEEP_Pin = BEEP_STATE(ON);
						LED_R = LED_STATE(ON);
						LED_G = LED_STATE(OFF);
						RELAY_Pin = RELAY_STATE(OFF);
						
						disMenu = UnlockErrorMenu;	
						SendCMD(0x01);//清除显示
						write_string(2,0,"验证失败错误次数");
						write_string(3,0,"过多!请等待15秒");
						
						timer0Count=0;
						TR0 = 1;
						continue;
					}
				}
				delay_ms(5000);
				RELAY_Pin = RELAY_STATE(OFF);
				LED_G = LED_STATE(OFF);
				LED_R = LED_STATE(OFF);
				BEEP_Pin = BEEP_STATE(OFF);
				SendCMD(0x01);//清除显示
				write_string(1,0," 2021 - 12 - 27 ");
				write_string(2,0,"   12 : 02 : 30 ");
				write_string(3,0,"请使用RFID卡验证");
				write_string(4,0,"  或使用指纹验证");
			}
		}
		else if(disMenu == UnlockErrorMenu)
		{
			if(flagTimer0_1S == TRUE)
			{
				flagTimer0_1S = FALSE;  //errorTimesCount = 15;
				if(errorTimesCount >0)
				{
					errorTimesCount --;
					SendCMD(0x88+6);
					SendDat(0x30+errorTimesCount/10%10);
					SendDat(0x30+errorTimesCount%10);
				}
				else
				{
					errorTimesCount = 15;
					BEEP_Pin = BEEP_STATE(OFF);
					LED_R = LED_STATE(OFF);
					LED_G = LED_STATE(OFF);
					RELAY_Pin = RELAY_STATE(OFF);	
					
					disMenu = NormalMenu;
					SendCMD(0x01);//清除显示
					write_string(1,0," 2021 - 12 - 27 ");
					write_string(2,0,"   12 : 02 : 30 ");
					write_string(3,0,"请使用RFID卡验证");
					write_string(4,0,"  或使用指纹验证");
					continue;
				}
			}
		}
        else if(disMenu == InputMenu)
        {
            keyNum = Getkeyboard();		   //按键判断函数 
            if(keyNum != KEY_NoTOUCH)
            {               
			   Buzz_Times(1);	
               if(keyNum < 10)	//如果输入的键值是数字键
               {
                    in_password_mun++;										//每输入一位密码,位数自增1.255+1=0
                    if(in_password_mun == 6)								//密码输入位数控制在6位
                        in_password_mun = 5;
                    password_bj[in_password_mun] = keyNum;					//把键值存入密码比较数组
                    if(password_dis == FALSE)									//如果密码是显示的.输入密码显示
                    {
                        SendCMD(0x90+3);						//液晶AC控制到第2行的第3位置.											
                        for(i = 0;i < 6;i++)								//循环检查6位密码,并且显示在屏上
                        {
                            if(password_bj[i] != 20)						//如果不是原始数值,说明有新的密码
                                SendDat(muntochar[password_bj[i]]);	//把新的密码显示在屏上
                        }
                    }
                    if(password_dis == TRUE)									//如果密码是隐藏的.就显示*号
                    {
                        SendCMD(0x90+3);						//液晶AC控制到第2行的第3位置.									
                        for(i = 0;i < 6;i++)								//循环检查6位密码,并且显示在屏上
                        {
                            if(password_bj[i] != 20)						//如果不是原始数值,说明有新的密码
                                SendDat('*');						//把输入的密码以*号显示出来.
                        }
                    }
                }
				else if(keyNum == 10)
				{
					LED_R = LED_STATE(OFF);	
					LED_G = LED_STATE(OFF);
					disMenu = NormalMenu;
					SendCMD(0x01);//清除显示
					write_string(1,0," 2021 - 12 - 27 ");
					write_string(2,0,"   12 : 02 : 30 ");
					write_string(3,0,"请使用RFID卡验证");
					write_string(4,0,"  或使用指纹验证");
				}
                else
                {
                    KeyPress_InputMenu(keyNum);
                } 
				while(flag_inputError == TRUE) //输入错误次数过多,等待15S
				{
                    if(flagTimer0_1S == TRUE)
					{
                        flagTimer0_1S = FALSE;                       
                        memset(disBuff,0,DISMAXBUFF);
                        sprintf(disBuff,"密码:稍后重试%02d",TimeErrorInput);
                        write_string(2,0,disBuff);	
                        TimeErrorInput --;
                        if(TimeErrorInput < 0)
                        {
                            flag_inputError = FALSE;
							TimeErrorInput = TIMEERROR;
							write_string(2,0,"密码:          ");//清空显示
							SendCMD(0x90+3);						//液晶AC控制到第2行的第3位置.
                        }
                    }                 
				}
            }            
			if(password_ok_open == TRUE)
			{
                password_ok_open = FALSE;
				disMenu = ChooseMenu;
                SendCMD(0x01);//清除显示				
                write_string(1,0,"密码验证成功!  ");
                write_string(2,0,"A:指纹更新      ");				
                write_string(3,0,"B:查阅记录      ");
                write_string(4,0,"C:手动    #:返回");
                continue;  //从while重新开始,不再运行下面程序
			}
        }
        else if(disMenu == ChooseMenu)
        {
			for(i = 0;i < 6;i++)								//循环6次
				password_bj[i] = 20;							//置密码比较数组里的数还原到20.
			in_password_mun = 255;								//记录密码输入位数还原.
			
            keyNum = Getkeyboard();		   //按键判断函数 
            if(keyNum != KEY_NoTOUCH)
            {
				Buzz_Times(1);	
                if(keyNum == 10)   //A:指纹更新
                {
					disMenu = ZhiWenMenu;
                    SendCMD(0x01);//清除显示				
					write_string(1,0,"A:添加指纹      ");
					write_string(2,0,"B:删除指纹      ");
					write_string(3,0,"C:搜索指纹      ");
					write_string(4,0,"#:返回上级      ");
                    continue;  //从while重新开始,不再运行下面程序
                }
                else if(keyNum == 11)  //B:查阅历史
                {
					disMenu = ViewHistoryMenu;
                    SendCMD(0x01);//清除显示				
					write_string(1,0,"  进出记录查阅  ");
					write_string(2,0,"姓名:          ");
					write_string(3,0,"B:加        C:减");
					write_string(4,0,"#:返回    D:确定");
					continue;  //从while重新开始,不再运行下面程序
                }
                else if(keyNum == 12)  //C:手动
                {
                    /*---------功能(手动开启/关闭密码锁)-----------*/ 
                    if(RELAY_Pin == RELAY_STATE(ON))						//如果手动关闭锁按键被按下,并且密码锁是打开的.
                        RELAY_Pin = RELAY_STATE(OFF);										//密码锁关闭.
                    else    
                        RELAY_Pin = RELAY_STATE(ON);   
                } 
                else if(keyNum == 15)  //#:返回
                {
					disMenu = InputMenu;
					LED_G = LED_STATE(OFF);
                    SendCMD(0x01);//清除显示				
					write_string(1,0,"请输入管理员密码");
					write_string(2,0,"密码:          ");				
					write_string(3,0,"*:明码    #:删除");
					write_string(4,0,"A:返回    D:确认");
                    continue;  //从while重新开始,不再运行下面程序
                }
            }
        }
		else if(disMenu == ViewHistoryMenu)
		{
			keyNum = Getkeyboard();		   //按键判断函数 
            if(keyNum != KEY_NoTOUCH)
			{
				Buzz_Times(1);	
				if(keyNum == 11)  //加
				{
					if(ViewId<3)
					{
						ViewId++;
					}
					else
					{
						ViewId= 3;
					}
				}
				else if(keyNum == 12)  //减
				{
					if(ViewId>0)
					{
						ViewId--;
					}
					else
					{
						ViewId = 0;
					}
				}
				else if(keyNum == 15)  //#:返回
                {
					disMenu = ChooseMenu;
					SendCMD(0x01);//清除显示				
					write_string(1,0,"密码验证成功!  ");
					write_string(2,0,"A:指纹更新      ");				
					write_string(3,0,"B:查阅记录      ");
					write_string(4,0,"C:手动    #:返回");
					continue;  //从while重新开始,不再运行下面程序
                }
				else if(keyNum == 13)  //D:确定
                {
					disMenu = StuHistoryMenu;
					SendCMD(0x01);//清除显示				
					write_string(1,0,"姓名:          ");
					write_string(2,0,"学号            ");				
					write_string(3,0,"B:加        C:减");
					write_string(4,0,"#:返回     No: 0");
					ViewPage = 0;
					continue;  //从while重新开始,不再运行下面程序
                }
			}
			memset(disBuff,0,DISMAXBUFF);
			sprintf(disBuff,"姓名:    %s",StuName[ViewId]);   // char ViewId =0;
			write_string(2,0,disBuff);	
		}
		else if(disMenu == StuHistoryMenu)
		{
			switch(ViewPage)
			{
				case 0:
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"姓名:    %s",StuName[ViewId]);   // char ViewId =0;
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"学号%s",StuNumber[ViewId]);
					write_string(2,0,disBuff);				
				    write_string(3,0,"B:加        C:减");
					write_string(4,0,"#:返回      No:");
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 			
					break;
				case 1:			
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count][2]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d        ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count][5]);
					write_string(2,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][2]);
					write_string(3,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d    ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][5]);
					write_string(4,0,disBuff);	
					
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 				
					break;
				case 2:
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count][2]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d        ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count][5]);
					write_string(2,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][2]);
					write_string(3,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d    ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][5]);
					write_string(4,0,disBuff);	
					
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 				
					break;
				case 3:
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count][2]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d        ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count][5]);
					write_string(2,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][2]);
					write_string(3,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d    ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][5]);
					write_string(4,0,disBuff);	
					
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 			
					break;
				case 4:
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count][2]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d        ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count][5]);
					write_string(2,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][2]);
					write_string(3,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d    ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][5]);
					write_string(4,0,disBuff);	
					
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 			
					break;
				case 5:
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count][2]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d        ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count][5]);
					write_string(2,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][2]);
					write_string(3,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d    ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][5]);
					write_string(4,0,disBuff);	
					
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 		
					break;
				case 6:
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count][2]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d        ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count][5]);
					write_string(2,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][2]);
					write_string(3,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d    ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][5]);
					write_string(4,0,disBuff);	
					
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 				
					break;
				case 7:
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count][2]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d        ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count][5]);
					write_string(2,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][2]);
					write_string(3,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d    ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][5]);
					write_string(4,0,disBuff);	
					
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 		
					break;
				case 8:
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count][2]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d        ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count][5]);
					write_string(2,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][2]);
					write_string(3,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d    ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][5]);
					write_string(4,0,disBuff);	
					
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 	
					break;
				case 9:
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count][2]);
					write_string(1,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d        ",(int)stuTimStore[ViewId].dateStore_Stu[page_count][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count][5]);
					write_string(2,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff," 20%02d - %02d - %02d ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][0],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][1],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][2]);
					write_string(3,0,disBuff);	
					memset(disBuff,0,DISMAXBUFF);
					sprintf(disBuff,"%02d:%02d:%02d    ",(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][3],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][4],(int)stuTimStore[ViewId].dateStore_Stu[page_count+1][5]);
					write_string(4,0,disBuff);	
					
					SendCMD(0x98+6); //如:第四行第6列显示No:1
					SendDat('N');
					SendDat('o');
					SendDat(':');
					SendDat(ViewPage+0x30); 			
					break;
				default:break;
			}
				
			keyNum = Getkeyboard();		   //按键判断函数 
            if(keyNum != KEY_NoTOUCH)
			{
				Buzz_Times(1);	
				if(keyNum == 11)  //加
				{
					ViewPage++;
					if(ViewPage>9)
					{
						ViewPage=9;
					}
					page_count+=2;
					if(page_count>16)
					{
						page_count = 16;
					}
					if(ViewPage == 1)
					{
						page_count = 0;
					}
					SendCMD(0x01);//清除显示		
				}
				else if(keyNum == 12)  //减
				{
					ViewPage --;
					if(ViewPage<0)
						ViewPage=0;		
					page_count-=2;
					if(page_count<0)
					{
						page_count = 0;
					}
					SendCMD(0x01);//清除显示		
				}
				else if(keyNum == 15)  //#:返回
                {
					disMenu = ViewHistoryMenu;
                    SendCMD(0x01);//清除显示				
					write_string(1,0,"  进出记录查阅  ");
					write_string(2,0,"姓名:          ");
					write_string(3,0,"B:加        C:减");
					write_string(4,0,"#:返回    D:确定");
					continue;  //从while重新开始,不再运行下面程序
                }
			}
		}
		else if(disMenu == ZhiWenMenu)
		{
			keyNum = Getkeyboard();		   //按键判断函数
			if(keyNum != KEY_NoTOUCH)
			{	
				Buzz_Times(1);	
				if(keyNum==10)
				{				
					FPM10A_Add_Fingerprint();
					SendCMD(0x01);//清除显示				
					write_string(1,0,"A:添加指纹      ");
					write_string(2,0,"B:删除指纹      ");
					write_string(3,0,"C:搜索指纹      ");
					write_string(4,0,"#:返回上级      ");
				}
				if(keyNum==11)
				{
					FPM10A_Delete_All_Fingerprint();
					SendCMD(0x01);//清除显示				
					write_string(1,0,"A:添加指纹      ");
					write_string(2,0,"B:删除指纹      ");
					write_string(3,0,"C:搜索指纹      ");
					write_string(4,0,"#:返回上级      ");
				}
				if(keyNum==12)
				{
					FPM10A_Find_Fingerprint();	
					SendCMD(0x01);//清除显示				
					write_string(1,0,"A:添加指纹      ");
					write_string(2,0,"B:删除指纹      ");
					write_string(3,0,"C:搜索指纹      ");
					write_string(4,0,"#:返回上级      ");
				}
				if(keyNum==15)
				{
					disMenu = ChooseMenu;
					write_string(1,0,"密码验证成功!  ");
                    write_string(2,0,"A:指纹更新      ");				
                    write_string(3,0,"B:查阅历史      ");
                    write_string(4,0,"C:手动    #:返回");
                    continue;  //从while重新开始,不再运行下面程序
				}
			}
		}
	}
}
void Timer0Init(void)		//5毫秒@11.0592MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x00;		//设置定时初值
	TH0 = 0xEE;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;
	EA  = 1;
}
static void Timer0_irt() interrupt 1
{
	timer0Count ++;
	if(timer0Count >= 200)
	{
		timer0Count =0;
        flagTimer0_1S = TRUE;
	}
}
#define RCREADTIME 1000
uchar rc522Read(void)
{
	unsigned char i=0;
	uchar rcID = NONEClient; //先清空,再判断卡有没有匹配到存储的人
	static uint rcCountTime=0;
	static char status=0;
	if(rcCountTime == 0)
		status = PcdRequest(PICC_REQALL, g_ucTempbuf);//寻卡
	rcCountTime ++;
	if(rcCountTime == RCREADTIME)
	{
		rcCountTime =0;
		for(i=0;i<4;i++)		 //卡序列号
		{
			Xuhao_Panduan[i]=0;
		}
		if(status == MI_OK )	//若得到卡,则进行判断卡序号
		{
			status = PcdAnticoll(g_ucTempbuf);//防冲撞
			if (status != MI_OK)
			{        }
			for(i=0;i<4;i++)		 //卡序列号  显示
			{
				Xuhao_Panduan[i]=g_ucTempbuf[i];
//				write_BCD(2,4+i,Xuhao_Panduan[i]);		
			}
			flag_ReceiveCard = TRUE;
			delay_ms(800);  //防抖动--稳定下来后再判断
			rcID=Xuhao_Check();			
		}
		else	 //如果没有得到卡,则重启PCD
		{    
			PcdReset();
			PcdAntennaOff(); 
			delay_ms(2);
			PcdAntennaOn();		
		}
	}
	return rcID;
}	
uchar Xuhao_Check()
{
	uchar i=0;
	for(i=0;i<4;i++)
	{
		if(Xuhao_Panduan[i]==Xuhao_SQL[i])
			continue;
		else
			break;
	}
	if(i==4)
	{
		return 1; //识别出为客户1 :绿色卡1	
	}	
	for(i=0;i<4;i++)
	{
		if(Xuhao_Panduan[i]==Xuhao_SQL[i+4])
			continue;
		else
			break;
	}
	if(i==4)
	{
		return 2; //识别出为客户2 :绿色卡2	
	}
	for(i=0;i<4;i++)
	{
		if(Xuhao_Panduan[i]==Xuhao_SQL[i+8])
			continue;
		else
			break;
	}
	if(i==4)
	{
		return 3; //识别出为客户3 :白色卡1	
	}
	for(i=0;i<4;i++)
	{
		if(Xuhao_Panduan[i]==Xuhao_SQL[i+12])
			continue;
		else
			break;
	}
	if(i==4)
	{
		return 4; //识别出为客户4 :白色卡2	
	}
	else
	{
		return NONEClient; //不在数据库内
	}
}
//按键响应程序,参数是键值
//返回键值:
//         1    2    3    10	   //10: 
//         4    5    6    11	   //11: 修改密码
//         7    8    9    12	   //12: 手动关闭锁 
//         14   0    15   13	   //14:显示/隐藏输入的密码    15:删除    13:确认
void KeyPress_InputMenu(uchar keycode)
{
	uchar i=0;
	switch (keycode)
	{
		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:	
            break;
        case 15:  	 	/*---------功能(删除1位密码)-----------*/
			if(in_password_mun != 255)
			{
				password_bj[in_password_mun] = 20;
                if(in_password_mun%2 != 0)  //偶数个,因序号从0开始:0,1,2,3,4,5
                {
                    SendCMD(0x90+3+in_password_mun/2);
                    if(password_dis == FALSE)
					{
						SendDat(muntochar[password_bj[in_password_mun-1]]);
					}	
                    else{
						SendDat('*');
					}                      
                    SendDat(' ');
                }
                else{
                    SendCMD(0x90+3+in_password_mun/2);
                    SendDat(' ');
                }
				if(in_password_mun > 0)        //0-1 = 255
					in_password_mun--;
				else
					in_password_mun = 255;									//记录密码输入位数还原.255+1=0
			}
			break;    
        case 13:	  /*---------功能(确定密码是否正确)-----------*/
				for(i = 0;i < 6;i++)										//先比较输入密码
				{
					if(password[i] != password_bj[i])						//如果密码不匹配.
						break;												//跳出for循环.								
				}
				if(i == 6)												//如果循环6次没有跳出for循环,密码输入正确.
				{
					LED_R = LED_STATE(OFF);	
					LED_G = LED_STATE(ON);
					password_ok_open = TRUE;								//密码正确致1,表示输入成功.
					password_err_mun = 0;								//密码错误次数清0.					
				}
				else
				{
					write_string(2,0,"密码:输入错误!");//清空显示
					LED_R = LED_STATE(ON);	
					LED_G = LED_STATE(OFF);										
					delay_ms(2000);
					LED_R = LED_STATE(OFF);		
					
					write_string(2,0,"密码:          ");//清空显示
					SendCMD(0x90+3);						//液晶AC控制到第2行的第3位置.
					
					password_err_mun ++;
					if(password_err_mun >= 3)
					{
						password_err_mun = 0;
						flag_inputError = TRUE;
					}
				}
				for(i = 0;i < 6;i++)								//循环6次
					password_bj[i] = 20;							//置密码比较数组里的数还原到20.
				in_password_mun = 255;								//记录密码输入位数还原.
            break;    
        case 14:          /*---------功能(显示输入密码)-----------*/	   
			password_dis = !password_dis;	
			if(password_dis == FALSE)
			{
				SendCMD(0x90+3);						//液晶AC控制到第2行的第3位置.									
				for(i = 0;i < 6;i++)
				{
					if(password_bj[i] != 20)
						SendDat(muntochar[password_bj[i]]);
				}
			}
			else
			{
				SendCMD(0x90+3);						//液晶AC控制到第2行的第3位置.							
				for(i = 0;i < 6;i++)
				{
					if(password_bj[i] != 20)
						SendDat('*');
				}
			}				
			break;
        default:break;    
    }
}
/*******************************************************************/

指纹识别模块

#ifndef __FPMXX_H__
#define __FPMXX_H__

#include "config.h"

sbit  Finger_Touch=P0^0;

#include "Uart4_Timer4.h"
#define Uart_Send_Byte            UART4_Send_Byte
#define Uart_Receive_Byte         UART4_Receive_Byte  

extern unsigned char FPM10A_RECEICE_BUFFER[32];

void FPM10A_Cmd_Get_Img(void);
void FPM10A_Cmd_Check(void);
void Device_Check(void);
void FPM10A_Receive_Data(unsigned char ucLength);
void FPM10A_Delete_All_Fingerprint();


#define NONESTORE 0xffff
uchar  FPM10A_Find_Fingerprint_Touch(uint *IdNumber);


void FPM10A_Find_Fingerprint();
void FPM10A_Cmd_Search_Finger(void);
void FPM10A_Add_Fingerprint();
void FPM10A_Cmd_Reg_Model();
void FPM10A_Cmd_Save_Finger( unsigned int storeID );
void FINGERPRINT_Cmd_Delete_All_Finger(void);



#endif
#include "FPM10A.h"
#include "beep.h"
#include "keyBoard.h"
#include "12864_serial.h"     

volatile unsigned char FPM10A_RECEICE_BUFFER[32];
int finger_id = 0;

#define DISMAXBUFF_ZW 30
char disBuff_ZW[DISMAXBUFF_ZW];  //LCD12864显示

//FINGERPRINT通信协议定义

code unsigned char FPM10A_Get_Device[10] ={0x01,0x00,0x07,0x13,0x00,0x00,0x00,0x00,0x00,0x1b};//口令验证
code unsigned char FPM10A_Pack_Head[6] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF};  //协议包头
code unsigned char FPM10A_Get_Img[6] = {0x01,0x00,0x03,0x01,0x00,0x05};    //获得指纹图像
code unsigned char FPM10A_Get_Templete_Count[6] ={0x01,0x00,0x03,0x1D,0x00,0x21 }; //获得模版总数
code unsigned char FPM10A_Search[11]={0x01,0x00,0x08,0x04,0x01,0x00,0x00,0x03,0xE7,0x00,0xF8}; //搜索指纹搜索范围0 - 999,使用BUFFER1中的特征码搜索
code unsigned char FPM10A_Search_0_9[11]={0x01,0x00,0x08,0x04,0x01,0x00,0x00,0x00,0x13,0x00,0x21}; //搜索0-9号指纹
code unsigned char FPM10A_Img_To_Buffer1[7]={0x01,0x00,0x04,0x02,0x01,0x00,0x08}; //将图像放入到BUFFER1
code unsigned char FPM10A_Img_To_Buffer2[7]={0x01,0x00,0x04,0x02,0x02,0x00,0x09}; //将图像放入到BUFFER2
code unsigned char FPM10A_Reg_Model[6]={0x01,0x00,0x03,0x05,0x00,0x09}; //将BUFFER1跟BUFFER2合成特征模版
code unsigned char FPM10A_Delete_All_Model[6]={0x01,0x00,0x03,0x0d,0x00,0x11};//删除指纹模块里所有的模版
volatile unsigned char  FPM10A_Save_Finger[9]={0x01,0x00,0x06,0x06,0x01,0x00,0x0B,0x00,0x19};//将BUFFER1中的特征码存放到指定的位置
//volatile:系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据
/*------------------ FINGERPRINT命令字 --------------------------*/
 //发送包头
void FPM10A_Cmd_Send_Pack_Head(void)
{
	int i;	
	for(i=0;i<6;i++) //包头
   {
     Uart_Send_Byte(FPM10A_Pack_Head[i]);   
    }		
}
//发送指令
void FPM10A_Cmd_Check(void)
{
	int i=0;
	FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头
	for(i=0;i<10;i++)
	 {		
		Uart_Send_Byte(FPM10A_Get_Device[i]);
	  }
}
//接收反馈数据缓冲
void FPM10A_Receive_Data(unsigned char ucLength)
{
  unsigned char i;

  for (i=0;i<ucLength;i++)
     FPM10A_RECEICE_BUFFER[i] = Uart_Receive_Byte();

}

//FINGERPRINT_获得指纹图像命令
void FPM10A_Cmd_Get_Img(void)
{
    unsigned char i;
    FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头
    for(i=0;i<6;i++) //发送命令 0x1d
	{
       Uart_Send_Byte(FPM10A_Get_Img[i]);
	}
}
//讲图像转换成特征码存放在Buffer1中
void FINGERPRINT_Cmd_Img_To_Buffer1(void)
{
 	unsigned char i;
	FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头      
   	for(i=0;i<7;i++)   //发送命令 将图像转换成 特征码 存放在 CHAR_buffer1
     {
      Uart_Send_Byte(FPM10A_Img_To_Buffer1[i]);
   	  }
}
//将图像转换成特征码存放在Buffer2中
void FINGERPRINT_Cmd_Img_To_Buffer2(void)
{
     unsigned char i;
     for(i=0;i<6;i++)    //发送包头
	 {
    	Uart_Send_Byte(FPM10A_Pack_Head[i]);   
   	 }
     for(i=0;i<7;i++)   //发送命令 将图像转换成 特征码 存放在 CHAR_buffer1
      {
      	Uart_Send_Byte(FPM10A_Img_To_Buffer2[i]);
   	  }
}
//搜索全部用户999枚
void FPM10A_Cmd_Search_Finger(void)
{
       unsigned char i;	   	    
			 FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头
       for(i=0;i<11;i++)
           {
    	      Uart_Send_Byte(FPM10A_Search[i]);   
   		   }
}

void FPM10A_Cmd_Reg_Model(void)
{
       unsigned char i;	   
	    
			 FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头

       for(i=0;i<6;i++)
           {
    	      Uart_Send_Byte(FPM10A_Reg_Model[i]);   
   		   }


}
//删除指纹模块里的所有指纹模版
void FINGERPRINT_Cmd_Delete_All_Model(void)
{
     unsigned char i;    
    for(i=0;i<6;i++) //包头
      Uart_Send_Byte(FPM10A_Pack_Head[i]);   
    for(i=0;i<6;i++) //命令合并指纹模版
	   {
      Uart_Send_Byte(FPM10A_Delete_All_Model[i]);   
		 }	
}
//保存指纹
void FPM10A_Cmd_Save_Finger( unsigned int storeID )
{
       unsigned long temp = 0;
		   unsigned char i;
       FPM10A_Save_Finger[5] =(storeID&0xFF00)>>8;
       FPM10A_Save_Finger[6] = (storeID&0x00FF);
		   for(i=0;i<7;i++)   //计算校验和
		   	   temp = temp + FPM10A_Save_Finger[i]; 
		   FPM10A_Save_Finger[7]=(temp & 0x00FF00) >> 8; //存放校验数据
		   FPM10A_Save_Finger[8]= temp & 0x0000FF;		   
       FPM10A_Cmd_Send_Pack_Head(); //发送通信协议包头	
       for(i=0;i<9;i++)  
      		Uart_Send_Byte(FPM10A_Save_Finger[i]);      //发送命令 将图像转换成 特征码 存放在 CHAR_buffer1
}
bit Flag_KEY_CANCEL=1;
//添加指纹
void FPM10A_Add_Fingerprint()
{
	unsigned char id_show[]={0,0,0};	
	unsigned char keycode;
	SendCMD(0x01);//清除显示LCD12864
	finger_id=0;  
	while(1)
	{
//	write_string(1,0,"   Add  finger  ");
//	write_string(2,0,"    ID is       ");
	write_string(1,0,"    添加指纹    ");	
	//指纹iD值显示处理  
//	 write_com(0xc0+10);
//	 write_date(finger_id/100+48);
//	 write_date(finger_id%100/10+48);
//	 write_date(finger_id%100%10+48);
	memset(disBuff_ZW,0,DISMAXBUFF_ZW);
	sprintf(disBuff_ZW,"指纹ID:%d    ",(int)finger_id);
	write_string(2,0,disBuff_ZW);	
	write_string(3,0,"B:ID加    C:ID减");	
	write_string(4,0,"D:确认    #:返回");				
	 keycode = Getkeyboard();	
		//有效键值0-15
		if (keycode<16)
		{
			Buzz_Times(1);
			if(keycode==15)	   //按返回键直接回到主菜单
			{
				break;
			}
			if(keycode==11)	  //按切换键指纹iD值加1
			{
				if(finger_id == 1000)
				{
					finger_id = 0;
				}
				else
					finger_id = finger_id + 1;
			}
			if(keycode==12)	  //按切换键指纹iD值减1
			{
				if(finger_id <= 0)
				{
					finger_id = 0;
				}
				else
					finger_id = finger_id - 1;
			}
			if(keycode==13)	   //按确认键开始录入指纹信息 
			{
				Flag_KEY_CANCEL=1;
//				write_string(1,0," Please  finger ");
//			    write_string(2,0,"                ");
				write_string(1,0,"    添加指纹    ");	
				memset(disBuff_ZW,0,DISMAXBUFF_ZW);
				sprintf(disBuff_ZW,"指纹ID:%d    ",(int)finger_id);
				write_string(2,0,disBuff_ZW);	
				write_string(3,0,"    请放置手指  ");	
				write_string(4,0,"                ");	
				while(Flag_KEY_CANCEL == 1)
			    {
					keycode = Getkeyboard();
					if (keycode<16)
					{
						Buzz_Times(1);
						if(keycode==15)	    //按下返回键退出录入返回fingerID调整状态   
						{
							Flag_KEY_CANCEL=0;
							break;
						}
					}
					FPM10A_Cmd_Get_Img(); //获得指纹图像
					FPM10A_Receive_Data(12);
					//判断接收到的确认码,等于0指纹获取成功
					if(FPM10A_RECEICE_BUFFER[9]==0)
					 {
						delay_ms(100);
						FINGERPRINT_Cmd_Img_To_Buffer1();
					    FPM10A_Receive_Data(12);
						 
//						write_string(1,0,"Successful entry");
						write_string(3,0,"第一次采集成功!");	 
						Buzz_Times(1);
						delay_ms(1000);
//						write_string(1,0," Please  finger ");
//				  		write_string(2,0,"                ");
						write_string(4,0,"请再次放置手指  ");	 
						while(1)
						{
							keycode = Getkeyboard();
							if (keycode<16)
							{
								Buzz_Times(1);
								if(keycode==15)	    //按下返回键退出录入返回fingerID调整状态   
								{
									break;
								}
							}
						 FPM10A_Cmd_Get_Img(); //获得指纹图像
						 FPM10A_Receive_Data(12);
						//判断接收到的确认码,等于0指纹获取成功
						if(FPM10A_RECEICE_BUFFER[9]==0)
						{
							delay_ms(200);
//							write_string(1,0,"Successful entry");
//							write_string(2,0,"    ID is       ");
							write_string(4,0,"第二次采集成功!");	
							
							write_string(2,0,"指纹ID:        ");	
							memset(disBuff_ZW,0,DISMAXBUFF_ZW);
							sprintf(disBuff_ZW,"指纹ID:%d    ",(int)finger_id);
							write_string(2,0,disBuff_ZW);	
							 //指纹iD值显示处理 
//							 write_com(0xc0+10);
//							 write_date(finger_id/100+48);
//							 write_date(finger_id%100/10+48);
//							 write_date(finger_id%100%10+48);
							
							FINGERPRINT_Cmd_Img_To_Buffer2();
					  		FPM10A_Receive_Data(12);
							FPM10A_Cmd_Reg_Model();//转换成特征码
		         			FPM10A_Receive_Data(12); 
						  	FPM10A_Cmd_Save_Finger(finger_id);                		         
		          			FPM10A_Receive_Data(12);
							Buzz_Times(1);
							delay_ms(1000);
							finger_id=finger_id+1;
					    	break;
					  	}
					   }
					   
		        		break;
						}
					}
			}
		}
		//	delay_ms(500);
	}
}
uchar  FPM10A_Find_Fingerprint_Touch(uint *IdNumber) //检测到指纹:1   未检测到指纹:0  --因为有界面变化
{
	unsigned int find_fingerid = 0;
	unsigned char id_show[]={0,0,0};
	FPM10A_Cmd_Get_Img(); //获得指纹图像
	FPM10A_Receive_Data(12);		
	//判断接收到的确认码,等于0指纹获取成功
	if(FPM10A_RECEICE_BUFFER[9]==0)
	{			
		delay_ms(100);
		FINGERPRINT_Cmd_Img_To_Buffer1();
		FPM10A_Receive_Data(12);		
		FPM10A_Cmd_Search_Finger();
		FPM10A_Receive_Data(16);			
		if(FPM10A_RECEICE_BUFFER[9] == 0) //搜索到  
		{
//			SendCMD(0x01);//清除显示
//			write_string(1,0,"  指纹检测成功  ");
				
			//拼接指纹ID数
			find_fingerid = FPM10A_RECEICE_BUFFER[10]*256 + FPM10A_RECEICE_BUFFER[11];					
			 //指纹iD值显示处理 
//			 write_com(0xc0+10);
//			 write_date(find_fingerid/100+48);
//			 write_date(find_fingerid%100/10+48);
//			 write_date(find_fingerid%100%10+48);
			
//			memset(disBuff_ZW,0,DISMAXBUFF_ZW);
//			sprintf(disBuff_ZW,"指纹ID:%d    ",(int)find_fingerid);
//			write_string(2,0,disBuff_ZW);		

			*IdNumber = find_fingerid;
		   }
			else //没有找到
			{
//				write_string(1,0," Search  failed ");
//				write_string(2,0,"                ");
				
//				SendCMD(0x01);//清除显示
//				write_string(1,0,"  指纹检测失败! ");
//				
//			 	Buzz_Times(5);
				*IdNumber = NONESTORE;
			}
//			delay_ms(5000);		
			/**************回到主功能界面****************/
			return 1;
			//	write_com(0x01);   //清屏	
			//	write_string(1,0,"  search finger ");	 //第一排显示搜索指纹
			//	write_string(2,0,"  Add     delete");	 //添加和删除指纹 
		} 
	return 0;
}
//搜索指纹
void FPM10A_Find_Fingerprint()
{
	unsigned int find_fingerid = 0,keycode;
	unsigned char id_show[]={0,0,0};
	Flag_KEY_CANCEL=1;
	SendCMD(0x01);//清除显示
	do
	{	
		write_string(1,0,"    搜索指纹    ");
		write_string(4,0,"按下# 键返回主页");	
		FPM10A_Cmd_Get_Img(); //获得指纹图像
		FPM10A_Receive_Data(12);		
		//判断接收到的确认码,等于0指纹获取成功
		if(FPM10A_RECEICE_BUFFER[9]==0)
		{			
			delay_ms(100);
			FINGERPRINT_Cmd_Img_To_Buffer1();
			FPM10A_Receive_Data(12);		
			FPM10A_Cmd_Search_Finger();
			FPM10A_Receive_Data(16);			
			if(FPM10A_RECEICE_BUFFER[9] == 0) //搜索到  
			{
//				write_string(1,0," Search success ");
//				write_string(2,0,"    ID is       ");
				
				write_string(2,0,"指纹搜索成功!  ");
				Buzz_Times(1);					
				//拼接指纹ID数
				find_fingerid = FPM10A_RECEICE_BUFFER[10]*256 + FPM10A_RECEICE_BUFFER[11];					
				 //指纹iD值显示处理 
//				 write_com(0xc0+10);
//				 write_date(find_fingerid/100+48);
//				 write_date(find_fingerid%100/10+48);
//				 write_date(find_fingerid%100%10+48);	
				memset(disBuff_ZW,0,DISMAXBUFF_ZW);
				sprintf(disBuff_ZW,"指纹ID:%d    ",(int)find_fingerid);
				write_string(3,0,disBuff_ZW);	
				
				delay_ms(2000);				
			   }
				else //没有找到
				{
//					write_string(1,0," Search  failed ");
//					write_string(2,0,"                ");
					
					write_string(2,0,"指纹搜索失败!  ");
					write_string(3,0,"                ");
					
				 	Buzz_Times(3);
				}
			}
			keycode = Getkeyboard();	
				//有效键值0-15
			if (keycode<16)
			{
				 Buzz_Times(1);
				 if(keycode==15)//返回键
				 {
				 	Flag_KEY_CANCEL=0;
					break;
				}
			}		
		}while(Flag_KEY_CANCEL == 1);
}
//删除所有存贮的指纹库
void FPM10A_Delete_All_Fingerprint()
{
		unsigned char i=0,keycode;
//				write_string(1,0,"   empty all    ");
//				write_string(2,0,"   Yes or no ?  "); 
		SendCMD(0x01);//清除显示
		write_string(1,0,"    指纹删除    ");
		write_string(2,0,"删除所有指纹?  ");
	    write_string(3,0,"  是  或  否?  ");
		write_string(4,0,"D:确认    #:返回");	
		Flag_KEY_CANCEL=1;
		do
		 {
		 	keycode = Getkeyboard();	
				//有效键值0-15
			if (keycode<16)
			{
				 Buzz_Times(1);
				 if(keycode==13)//确认键
				 {
				 	Flag_KEY_CANCEL=0;
					write_string(3,0,"正在删除指纹....");
					delay_ms(300);
					SendCMD(0x98);  //设定光标位置
					for(i=0;i<16;i++)      //进度条式更新,看起来美观
					 {
						SendDat(42);   //42对应ASIC码的 *
						delay_ms(200);     //控制进度条速度
					 }
						FINGERPRINT_Cmd_Delete_All_Model();
					    FPM10A_Receive_Data(12); 
						write_string(4,0,"  指纹删除成功!");
						Buzz_Times(3);
						break;
				 }	
				 if(keycode==15)//返回键
				 {
					 break;
				 }
			}
		 }while(Flag_KEY_CANCEL==1);
}

void Device_Check(void)
{
		unsigned char i=0;
		P3M1 &= ~(1<<5);	//Finger_Touch接口设置成准双向IO
		P3M0 &= ~(1<<5);
	
		
		FPM10A_RECEICE_BUFFER[9]=1;				           //串口数组第九位可判断是否通信正常
	
		SendCMD(0x01);//清除显示
		write_string(1,0,"    指纹模块    ");
		write_string(2,0,"正在加载        ");
		SendCMD(0x90+4);
		for(i=0;i<8;i++)						   //进度条式更新,看起来美观
		{
			SendDat(42);	                       //42对应ASIC码的 *
			delay_ms(300);						           //控制进度条速度
		}	
		FPM10A_Cmd_Check();									//单片机向指纹模块发送校对命令
		FPM10A_Receive_Data(12);							//将串口接收到的数据转存
 		if(FPM10A_RECEICE_BUFFER[9] == 0)					//判断数据低第9位是否接收到0
		{
			write_string(2,0,"    加载成功!  ");   //符合成功条件则显示对接成功
		}
		else
		{
			write_string(2,0,"    加载失败!  ");               //液晶先显示对接失败  		
		}
		delay_ms(2000);
}

DS1302时钟模块

#ifndef  __DS1302_H__
#define  __DS1302_H__
//注意:使用时注意 BCD码问题
#include "config.h"
/********************************************************************/ 
sbit SCK=P2^0;		
sbit SD=P2^1;		
sbit RST=P2^2;
/********************************************************************/ 
/*复位脚*/
#define RST_CLR	RST=0	/*电平置低*/
#define RST_SET	RST=1	/*电平置高*/
/*双向数据*/
#define SDA_CLR	SD=0	/*电平置低*/
#define SDA_SET	SD=1	/*电平置高*/
#define SDA_R	SD	/*电平读取*/	
/*时钟信号*/
#define SCK_CLR	SCK=0	/*时钟信号*/
#define SCK_SET	SCK=1	/*电平置高*/
/********************************************************************/ 
#define ds1302_sec_addr_write			0x80		//秒数据地址	   //写地址
#define ds1302_min_addr_write			0x82		//分数据地址
#define ds1302_hr_addr_write			0x84		//时数据地址
#define ds1302_date_addr_write		0x86		//日数据地址
#define ds1302_month_addr_write		0x88		//月数据地址
#define ds1302_day_addr_write			0x8A		//星期数据地址
#define ds1302_year_addr_write		0x8C		//年数据地址


//#define ds1302_sec_addr_read		0x81		//秒数据地址	 //读地址
//#define ds1302_min_addr_read		0x83		//分数据地址
//#define ds1302_hr_addr_read			0x85		//时数据地址
//#define ds1302_date_addr_read		0x87		//日数据地址
//#define ds1302_month_addr_read		0x89		//月数据地址
//#define ds1302_day_addr_read		0x8B		//星期数据地址
//#define ds1302_year_addr_read		0x8D		//年数据地址


#define ds1302_control_addr		0x8E		//写保护命令字单元地址
#define ds1302_charger_addr		0x90 		//涓电流充电命令字地址			 
#define ds1302_clkburst_addr	0xBE		//日历、时钟突发模式命令字地址
/********************************************************************/ 
/********************************************************************/ 

/*初始化DS1302*/
extern void Init_Ds1302(uchar *p);		//初始化时间
/*读取DS1302*/
extern void Read_NowTime_Ds1302(uchar *p);	  
/*设置DS1302*/
void Set_Ds1302(uchar *p);

#endif	 
/********************************************************************/
//		     	END FILE
/********************************************************************/
#include "ds1302.h"
//注意:使用时注意 BCD码问题
/********************************************************************/ 
/*单字节写入一字节数据*/
void Write_Ds1302_Byte(unsigned char dat) 
{
	unsigned char i;
	SCK = 0;
	for (i=0;i<8;i++) 
	{ 
		if (dat & 0x01) 	// 等价于if((addr & 0x01) ==1) 
		{
			SDA_SET;		//#define SDA_SET SDA=1 /*电平置高*/
		}
		else 
		{
			SDA_CLR;		//#define SDA_CLR SDA=0 /*电平置低*/
		}		 
		SCK_SET;
		_nop_();
		SCK_CLR;
		_nop_();		
		dat = dat >> 1; 
	} 
}
/********************************************************************/ 
/*单字节读出一字节数据*/
unsigned char Read_Ds1302_Byte(void) 
{
	unsigned char i, dat=0;	
	for (i=0;i<8;i++)
	{	
		dat = dat >> 1;
		if (SDA_R) 	  //等价于if(SDA_R==1)    #define SDA_R SDA /*电平读取*/	
		{
			dat |= 0x80;
		}
		else 
		{
			dat &= 0x7F;
		}
		SCK_SET;
		_nop_();
		SCK_CLR;
		_nop_();
	}
	return dat;
}

/********************************************************************/ 
/*向DS1302 单字节写入一字节数据*/
void Ds1302_Single_Byte_Write(unsigned char addr, unsigned char dat)
{ 

	RST_CLR;			/*RST脚置低,实现DS1302的初始化*/
	_nop_();
	SCK_CLR;			/*SCK脚置低,实现DS1302的初始化*/
	_nop_();
	RST_SET;			/*启动DS1302总线,RST=1电平置高 */
	_nop_();

	addr = addr & 0xFE;	 
	Write_Ds1302_Byte(addr); /*写入目标地址:addr,保证是写操作,写之前将最低位置零*/	
	Write_Ds1302_Byte(dat);	 /*写入数据:dat*/
	RST_CLR;				 /*停止DS1302总线*/
	_nop_();				//  注意驱动上没有!!!!!!!!
}

/********************************************************************/ 
/*从DS1302单字节读出一字节数据*/
unsigned char Ds1302_Single_Byte_Read(unsigned char addr) 
{ 
	unsigned char temp;
	RST_CLR;			/*RST脚置低,实现DS1302的初始化*/
	_nop_();
	SCK_CLR;			/*SCK脚置低,实现DS1302的初始化*/
	_nop_();
	RST_SET;	/*启动DS1302总线,RST=1电平置高 */	
	_nop_();

	addr = addr | 0x01;	   //最低位置位1,则是读地址  ,写之前将最低位置高
	Write_Ds1302_Byte(addr); /*写入目标地址:addr,保证是读操作*/
	temp=Read_Ds1302_Byte(); /*从DS1302中读出一个字节的数据*/	
		
	RST_CLR;	/*停止DS1302总线*/
	_nop_();	 //  注意驱动上没有!!!!!!!!
	SCK_SET;
	_nop_();
	SDA_CLR;	//  注意驱动上没有!!!!!!!!
	_nop_();
	SDA_SET	;
	_nop_();  //  注意驱动上没有!!!!!!!!
	return temp;
}
/********************************8421BCD码到十进制转换*****************************/
uchar BCD_DEC_conv(uchar x)
{
	uchar dec;
	dec =  0x0f & x;
	x = x >> 4;
	dec	= dec + x * 10;
	return(dec);
}
/********************************十进制到8421BCD码转换*****************************/	
uchar DEC_BCD_conv(uchar x)
{
	uchar bcd;
	bcd =  x % 10;
	x = x / 10;
	x = x << 4;
	bcd	= bcd | x ;
	return(bcd);
}
void DS1302_IOInit(void)
{
	P2M1 &= ~(1<<0);	//设置成准双向IO
	P2M0 &= ~(1<<0);

	P2M1 &= ~(1<<1);	//设置成准双向IO
	P2M0 &= ~(1<<1);
	
	P2M1 &= ~(1<<2);	//设置成准双向IO
	P2M0 &= ~(1<<2);
}
void Init_Ds1302(uchar *p)
{
	DS1302_IOInit();
	if((Ds1302_Single_Byte_Read(ds1302_sec_addr_write) & 0x80))  //  秒寄存器最高位:1的时候 时钟停止,0的时候时钟运行
	{
		Ds1302_Single_Byte_Write(ds1302_control_addr,0x00);	   //关写保护
		Ds1302_Single_Byte_Write(ds1302_year_addr_write,DEC_BCD_conv(*(p)));
		Ds1302_Single_Byte_Write(ds1302_month_addr_write,DEC_BCD_conv(*(p+1)));
		Ds1302_Single_Byte_Write(ds1302_date_addr_write,DEC_BCD_conv(*(p+2)));
		Ds1302_Single_Byte_Write(ds1302_hr_addr_write,DEC_BCD_conv(*(p+3)));
		Ds1302_Single_Byte_Write(ds1302_min_addr_write,DEC_BCD_conv(*(p+4)));
		Ds1302_Single_Byte_Write(ds1302_sec_addr_write,DEC_BCD_conv(*(p+5)));
		Ds1302_Single_Byte_Write(ds1302_control_addr,0x80);					 //开写保护
	}
	else   //时钟运行的话就不初始化了
	{
//		Ds1302_Single_Byte_Write(ds1302_control_addr,0x00);	   //关写保护
//		Ds1302_Single_Byte_Write(ds1302_year_addr_write,DEC_BCD_conv(*(p)));
//		Ds1302_Single_Byte_Write(ds1302_month_addr_write,DEC_BCD_conv(*(p+1)));
//		Ds1302_Single_Byte_Write(ds1302_date_addr_write,DEC_BCD_conv(*(p+2)));
//		Ds1302_Single_Byte_Write(ds1302_hr_addr_write,DEC_BCD_conv(*(p+3)));
//		Ds1302_Single_Byte_Write(ds1302_min_addr_write,DEC_BCD_conv(*(p+4)));
//		Ds1302_Single_Byte_Write(ds1302_sec_addr_write,DEC_BCD_conv(*(p+5)));
//		Ds1302_Single_Byte_Write(ds1302_control_addr,0x80);					 //开写保护
	}
}
void Read_NowTime_Ds1302(uchar *p)
{
	*(p) =   BCD_DEC_conv(Ds1302_Single_Byte_Read(ds1302_year_addr_write));
	*(p+1) = BCD_DEC_conv(Ds1302_Single_Byte_Read(ds1302_month_addr_write));
	*(p+2) = BCD_DEC_conv(Ds1302_Single_Byte_Read(ds1302_date_addr_write));
	*(p+3) = BCD_DEC_conv(Ds1302_Single_Byte_Read(ds1302_hr_addr_write));
	*(p+4) = BCD_DEC_conv(Ds1302_Single_Byte_Read(ds1302_min_addr_write));
	*(p+5) = BCD_DEC_conv(Ds1302_Single_Byte_Read(ds1302_sec_addr_write));
}
void Set_Ds1302(uchar *p)
{
	Ds1302_Single_Byte_Write(ds1302_control_addr,0x00);	   //关写保护
	Ds1302_Single_Byte_Write(ds1302_year_addr_write,DEC_BCD_conv(*(p)));
	Ds1302_Single_Byte_Write(ds1302_month_addr_write,DEC_BCD_conv(*(p+1)));
	Ds1302_Single_Byte_Write(ds1302_date_addr_write,DEC_BCD_conv(*(p+2)));
	Ds1302_Single_Byte_Write(ds1302_hr_addr_write,DEC_BCD_conv(*(p+3)));
	Ds1302_Single_Byte_Write(ds1302_min_addr_write,DEC_BCD_conv(*(p+4)));
	Ds1302_Single_Byte_Write(ds1302_sec_addr_write,DEC_BCD_conv(*(p+5)));
	Ds1302_Single_Byte_Write(ds1302_control_addr,0x80);					 //开写保护
}

STC系列的内置EEPROM读写程序

#ifndef	__EEPROM_H
#define	__EEPROM_H

#include "config.h"

/************************** ISP/IAP *****************************

用户可用根据自己的需要在整个FLASH空间中规划出任意不超过FLASH大小的EEPROM空间,但需要注意:EEPROM总是从后向前进行规划的。
例如:STC8A8K64S4A12这个型号的FLASH为64K,
此时若用户想分出其中的8K作为EEPROM使用,则EEPROM的物理地址则为64K的最后8K,物理地址为E000h-FFFFh,
当然,用户若使用IAP的方式进行访问,目标地址仍然从0000h开始,到1FFFh结束,当使用MOVC读取则需要从EO00h开始,到FFFFh结束。
0x0000 - 0x01ff
0x0200 - 0x03ff
0x0400 - 0x05ff
*/
#define   EEROM_USEADDR 0x0200
#define   EEROM_CHECK_VALUE 0xf1


#define WT_30M          0x80
#define WT_24M          0x81
#define WT_20M          0x82
#define WT_12M          0x83
#define WT_6M           0x84
#define WT_3M           0x85
#define WT_2M           0x86
#define WT_1M           0x87

uchar IapRead(uint addr);
void IapErase(uint addr);
void IapProgram(uint addr, uchar dat);

void 	EEPROM_read_n(uint EE_address,uchar *DataAddress,uint number);
void 	EEPROM_write_n(uint EE_address,uchar *DataAddress,uint number);


#endif
//	本程序是STC系列的内置EEPROM读写程序。
#include "eeprom.h"

void IapIdle()
{
    IAP_CONTR = 0;                              //关闭IAP功能
    IAP_CMD = 0;                                //清除命令寄存器
    IAP_TRIG = 0;                               //清除触发寄存器
    IAP_ADDRH = 0x80;                           //将地址设置到非IAP区域
    IAP_ADDRL = 0;
}
uchar IapRead(uint addr)
{
    uchar dat;

    IAP_CONTR = WT_12M;                         //使能IAP
    IAP_CMD = 1;                                //设置IAP读命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();
    dat = IAP_DATA;                             //读IAP数据
    IapIdle();                                  //关闭IAP功能

    return dat;
}
void IapProgram(uint addr, uchar dat)
{
    IAP_CONTR = WT_12M;                         //使能IAP
    IAP_CMD = 2;                                //设置IAP写命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_DATA = dat;                             //写IAP数据
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();
    IapIdle();                                  //关闭IAP功能
}
void IapErase(uint addr)
{
    IAP_CONTR = WT_12M;                         //使能IAP
    IAP_CMD = 3;                                //设置IAP擦除命令
    IAP_ADDRL = addr;                           //设置IAP低地址
    IAP_ADDRH = addr >> 8;                      //设置IAP高地址
    IAP_TRIG = 0x5a;                            //写触发命令(0x5a)
    IAP_TRIG = 0xa5;                            //写触发命令(0xa5)
    _nop_();                                    //
    IapIdle();                                  //关闭IAP功能
}
//========================================================================
// 函数: void EEPROM_read_n(uint EE_address,uchar *DataAddress,uint number)
// 描述: 从指定EEPROM首地址读出n个字节放指定的缓冲.
// 参数: EE_address:  读出EEPROM的首地址.
//       DataAddress: 读出数据放缓冲的首地址.
//       number:      读出的字节长度.
// 返回: non.
// 版本: V2.0, 2021-12-27
//========================================================================
void EEPROM_read_n(uint EE_address,uchar *DataAddress,uint number)
{
	EA = 0;		//禁止中断
	do
	{
		*DataAddress = IapRead(EE_address);			//读出数据
		EE_address++;
		DataAddress++;
	}while(--number);
	EA = 1;		//重新允许中断
}
//========================================================================
// 函数: void EEPROM_write_n(uint EE_address,uchar *DataAddress,uint number)
// 描述: 把缓冲的n个字节写入指定首地址的EEPROM.
// 参数: EE_address:  写入EEPROM的首地址.
//       DataAddress: 写入源数据的缓冲的首地址.
//       number:      写入的字节长度.
// 返回: non.
// 版本: V2.0, 2021-12-27
//========================================================================
void EEPROM_write_n(uint EE_address,uchar *DataAddress,uint number)
{
	EA = 0;		//禁止中断	
	//-----------------------------------------	
    IapErase(EE_address);
	do
	{
        IapProgram(EE_address, *DataAddress);
		EE_address++;
		DataAddress++;
	}while(--number);
	EA = 1;		//重新允许中断
}

LCD12864串行方式驱动

#ifndef __12864_SERIAL_H__
#define __12864_SERIAL_H__

#include "config.h"

sbit RS_CS =P0^7;//chip select 
sbit RW_SID=P0^6;//data input/output         
sbit E_CLK =P0^5;//clock input 

//sbit psb=P3^2;//串并行选择端  L串行  可直接接到L  	 可接地

#define cursor_on  0x0f 		  //开光标
#define cursor_off 0x0c 	      //关光标	 

void lcd_12864_delay(uint n);
//串行发送一字节数据
void SendByte(uchar dat);     
//写控制命令
void SendCMD(unsigned char dat);
//写显示数据或单字节字符
void SendDat(unsigned char dat);
void write_string(uchar hang,uchar add,uchar *p);
void write_BCD(uchar hang,uchar add,uchar dat);
//初始化 LCM
void LCD_Init(void);


#endif
#include "12864_serial.h"     
void lcd_12864_delay(uint n) 
{  
	unsigned int i;
	 do{
	      i = 10;
		  while(--i);   
     }while(--n);
}
//串行发送一字节数据
void SendByte(uchar dat)
{    
	uchar i;
	for(i=0;i<8;i++)
	{
		E_CLK=0;
		if(dat&0x80)
		RW_SID=1;
		else RW_SID=0;
		E_CLK=1;

		dat<<=1;
	}
}
      
//写控制命令
void SendCMD(unsigned char dat)
{    lcd_12864_delay(30);
	 RS_CS=1;
     SendByte(0xF8);//11111,00,0 RW=0,RS=0   同步标志
     SendByte(dat&0xF0);//高四位
     SendByte((dat&0x0F)<<4);//低四位
     RS_CS=0;
	if(dat == 0x01) delay_ms(100);  //清屏指令需要延时!
}

//写显示数据或单字节字符
void SendDat(unsigned char dat)
{	lcd_12864_delay(30);
 	RS_CS=1;
 	SendByte(0xFA);//11111,01,0 RW=0,RS=1
 	SendByte(dat&0xF0);//高四位
 	SendByte((dat&0x0F)<<4);//低四位
    RS_CS=0;
}  

//void SendStr(uchar x_add,uchar code *ptr)
//{   if(x_add)
//       SendCMD(x_add);
//    //1xxx,xxxx 设定DDRAM 7位地址xxx,xxxx到地址计数器AC
//	    while(*ptr != '\0')
//             {  		SendDat(*ptr++);
//	      }
//}    
/***********************lcd12864上显示字符函数************************/
//地址:  128/16=8,一行只有8个汉字,可显示16个字符	  add<8(汉字)

void write_string(uchar hang,uchar add,uchar *p)
{
	uchar number=0;
	if(hang==1)   
		SendCMD(0x80+add);
	else if(hang==2)
		SendCMD(0x90+add);
	else if(hang==3)
		SendCMD(0x88+add);
	else 
		SendCMD(0x98+add);

	while(*p != '\0' && number<16)
	{  		
		SendDat(*p++);
		number++;
	}	
}
void write_BCD(uchar hang,uchar add,uchar dat)
{
	uchar a=0,b=0;
	a=dat/16;
	b=dat%16;
	if(hang==1)   
		SendCMD(0x80+add);
	else if(hang==2)
		SendCMD(0x90+add);
	else if(hang==3)
		SendCMD(0x88+add);
	else 
		SendCMD(0x98+add);
	if(a<10)
	{
		SendDat(0x30+a);			
	}
	else if(a==10)
	{
		SendDat('A');	
	}
	else if(a==11)
	{
		SendDat('B');	
	}
	
	else if(a==12)
	{
		SendDat('C');	
	}
	else if(a==13)
	{
		SendDat('D');	
	}
	else if(a==14)
	{
		SendDat('E');	
	}
	else if(a==15)
	{
		SendDat('F');	
	}
	else{}	
	if(b<10)
	{
		SendDat(0x30+b);			
	}
	else if(b==10)
	{
		SendDat('A');	
	}
	else if(b==11)
	{
		SendDat('B');	
	}
	
	else if(b==12)
	{
		SendDat('C');	
	}
	else if(b==13)
	{
		SendDat('D');	
	}
	else if(b==14)
	{
		SendDat('E');	
	}
	else if(b==15)
	{
		SendDat('F');	
	}
	else{}	
}
//初始化 LCM
void LCD_Init(void)
{  
	P0M1 &= ~(1<<5);	//设置成准双向IO
	P0M0 &= ~(1<<5);
	
	P0M1 &= ~(1<<6);	//设置成准双向IO
	P0M0 &= ~(1<<6);

	P0M1 &= ~(1<<7);	//设置成准双向IO
	P0M0 &= ~(1<<7);
	
	
//	psb=0;			//可接地
    RS_CS=0;
    lcd_12864_delay(100);
	SendCMD(0x30);//功能设定
	SendCMD(0x0C);//0000,1100 整体显示,游标off,游标位置	    
	SendCMD(0x01);//清除显示
	write_string(1,0,"    欢迎使用    ");
	write_string(2,0,"  ****学院  ");
	write_string(3,0,"学生门禁管理系统");
	write_string(4,0,"设计人:    **");	
	delay_ms(3000);	   
//	SendCMD(0x01);//清除显示
}

PS:由于之前文章有RFID代码,故不在此放了,感兴趣可看之前的文章。