3G指纹考勤系统(STM32芯片)
本设计为3G移动网络指纹考勤系统,通过3G网络与上位机的实时连接实现实时考勤。
该系统主要有六个模块,分为主控制芯片,屏幕模块,矩阵键盘模块,3G通信模块,指纹模块和LED灯
该系统分为3个模式A.读取模式 B.录入模式 C.删除指纹库模式
2.1 屏幕显示部分设计
开机后,屏幕显示开机欢迎的画面,然后过2s左右,进入显示的主界面,主界面显示各种功能键对应的模式选择,当前系统所在的模式(读取模式或者录入模式)。
当前时间的显示,实时显示当前时间,上面为当前日期,下面为时间。
指纹录入与读取状态的显示:
在读取模式下,显示指纹正确与否,指纹正确时提示指纹所对应的工号,错误则提示请重新录入指纹。
在录入指纹模式下提示请输入工号,每输入一个工号屏幕便会显示输入的数字,输入3位工号后会提示请录入指纹,录入指纹成功后会显示保存成功,失败显示保存失败,然后自动回到输入工号的界面。
在删除指纹库模式下,若删除指纹库成功,屏幕显示删除指纹库成功,过1s左右的延时后退回到读取模式。
在屏幕的最下面是用来提示网络的连接状态与3G模块与单片机连接的情况,在网络初始化的一开始显示AT init,如果单片机与3G模块是正常连接的,显示AT OK,当开发板上的NET灯快闪时,表示已成功打开3G网络,可以往上位机发送数据了。
2.2 相关提示灯设计
本系统主要的提示灯有三个
DS3 当开机时,DS3灯常亮
NET灯:网络状态指示灯,当此灯快闪是,代表网络已打开
DS4 指示指纹模块与单片是否握手成功,如果握手成功,DS4灯常亮,如果握手失败,DS4亮2s后灭,此时需要检查指纹模块是否与单片机连接成功,指纹模块电源有无接对,RX TX端是否正确连接到单片机串口的TX RX端
2.3通信格式与上位机的设计
在系统获取到打卡信息后,需要把打卡信息发送给上位机从而实现实时考勤的功能,而在发送的信息上,考勤系统发送的格式要与上位机接收的格式一致,才能正常实现考勤的功能
根据上位机数据接收的设置,设定了如下的发送格式
sn;msgType;devID;staffID;time;status
sn代表打卡条目序列号,每次从考勤系统发送给上位机的sn号应该不同,从而区分不同的打卡条目。
msgType 代表消息的种类,在开机时,发送1,代表设备登录,在打卡时发送2,代表有人打卡
devID 代表设备的序列号,设置为1,对应上位机的设备ID
staffID 代表职员的工号,由四位数组成,范围是0000-0999,对应上位机的员工ID
time 代表打卡信息发送的时间
status 代表设备的运行状态,在线为1,离线为0,因为本系统是用直接用按钮开关控制设备的开关机,所以status默认为1
上位机主要实现对连接设备的管理,公司人员的管理,考勤记录的显示与导出的功能。一个上位机可以对应多个考勤机,在系统使用前,先通过主界面员工管理的功能去添加与删除员工信息,员工的信息有工号,姓名,性别,部门,当设备上线后,考勤机向上位机发送设备登录命令,软件的考勤日志中显示设备上线时间和设备ID XXX已上线,提示有设备已上线,同时在设备列表中显示该设备,我们可以通过点击设备管理中的删除设备来管理连接的考勤机,当设备上线后,我们就可以接收由考勤系统发送的打卡信息,当有人打卡时,显示打卡时间与员工IDXXXX 已签到。这时,可以通过设备管理中的显示记录功能来查看考勤记录,考勤记录有序号,工号,姓名,时间四项内容。可以通过设备管理中的导出记录导出考勤记录,导出格式为TXT格式。
开机后,单片机向3G模块发送AT指令,判断单片机是否与模块正常连接,接收到ok后单片机再向3G模块发送开启网络请求,本系统与上位机传输用的协议是UDP,开启UDP连接,然后再根据发送的字节发送数据给上位机。
2.4键盘控制的设计
本设计用到的是矩阵键盘,我们主要用到的键是数字键和ABC功能键,其他键暂时没有用到,A键代表读取模式 ,B键代表录入模式, C键代表删除指纹库模式,数字键主要用于录入模式时工号的输入
2.5 时钟的设计
本设计通过STM32自带的RTC实时时钟单元提供时钟功能, 显示方式为:年-月-日
时:分:秒
2.6指纹模块的功能设计
在读取模式中,指纹模块不断进行指纹的扫描,判断是否有人按压指纹,如果有人按压指纹,与flash中指纹库中的指纹特征进行比较,读取相对应的指纹号,显示在屏幕上,并实时通过3G模块上传至上位机。
在录入模式中,先输入工号,输入三位数工号后,指纹模块进行指纹的扫描,这里做了超时的设置,如果在一定的时间内没有指纹录入,将会自动跳回到输入工号的模式,指纹录入成功后,指纹特征文件会储存在指纹模块相对应指纹号的flash空间内。
在删除模式中,单片机通过发送删除指纹库指令给指纹模块,指纹识别模块中的flash保存的指纹模板清空。
Main函数:
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
#include "GPRS.h"
#include "GUI.h"
#include "Lcd_Driver.h"
#include "key.h"
#include "timer.h"
#include "led.h"
#include "Lcd_Display.h"
#include "fingerprint.h"
#include "keyboard.h"
#include "rtc.h"
#include "numchange.h"
#include "stdlib.h"
/********************************主函数**********************************************/
int main(void)
{
unsigned int sn,msgTypeStart=1,msgTypeDoing=2,devID=1;
int status=1;
u8 InputNum[20]={'\0'};
u8 Inputcout;
SystemInit(); //System init.
delay_init(72);//Delay init.
NVIC_Configuration();
USART1_Config();
uart2_init(9600);
uart3_init(9600);
LED_Init(); //DS4LED灯初始化
Keyboard_RCCInit(); //矩阵键盘时钟使能
Rtc_Init(); //时钟初始化,PWR,BKP时钟使能,NVIC设置,RCC设置,秒中断中断服务函数
Lcd_Display_Init(); //屏幕显示初始化与开机画面
GPRS_INT(); //3G模块初始化
GPRS_TEST();
TIM3_Int_Init(200,7199); //10Khz的计数频率,计数到200为20ms
/*******初始化发送数据给上位机******/
Time_Show(); //读出时间
ChangeDateformat(); //改变时间格式int->char,便于数据传输
srand(time_now.tm_sec+time_now.tm_min); //使用时间的秒+分钟做时间种子
sn=rand()%900000+100000; //随机生成6位数的序列号
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
printf("AT+UDPSEND=37,\"61.174.40.245\",28176\r\n"); //发送37字节请求,
delay_ms(200);
printf("%d;%d;%d;%s;%s-%s-%s %s:%s:%s;%d\x1a", //发送数据 x1a发送符号ctrl+Z
sn,msgTypeStart,devID,SearNumb,YearNumb,MonthNumb, DayNumb,HourNumb,MinNumb,SecNumb,
status);
delay_ms(100);
/**************************************************************************************/
if( 1==VefPSW() ) //如果传感器握手成功,DS4长亮,握手失败,DS4亮2s后灭
{
GPIO_ResetBits(GPIOD,GPIO_Pin_2);}
else
{
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
delay_ms(2000);
GPIO_SetBits(GPIOD,GPIO_Pin_2);
}
while(1)
{
Time_Show(); //时间显示
/*****************录入指纹模式modeflag == 1*******************************************/
if( modeflag == 1 ) //modeflag == 1为录入指纹模式
{
for(Inputcout=0;Inputcout<3;Inputcout++) //循环,清零,当按了功能键后InputNum[]会有记录,此时需要清零
InputNum[Inputcout]=0;
Gui_DrawFont_GBK16(16,180,RED,GRAY0,"录入模式 请输入工号:");
for(Inputcout=0;Inputcout<3;) //循环,每次输入1个数字就在屏幕显示,并储存在InputNum[Inputcout]中,每当按了非数字键,跳出循环
{
if(KEY_Inputflag==1) //使用if...while...语句,防止重复扫描
{
while(KEY_Inputflag==1); if(KEY_Input=='#'||KEY_Input=='*'||KEY_Input=='A'||KEY_Input=='B'||KEY_Input=='C'||KEY_Input=='D') //如果按了功能键,清零
{
for(Inputcout=0;Inputcout<3;Inputcout++)
InputNum[Inputcout]=0;
break;
}
InputNum[Inputcout]=KEY_Input; //输入键值保存在InputNum[Inputcout]中
if(Inputcout==0)
Gui_DrawFont_GBK16(190,180,RED,GRAY0,&InputNum[Inputcout]);//屏幕显示输入值
if(Inputcout==1)
Gui_DrawFont_GBK16(200,180,RED,GRAY0,&InputNum[Inputcout]);
if(Inputcout==2)
Gui_DrawFont_GBK16(210,180,RED,GRAY0,&InputNum[Inputcout]);
Inputcout++;
}
}
SaveNumber=((InputNum[0]-48)*100)+((InputNum[1]-48)*10)+(InputNum[2]-48); //保存,ASCII码-48为数字,转换成SaveNumber
Gui_DrawFont_GBK16(16,180,RED,GRAY0," "); //清屏
Gui_DrawFont_GBK16(16,180,RED,GRAY0,"录入模式 请录入指纹");
if( SaveNumber<1000 ) //如果SaveNumber小于1000
{
if(enroll()==1) //采集两次,生成1个指纹模板成功
{
if(savefingure(SaveNumber)==1) //保存也成功
{
delay_ms(100);
Gui_DrawFont_GBK16(16,200,RED,GRAY0," ");
Gui_DrawFont_GBK16(16,200,RED,GRAY0,"保存成功"); //显示保存成功
delay_ms(800);
Gui_DrawFont_GBK16(16,200,RED,GRAY0," “ );//清屏
}
}
else
{
delay_ms(100);
Gui_DrawFont_GBK16(16,180,RED,GRAY0," "); //清屏
Gui_DrawFont_GBK16(16,200,RED,GRAY0," ");
Gui_DrawFont_GBK16(16,200,RED,GRAY0,"保存失败");
delay_ms(800);
Gui_DrawFont_GBK16(16,200,RED,GRAY0," ");
}
}
}
/***************************************END***************************************/
/*****************************识别模式modeflag==0*********************************/
if( modeflag==0 ) //modeflag为识别模式
{
Gui_DrawFont_GBK16(16,200,RED,GRAY0," ");
Gui_DrawFont_GBK16(80,180,RED,GRAY0," ");
Gui_DrawFont_GBK16(16,180,RED,GRAY0,"读取模式"); //显示读取模式
searchnum = search(); //搜索指纹号
if(searchnum>=1 && searchnum<= 1000 ) //指纹号有效
{
numshow(searchnum); //显示搜索到的指纹 显示工号 800ms
ChangeDateformat(); //把int格式转成字符串格式,便于网络传输
srand(time_now.tm_sec+time_now.tm_min); //设置随机种子
sn=rand()%900000+100000; //生成6为的随机数
delay_ms(1000);
printf("AT+UDPSEND=37,\"61.174.40.245\",28176\r\n");
delay_ms(200);
printf("%d;%d;%d;%s;%s-%s-%s %s:%s:%s;%d\x1a", //发送数据
sn, msgTypeDoing,devID,SearNumb,YearNumb,MonthNumb, DayNumb,HourNumb,MinNumb,
SecNumb,status);
delay_ms(100);
}
if(searchnum == 65535) //识别指纹失败
{
Gui_DrawFont_GBK16(16,220,RED,GRAY0," ");
Gui_DrawFont_GBK16(16,200,RED,GRAY0,"请重新识别");
delay_ms(800);
}
}
/*****************************************END****************************************/
/**********************************删除指纹库*****************************************/ if(clearallflag==1)
{
clearallflag=0;
Clear_All(); //清除所有指纹
SaveNumber=0;
Gui_DrawFont_GBK16(16,180,RED,GRAY0," ");
Gui_DrawFont_GBK16(16,200,RED,GRAY0," ");
Gui_DrawFont_GBK16(16,220,RED,GRAY0," ");
Gui_DrawFont_GBK16(16,180,RED,GRAY0,"删除指纹库成功");
delay_ms(800);
Gui_DrawFont_GBK16(16,180,RED,GRAY0," ");
modeflag=0; //进入录入指纹模式
}
/********************END*************************************************************/
}
}
/************************END OF MAIN************************************************/
/***************3G模块初始化程序*****************************************************/
void GPRS_INT(void)//GPRS初始化
{
Send_AT();//AT联机测试,发送AT
}
void Send_AT(void)
{
u8 *p,i=10; //
while(i--) //测试10次,在某一次成功就退出
{ Gui_DrawFont_GBK16(16,280,BLUE,GRAY0,"AT init");
CLR_RBUF1();
SendToGsm(AT,2); //发送"AT"
Send_DA();//回车符0x0d 0x0a
delay_ms(100);delay_ms(100); delay_ms(100);delay_ms(100);
p=mystrstr(sci1_rbuf,"OK"); //接收到的数据存在RsBuf,确认是否收到OK
if(p!=NULL) //接收到"OK"
{
printf("AT OK");
Gui_DrawFont_GBK16(16,300,BLUE,GRAY0,"AT OK");
break;
}
}
void GPRS_TEST(void) //开启网络
{
delay_ms(1000);delay_ms(1000);delay_ms(1000);delay_ms(1000);delay_ms(1000);
delay_ms(1000);delay_ms(1000);delay_ms(1000);delay_ms(1000);delay_ms(1000);//等待开机初始化
OPEN_NET();
delay_ms(200);
}
void OPEN_NET(void)
{
u8 *p,a=5; //
while(a--) //测试5次,在某一次成功就退出
{
CLR_RBUF1();
Gui_DrawFont_GBK16(16,280,BLUE,GRAY0," ");
Gui_DrawFont_GBK16(16,300,BLUE,GRAY0," ");
SendToGsm(AT,3); //"AT+"
printf("NETOPEN=\"%s\",%s",modetbl[1],PROT1);
Send_DA();//回车符//设置短消息模式 1 TEXT
delay_ms(1500);
p=mystrstr(sci1_rbuf,"OK"); //接收到的数据存在sci1_rbuf
if(p!=NULL)
{
Gui_DrawFont_GBK16(16,300,BLUE,GRAY0,"OPEN_NET ok");
break;//接收到"OK"
}
}
}
/**********************************************************************************/
/**************通用定时器3中断服务函数********************************************/
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIMx更新中断标志
// KEY_Scan();
Keyboard_Timerscan(); //每次TIM3中断扫描一次键盘,得到键值,读出KEY_Input
Mode_scan(); //每次TIM3中断扫描一次模式,读出响应的modeflag,clearallflag
clk0++; //clk0++,防止指纹模块超时
}
}
/*************************************************************************************/
/**************************时间相关函数***********************************************/
void Time_Show(void) //时间显示函数
{
u32 CurrenTime = 0;
if(TimeDisplay)
{
/* 读出当前RTC计数值(UNIX时间格式) */
CurrenTime = Time_GetUnixTime();
/* 将UNIX时间格式转换为标准系统时间格式 */
time_now = Time_ConvUnixToCalendar(CurrenTime);
Yearshow(time_now.tm_year);
Monthshow(time_now.tm_mon);
Dayshow(time_now.tm_mday);
Hourshow(time_now.tm_hour);
Minshow(time_now.tm_min);
Secshow(time_now.tm_sec);
TimeDisplay = 0;
}
}
void RTC_IRQHandler(void) //RTC秒中断服务函数
{
if(RTC_GetITStatus(RTC_IT_SEC) != RESET)
{
/* 清除 RTC 秒中断 */
RTC_ClearITPendingBit(RTC_IT_SEC);
/* 更新时间显示标志位 */
TimeDisplay = 1;
}
}
void Yearshow(int num) //把int数字成数组以便显示,+0x30代表是+48,ASCII码
{
unsigned char str[5] = {0};
str[0] = num/1000 + 0x30;
str[1] = num%1000/100 + 0x30;
str[2] = num%100/10 + 0x30;
str[3] = num%10 + 0x30;
str[4] = '\0';
Gui_DrawFont_GBK16(106,240,RED,GRAY0,str);
}
void ChangeDateformat(void) //使年月日时分秒从int>char,便于数据传输
{
YearNumbf(time_now.tm_year);
MonthNumbf(time_now.tm_mon);
DayNumbf(time_now.tm_mday);
HourNumbf(time_now.tm_hour);
MinNumbf(time_now.tm_min);
SecNumbf(time_now.tm_sec);
SearNumbf(SearchNumber);
}
struct tm { //时间戳中时间变量(年月日时分秒)的定义
int tm_sec; /* seconds after the minute, 0 to 60
(0 - 60 allows for the occasional leap second) */
int tm_min; /* minutes after the hour, 0 to 59 */
int tm_hour; /* hours since midnight, 0 to 23 */
int tm_mday; /* day of the month, 1 to 31 */
int tm_mon; /* months since January, 0 to 11 */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday, 0 to 6 */
int tm_yday; /* days since January 1, 0 to 365 */
int tm_isdst; /* Daylight Savings Time flag */
union { /* ABI-required extra fields, in a variety of types */
struct {
int __extra_1, __extra_2;
};
struct {
long __extra_1_long, __extra_2_long;
};
struct {
char *__extra_1_cptr, *__extra_2_cptr;
};
struct {
void *__extra_1_vptr, *__extra_2_vptr;
};
};
};
/**************************************************************************************/
/***************************指纹模块相关程序*******************************************/
unsigned char VefPSW(void)//验证设备握手口令,成功返回1
{
unsigned char count=0;
while (1)
{
if(Command(VPWD,20) && (FifoNumber==11) && (FIFO[9]==0x00)) //Command函数的含义为向指纹模块发送VPWD对应的AT指令,并设置超时时间为20
{
return(1);
}
count++;
if (count>=3)//如果不成功,再验证一次,如果3次不成功,返回失败
{
return(0);
}
}
}
void Clear_All(void) //清空指纹库
{
delay_ms(200);
Command(DELE_all,50); //清空指纹库
}
unsigned char ImgProcess(unsigned char BUFID) //发获取图像并生成特征文件,存入BUFID中//输入参数为缓冲区号
{
if(Command(GIMG,89) && (FifoNumber==11) && (FIFO[9]==0x00))
{
if(BUFID==1)
{
if(Command(GENT1,60) && (FifoNumber==11) && (FIFO[9]==0x00))
{
return 1;
}
else
{
return 0;
}
}
else if(BUFID==2)
{
if(Command(GENT2,60) && (FifoNumber==11) && (FIFO[9]==0x00))
{
return 1;
}
else
{
return 0;
}
}
}
else
{
return 0;
}
return 0;
}
unsigned int Searchfinger(void)//搜索指纹(发送搜索命令、以及根据返回值确定是否存在)
{
if(Command(SEAT,60) && (FifoNumber==15) && (FIFO[9]==0x00) )
{
SearchNumber = FIFO[10]*256 + FIFO[11];//搜索到的页码,从而判断是几号指纹
return 1;
}
else
{
return 0;
}
}
unsigned int search(void)//搜索指纹
{
unsigned char SearchBuf=0,i=0;
while (i<4)
{
if (ImgProcess(1)==1)//首先读入一次指纹
{
SearchBuf = Searchfinger();//进行指纹比对,如果搜索到,返回搜索到的指纹序号
if(SearchBuf==1)
{
return SearchNumber;
}
else
{
return 65535;//表示搜索到的指纹不正确
}
}
i++;
}
return 0;
}
unsigned char savefingure(unsigned int ID)//保存指纹
{
unsigned char i=0;
unsigned int sum = 0;
for (i=0;i<16;i++) //保存指纹信息 16代表16个十六进制指令
{
FIFO[i]=STOR[i];
}
FIFO[12]=ID/256;
FIFO[13]=ID%256; //把指纹模板存放的PAGE_ID也就是FLASH的位置
//现在开始计算校验和 由于第一个字节(数组地址0)存放的是这个命令长度,所以要注意位置
for (i=7;i<14;i++)
{
sum= sum + FIFO[i];
}
FIFO[14]=sum/256; //校验和
FIFO[15]=sum%256; //校验和
if (Command(FIFO,70)==1)//此处进行存放指纹模板的命令
{
return(1);
}
else
{
return(0);//不成功返回0
}
}
unsigned char enroll(void) //采集两次指纹,生成1个 指纹模板
{
unsigned char temp=0,count=0;
while(1)
{
temp=ImgProcess(1); //生成特征1
if (temp==1)//生成特征文件成功
{
//采集第一个特征成功
count=0;
delay_ms(100);
break;
}
else
{
if (temp==0)//采集指纹没有成功
{
count++;
if (count>=40)//如果采集了40次,还不成功,直接采集失败,直接退出enroll函数----返回0
return(0);
}
}
}
delay_ms(2000);//延时2S开始采集下一个特征
//开始采集第二个特征
while(1)
{
temp=ImgProcess(2); //生成特征2
if (temp==1)//生成特征文件2成功
{
if( (Command(MERG,40)==1) && (FifoNumber==11) && (FIFO[9]==0x00) ) //合并成功返回1
{
return(1);
}
else
{
return(0);
}
}
else
{
if (temp==1)//采集指纹没有成功
{
count++;
if (count>=25)
return(0);
}
}
}
}
/**************************************************************************************/
/**********************按键相关函数****************************************************/
void Keyboard_Timerscan(void) //定时器扫描
{
u8 KEY=0; //当前键盘代码
KEY = Keyboard_Scan( ); //得到键值字符
if(KEY ==0 && KEY_record==0) //F=0.无按下
{
KEY_Inputflag=0;
KEY_Input = 0;
}
if(KEY != KEY_record) //有按键变化
{
if(KEY_record ==0&&KEY!=0) //F=1.从松开>按下的变化
{
KEY_Inputflag=1 ; //调用函数是等待变化
KEY_Input = KEY;/* */ //这里可以设置0(通常松开再读取)
}
if(KEY_record !=0&&KEY==0) //F=2.从按下>松开的变化
{
KEY_Inputflag=2 ; //按下后松开
KEY_Input = KEY_record; //需要读取按键,不能KEY_Input = KEY,因为KEY已经=0
}
KEY_record = KEY; //记录下键值,防止长按导致连续输入
}
}
void Mode_scan(void) //定时器中断键盘扫描,记录到标志位
{
if(KEY_Inputflag==2)
{
/*识别模式*/
if(KEY_Input=='A') //当按下A
{
modeflag=0;
KEY_Inputflag=0;
}
/*录入模式*/
if(KEY_Input=='B') //当按下B
{
modeflag=1;
KEY_Inputflag=0;
}
//清除所有指纹模式
if(KEY_Input=='C') //当按下C
{
clearallflag=1;
KEY_Inputflag=0;
}
/***************************************************************************************/
- 点赞
- 收藏
- 关注作者
评论(0)