AS608光学指纹模组编程和应用详解
一、前言
上次讲解了两个简单实用的传感器,反馈很多,能够帮助到大家,我也很开心。所以今天继续给大家介绍一个实用易上手的传感器,在这里有些同学可能就会质疑了,指纹识别哪里简单了?别急,容我细细道来,看完整篇文章,你可能就会觉得,不过如此嘛。
需要程序源码,原理图,数据手册等资料可以到文章底部的链接自行下载。因为这个是我大学做毕设时用到的一个模块,内容比较多,有些细节一时记不清了,所以有些地方可能讲的不是特别深入,如果你们有什么地方看不懂,可以留言和私信给我,我再给你们解答。
如果能够帮助到你,请点赞+关注,又快到做毕设的日子了,我后面也会继续写更多的博文,希望能够帮到你们。
二、芯片介绍
1、芯片简介
AS608是一个集成的光学指纹芯片,内部有指纹算法,其实指纹识别最难的地方在于算法,目前的算法是前人不断开发完善得到的,所以在前几年,算法都是某些大企业独有的技术,他们只卖芯片不卖算法。当然现在的话,光学指纹的算法也逐渐放出来了,因为现在用的更多的是半导体,超声波指纹识别,光学指纹他们已经玩腻了。说远了,回归正题,虽然AS608内部的算法我们是看不到的,但是它预留了一个串口和相关的串口指令集,我们可以用这些指令调用指纹算法,从而实现我们需要的功能。
2、引脚定义
三、通讯原理和通讯的流程讲解
1、通讯方法
通过给AS608串口发送特定的指令,就可以调用里面的算法,进行相应的操作。这些指令有三种格式:命令包格式,数据包格式和接收包格式。命令包是用来控制AS608的,数据包和结束包只在导出(把模块里面的指纹导出到别的设备)和导入(把其他设备的数据导入模块)指纹数据的时候用到的。
我这里以命令包格式为例,给大家讲解一下,包头是2个字节的数据,固定为0xEF01;芯片地址4个字节,默认是0xFFFFFFFF,可以后面发送指令修改;包识别是用来区分指令的类型的,如命令包固定为0x01;包长度是指这一条指令有多少个重要的数据,包长度=包长度至校验和(指令、参数或数据)的总字节数,包含校验和,但不包含包长度本身的字节数;指令就是需要AS608执行的操作,具体的定义我后面再说;参数和具体的指令有关,不同的指令,参数的长度和数值都有所不同;校验和是为了确保串口通讯过程中出现错误,导致命令出错,校验和是从包标识至校验和之间所有字节之和。
2、功能的实现及通讯的流程
我这里只详细讲解录入指纹、识别指纹和删除指纹这三个最常用的功能的实现和操作步骤。如果你们需要用到导出和导入指纹,可以留言或私信给我,我再详细讲解。
1)录入指纹
录入指纹有以下几个步骤:
第一,录入图像。当你的手指放在光学指纹窗口的时候执行这个指令,就可以把指纹的图像拍下来。
第二,生成特征。当你的指纹图像拍下来之后,调用这个指令就可以把图像中的指纹特征记录下来。AS608里面有2个缓存区可以存这个特征,我们这里先用第1个存。
第三,再次录入图像。把同一个手指放在光学指纹窗口,然后执行这个指令,再拍一个指纹图像。
第四,再次生成特征。把第二次录入的图像的指纹特征存到第2个缓存区。
第五.精确比对两枚指纹特征。录入指纹的时候为了增加准确性,最好是记录多次指纹特征,这个指令就是对录入的两个指纹特征进行比对,如果一致就说明是同一个人的同一个指纹。
第六,合并特征(生成模板)。当录入了两个指纹特征,并且两个特征是一致的时候,就可以把两个特征合并成一个指纹模板。这个指纹模板就是我们最终要录入的指纹。
第七,储存模板。当生成一个模板的时候,我们可以把这个模板存到AS608内部的Flash里面,存储的时候需要输入一个指纹ID号,这个ID其实是Flash的地址,不同的ID号,存储的位置不同,最多好像能存两三百个指纹。
2)识别指纹
第一,录入图像。这一步和录入指纹的第一步是一样的,目的是把指纹的图像拍下来。
第二,生成特征。这一步和录入指纹的第二步是一样的。指纹特征可以存到第一个缓存区也可以存到第二个缓存区。
第三,搜索指纹。调用这个指令就会将已经存在Flash里面的指纹模板和缓存区的指纹特征一一比对,如果有搜索到,会返回这个指纹的ID。要注意的是,调用的时候需要指明比对的特征是缓存区1还是缓存区2,你要选择第二步生成的特征所存储的缓存区。
3)删除指纹
删除指纹可以选择删除个别指纹或者删除所有指纹。
删除一个或几个:调用“删除模板”指令,调用的时候需要输入要删除的起始ID号和删除个数,调用之后就会把Flash里面ID号对应位置的数据清除掉。如:起始ID号是3,删除个数是4,那么就会把3,4,5,6这四个ID号对应的指纹删掉。
删除所有:调用“清空指纹库”指令即可。
4)导出指纹模板
第一,读出模板存到缓存区。
第二,上传特征或模板。
5)导入指纹模板
第一,下载特征或模板。
第二,储存模板。
6)导出图像
第一,录入图像。
第二,上传图像。
7)导入图像
第一,下载图像。
第二,生成特征。
第三,存储模板。
3、指令讲解
所有的指令在“AS60x指纹识别SOC用户手册V10”这个文件中都有说明,我这里只给大家讲解一些常用的指令。
1) 录入图像
这个指令你只需要按照下面指令包格式用串口给AS608发送数据就可以了,建议先用电脑串口助手发,理解这个通讯的过程之后再用单片机发。注意下面的数据是十六进制表示的,所以你如果用串口助手发的话要用“hex发送”。发送成功之后模块应该会有回复,根据回复的内容可以知道指令执行的状态。
如:发送:EF 01 FF FF FF FF 01 00 03 01 00 05 回复:EF 01 FF FF FF FF 07 00 03 00 00 0A
说明图像录入成功。
2) 生成特征
这个指令的发送和接收跟上面录入图像基本一致,唯一的区别是多了一个缓存区号,缓存区有两个,是用来暂存和比较两个指纹特征的。我们可以把第一次录入的图像生成特征存在缓存区1,把第二次录入的图像生成特征存在缓存区2。
3) 精确比对两枚指纹特征
这个指令可以比对2个缓存区的指纹特征。
4) 合并特征(生成模板)
把录入的指纹特征进行合并,生成模板以便于存储,合并之后的特征分别存在缓存区1和缓存区2。
5) 储存模板
当生成一个模板的时候,我们可以把这个模板存到AS608内部的Flash里面。存储的时候要选择缓存区号和指纹ID。缓存区号1和缓存区号2分别对应你两次录入的指纹,选择其中一个保存即可。ID是模块内部Flash的存储地址,不同的ID代表存储的位置不同,同一个位置只能存一个指纹,如果录入了两个不同的指纹,但是都存到同一个ID里面,那么第二次录入的指纹就会覆盖掉第一次录入的。
6) 搜索指纹
这个指令是用来识别指纹的。调用这个指令就会将已经存在Flash里面的指纹模板和缓存区的指纹特征一一比对,如果有搜索到,会返回这个指纹的ID。要注意的是,调用的时候需要指明比对的特征是在缓存区1还是缓存区2的,你在调用这个指令之前就应该把要识别的指纹的特征存到缓存区1或缓存区2。
7) 删除模板
调用的时候需要输入要删除的起始ID号和删除个数,调用之后就会把Flash里面ID号对应位置的数据清除掉。如:起始ID号是3,删除个数是4,那么就会把3,4,5,6这四个ID号对应的指纹删掉。
四、编程讲解
我这里以stm32驱动为例,我这个程序是做一个学生考勤系统的。上位机通过串口给单片机发对应的指令,单片机执行相应的操作,用到了OLED显示屏,语音播报模块,flash芯片等外设,因此里面会有一些外设相关的程序,可以忽略,你们可以只看录入指纹、识别指纹和清空指纹那部分。
1、 底层驱动
#include <string.h>
#include "delay.h"
#include "usart2.h"
#include "as608.h"
u32 AS608Addr = 0XFFFFFFFF; //默认地址
//初始化PA6为下拉输入
//读摸出感应状态(触摸感应时输出高电平信号)
void PS_StaGPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟
//初始化读状态引脚GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;//输入下拉模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
GPIO_ResetBits(GPIOA,GPIO_Pin_6); //输出低
}
//串口发送一个字节
static void MYUSART_SendData(u8 data)
{
while((USART2->SR&0X40)==0);
USART2->DR = data;
}
//发送包头
static void SendHead(void)
{
MYUSART_SendData(0xEF);
MYUSART_SendData(0x01);
}
//发送地址
static void SendAddr(void)
{
MYUSART_SendData(AS608Addr>>24);
MYUSART_SendData(AS608Addr>>16);
MYUSART_SendData(AS608Addr>>8);
MYUSART_SendData(AS608Addr);
}
//发送包标识,
static void SendFlag(u8 flag)
{
MYUSART_SendData(flag);
}
//发送包长度
static void SendLength(int length)
{
MYUSART_SendData(length>>8);
MYUSART_SendData(length);
}
//发送指令码
static void Sendcmd(u8 cmd)
{
MYUSART_SendData(cmd);
}
//发送校验和
static void SendCheck(u16 check)
{
MYUSART_SendData(check>>8);
MYUSART_SendData(check);
}
//判断中断接收的数组有没有应答包
//waittime为等待中断接收数据的时间(单位1ms)
//返回值:数据包首地址
static u8 *JudgeStr(u16 waittime)
{
char *data;
u8 str[8];
str[0]=0xef;str[1]=0x01;str[2]=AS608Addr>>24;
str[3]=AS608Addr>>16;str[4]=AS608Addr>>8;
str[5]=AS608Addr;str[6]=0x07;str[7]='\0';
USART2_RX_STA=0;
while(--waittime)
{
delay_ms(1);
if(USART2_RX_STA&0X8000)//接收到一次数据
{
USART2_RX_STA=0;
data=strstr((const char*)USART2_RX_BUF,(const char*)str);
if(data)
return (u8*)data;
}
}
return 0;
}
//录入图像 PS_GetImage
//功能:探测手指,探测到后录入指纹图像存于ImageBuffer。
//模块返回确认字
u8 PS_GetImage(void)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x03);
Sendcmd(0x01);
temp = 0x01+0x03+0x01;
SendCheck(temp);
data=JudgeStr(1500);
if(data)
ensure=data[9];
else
ensure=0xff;
return ensure;
}
//生成特征 PS_GenChar
//功能:将ImageBuffer中的原始图像生成指纹特征文件存于CharBuffer1或CharBuffer2
//参数:BufferID --> charBuffer1:0x01 charBuffer1:0x02
//模块返回确认字
u8 PS_GenChar(u8 BufferID)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x04);
Sendcmd(0x02);
MYUSART_SendData(BufferID);
temp = 0x01+0x04+0x02+BufferID;
SendCheck(temp);
data=JudgeStr(1500);
if(data)
ensure=data[9];
else
ensure=0xff;
return ensure;
}
//精确比对两枚指纹特征 PS_Match
//功能:精确比对CharBuffer1 与CharBuffer2 中的特征文件
//模块返回确认字
u8 PS_Match(void)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x03);
Sendcmd(0x03);
temp = 0x01+0x03+0x03;
SendCheck(temp);
data=JudgeStr(1500);
if(data)
ensure=data[9];
else
ensure=0xff;
return ensure;
}
//搜索指纹 PS_Search
//功能:以CharBuffer1或CharBuffer2中的特征文件搜索整个或部分指纹库.若搜索到,则返回页码。
//参数: BufferID @ref CharBuffer1 CharBuffer2
//说明: 模块返回确认字,页码(相配指纹模板)
u8 PS_Search(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x08);
Sendcmd(0x04);
MYUSART_SendData(BufferID);
MYUSART_SendData(StartPage>>8);
MYUSART_SendData(StartPage);
MYUSART_SendData(PageNum>>8);
MYUSART_SendData(PageNum);
temp = 0x01+0x08+0x04+BufferID
+(StartPage>>8)+(u8)StartPage
+(PageNum>>8)+(u8)PageNum;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
{
ensure = data[9];
p->pageID =(data[10]<<8)+data[11];
p->mathscore=(data[12]<<8)+data[13];
}
else
ensure = 0xff;
return ensure;
}
//合并特征(生成模板)PS_RegModel
//功能:将CharBuffer1与CharBuffer2中的特征文件合并生成 模板,结果存于CharBuffer1与CharBuffer2
//说明: 模块返回确认字
u8 PS_RegModel(void)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x03);
Sendcmd(0x05);
temp = 0x01+0x03+0x05;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
ensure=data[9];
else
ensure=0xff;
return ensure;
}
//储存模板 PS_StoreChar
//功能:将 CharBuffer1 或 CharBuffer2 中的模板文件存到 PageID 号flash数据库位置。
//参数: BufferID @ref charBuffer1:0x01 charBuffer1:0x02
// PageID(指纹库位置号)
//说明: 模块返回确认字
u8 PS_StoreChar(u8 BufferID,u16 PageID)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x06);
Sendcmd(0x06);
MYUSART_SendData(BufferID);
MYUSART_SendData(PageID>>8);
MYUSART_SendData(PageID);
temp = 0x01+0x06+0x06+BufferID
+(PageID>>8)+(u8)PageID;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
ensure=data[9];
else
ensure=0xff;
return ensure;
}
//删除模板 PS_DeletChar
//功能: 删除flash数据库中指定ID号开始的N个指纹模板
//参数: PageID(指纹库模板号),N删除的模板个数。
//说明: 模块返回确认字
u8 PS_DeletChar(u16 PageID,u16 N)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x07);
Sendcmd(0x0C);
MYUSART_SendData(PageID>>8);
MYUSART_SendData(PageID);
MYUSART_SendData(N>>8);
MYUSART_SendData(N);
temp = 0x01+0x07+0x0C
+(PageID>>8)+(u8)PageID
+(N>>8)+(u8)N;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
ensure=data[9];
else
ensure=0xff;
return ensure;
}
//清空指纹库 PS_Empty
//功能: 删除flash数据库中所有指纹模板
//参数: 无
//说明: 模块返回确认字
u8 PS_Empty(void)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x03);
Sendcmd(0x0D);
temp = 0x01+0x03+0x0D;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
ensure=data[9];
else
ensure=0xff;
return ensure;
}
//写系统寄存器 PS_WriteReg
//功能: 写模块寄存器
//参数: 寄存器序号RegNum:4\5\6
//说明: 模块返回确认字
u8 PS_WriteReg(u8 RegNum,u8 DATA)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x05);
Sendcmd(0x0E);
MYUSART_SendData(RegNum);
MYUSART_SendData(DATA);
temp = RegNum+DATA+0x01+0x05+0x0E;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
ensure=data[9];
else
ensure=0xff;
if(ensure==0)
printf("\r\n设置参数成功!");
else
printf("\r\n%s",EnsureMessage(ensure));
return ensure;
}
//读系统基本参数 PS_ReadSysPara
//功能: 读取模块的基本参数(波特率,包大小等)
//参数: 无
//说明: 模块返回确认字 + 基本参数(16bytes)
u8 PS_ReadSysPara(SysPara *p)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x03);
Sendcmd(0x0F);
temp = 0x01+0x03+0x0F;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
{
ensure = data[9];
p->PS_max = (data[14]<<8)+data[15];
p->PS_level = data[17];
p->PS_addr=(data[18]<<24)+(data[19]<<16)+(data[20]<<8)+data[21];
p->PS_size = data[23];
p->PS_N = data[25];
}
else
ensure=0xff;
if(ensure==0x00)
{
printf("\r\n模块最大指纹容量=%d",p->PS_max);
printf("\r\n对比等级=%d",p->PS_level);
printf("\r\n地址=%x",p->PS_addr);
printf("\r\n波特率=%d",p->PS_N*9600);
}
else
printf("\r\n%s",EnsureMessage(ensure));
return ensure;
}
//设置模块地址 PS_SetAddr
//功能: 设置模块地址
//参数: PS_addr
//说明: 模块返回确认字
u8 PS_SetAddr(u32 PS_addr)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x07);
Sendcmd(0x15);
MYUSART_SendData(PS_addr>>24);
MYUSART_SendData(PS_addr>>16);
MYUSART_SendData(PS_addr>>8);
MYUSART_SendData(PS_addr);
temp = 0x01+0x07+0x15
+(u8)(PS_addr>>24)+(u8)(PS_addr>>16)
+(u8)(PS_addr>>8) +(u8)PS_addr;
SendCheck(temp);
AS608Addr=PS_addr;//发送完指令,更换地址
data=JudgeStr(2000);
if(data)
ensure=data[9];
else
ensure=0xff;
AS608Addr = PS_addr;
if(ensure==0x00)
printf("\r\n设置地址成功!");
else
printf("\r\n%s",EnsureMessage(ensure));
return ensure;
}
//功能: 模块内部为用户开辟了256bytes的FLASH空间用于存用户记事本,
// 该记事本逻辑上被分成 16 个页。
//参数: NotePageNum(0~15),Byte32(要写入内容,32个字节)
//说明: 模块返回确认字
u8 PS_WriteNotepad(u8 NotePageNum,u8 *Byte32)
{
u16 temp;
u8 ensure,i;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(36);
Sendcmd(0x18);
MYUSART_SendData(NotePageNum);
for(i=0;i<32;i++)
{
MYUSART_SendData(Byte32[i]);
temp += Byte32[i];
}
temp =0x01+36+0x18+NotePageNum+temp;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
ensure=data[9];
else
ensure=0xff;
return ensure;
}
//读记事PS_ReadNotepad
//功能: 读取FLASH用户区的128bytes数据
//参数: NotePageNum(0~15)
//说明: 模块返回确认字+用户信息
u8 PS_ReadNotepad(u8 NotePageNum,u8 *Byte32)
{
u16 temp;
u8 ensure,i;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x04);
Sendcmd(0x19);
MYUSART_SendData(NotePageNum);
temp = 0x01+0x04+0x19+NotePageNum;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
{
ensure=data[9];
for(i=0;i<32;i++)
{
Byte32[i]=data[10+i];
}
}
else
ensure=0xff;
return ensure;
}
//高速搜索PS_HighSpeedSearch
//功能:以 CharBuffer1或CharBuffer2中的特征文件高速搜索整个或部分指纹库。
// 若搜索到,则返回页码,该指令对于的确存在于指纹库中 ,且登录时质量
// 很好的指纹,会很快给出搜索结果。
//参数: BufferID, StartPage(起始页),PageNum(页数)
//说明: 模块返回确认字+页码(相配指纹模板)
u8 PS_HighSpeedSearch(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x08);
Sendcmd(0x1b);
MYUSART_SendData(BufferID);
MYUSART_SendData(StartPage>>8);
MYUSART_SendData(StartPage);
MYUSART_SendData(PageNum>>8);
MYUSART_SendData(PageNum);
temp = 0x01+0x08+0x1b+BufferID
+(StartPage>>8)+(u8)StartPage
+(PageNum>>8)+(u8)PageNum;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
{
ensure=data[9];
p->pageID =(data[10]<<8) +data[11];
p->mathscore=(data[12]<<8) +data[13];
}
else
ensure=0xff;
return ensure;
}
//读有效模板个数 PS_ValidTempleteNum
//功能:读有效模板个数
//参数: 无
//说明: 模块返回确认字+有效模板个数ValidN
u8 PS_ValidTempleteNum(u16 *ValidN)
{
u16 temp;
u8 ensure;
u8 *data;
SendHead();
SendAddr();
SendFlag(0x01);//命令包标识
SendLength(0x03);
Sendcmd(0x1d);
temp = 0x01+0x03+0x1d;
SendCheck(temp);
data=JudgeStr(2000);
if(data)
{
ensure=data[9];
*ValidN = (data[10]<<8) +data[11];
}
else
ensure=0xff;
if(ensure==0x00)
{
printf("\r\n有效指纹个数=%d",(data[10]<<8)+data[11]);
}
else
printf("\r\n%s",EnsureMessage(ensure));
return ensure;
}
//与AS608握手 PS_HandShake
//参数: PS_Addr地址指针
//说明: 模块返新地址(正确地址)
u8 PS_HandShake(u32 *PS_Addr)
{
SendHead();
SendAddr();
MYUSART_SendData(0X01);
MYUSART_SendData(0X00);
MYUSART_SendData(0X00);
delay_ms(200);
if(USART2_RX_STA&0X8000)//接收到数据
{
if(//判断是不是模块返回的应答包
USART2_RX_BUF[0]==0XEF
&&USART2_RX_BUF[1]==0X01
&&USART2_RX_BUF[6]==0X07
)
{
*PS_Addr=(USART2_RX_BUF[2]<<24) + (USART2_RX_BUF[3]<<16)
+(USART2_RX_BUF[4]<<8) + (USART2_RX_BUF[5]);
USART2_RX_STA=0;
return 0;
}
USART2_RX_STA=0;
}
return 1;
}
//模块应答包确认码信息解析
//功能:解析确认码错误信息返回信息
//参数: ensure
const char *EnsureMessage(u8 ensure)
{
const char *p;
switch(ensure)
{
case 0x00:
p="OK";break;
case 0x01:
p="数据包接收错误";break;
case 0x02:
p="传感器上没有手指";break;
case 0x03:
p="录入指纹图像失败";break;
case 0x04:
p="指纹图像太干、太淡而生不成特征";break;
case 0x05:
p="指纹图像太湿、太糊而生不成特征";break;
case 0x06:
p="指纹图像太乱而生不成特征";break;
case 0x07:
p="指纹图像正常,但特征点太少(或面积太小)而生不成特征";break;
case 0x08:
p="指纹不匹配";break;
case 0x09:
p="没搜索到指纹";break;
case 0x0a:
p="特征合并失败";break;
case 0x0b:
p="访问指纹库时地址序号超出指纹库范围";
case 0x10:
p="删除模板失败";break;
case 0x11:
p="清空指纹库失败";break;
case 0x15:
p="缓冲区内没有有效原始图而生不成图像";break;
case 0x18:
p="读写 FLASH 出错";break;
case 0x19:
p="未定义错误";break;
case 0x1a:
p="无效寄存器号";break;
case 0x1b:
p="寄存器设定内容错误";break;
case 0x1c:
p="记事本页码指定错误";break;
case 0x1f:
p="指纹库满";break;
case 0x20:
p="地址错误";break;
default :
p="模块返回确认码有误";break;
}
return p;
}
2、录指纹函数
//录入指纹
u16 Add_FR(u16 ID)
{
u8 i=0,ensure,processnum=0;
int a=1;
Audio(2); //播放"开始录入指纹"
delay_ms(1000);
OLED_Clear();
OLED_ShowCHinese(28,0,14);
OLED_ShowCHinese(48,0,15);
OLED_ShowCHinese(68,0,16);
OLED_ShowCHinese(88,0,17); //显示"录入指纹"
while(a)
{
switch (processnum)
{
case 0:
i++;
Audio(3);
OLED_ShowCHinese(28,2,18);
OLED_ShowCHinese(48,2,25);
OLED_ShowCHinese(68,2,26);
OLED_ShowCHinese(88,2,27); //显示"请按手指"
delay_ms(50);
if(PS_Sta==1)
{
ensure=PS_GetImage(); //录入图像
if(ensure==0x00)
{
ensure=PS_GenChar(CharBuffer1); //生成特征
if(ensure==0x00)
{
Audio(4); //播报"开始录入指纹"
OLED_ShowCHinese(28,2,16);
OLED_ShowCHinese(48,2,17);
OLED_ShowCHinese(68,2,28);
OLED_ShowCHinese(88,2,29); //显示"指纹正确"
delay_ms(1000);
i=0;
processnum=1; //跳到第二步
}
else
{
OLED_ShowCHinese(28,2,16);
OLED_ShowCHinese(48,2,17);
OLED_ShowCHinese(68,2,8);
OLED_ShowCHinese(88,2,9); //显示"指纹错误"
}
}
}
break;
case 1:
i++;
Audio(5); //播报"请再按一次手指"
OLED_ShowCHinese(8,2,30);
OLED_ShowCHinese(28,2,25);
OLED_ShowCHinese(48,2,31);
OLED_ShowCHinese(68,2,32);
OLED_ShowCHinese(88,2,26);
OLED_ShowCHinese(108,2,27); //显示"再按一次手指"
if(PS_Sta==1)
{
ensure=PS_GetImage(); //录入图像
if(ensure==0x00)
{
ensure=PS_GenChar(CharBuffer2); //生成特征
if(ensure==0x00)
{
Audio(4);
OLED_ShowString(8,2," ",16);
OLED_ShowCHinese(28,2,16);
OLED_ShowCHinese(48,2,17);
OLED_ShowCHinese(68,2,28);
OLED_ShowCHinese(88,2,29); //显示"指纹正确"
i=0;
processnum=2; //跳到第三步
}
else
{
OLED_ShowCHinese(28,2,16);
OLED_ShowCHinese(48,2,17);
OLED_ShowCHinese(68,2,8);
OLED_ShowCHinese(88,2,9); //显示"指纹错误"
}
}
}
break;
case 2:
i++;
ensure=PS_Match(); //对比两次指纹
if(ensure==0x00) //两次指纹一致
{
processnum=3; //跳到第四步
}
else //两次指纹不一致
{
Audio(19); //播报"两次指纹不一样,请重新录入"
delay_ms(50);
OLED_ShowCHinese(8,2,33);
OLED_ShowCHinese(28,2,32);
OLED_ShowCHinese(48,2,16);
OLED_ShowCHinese(68,2,17);
OLED_ShowCHinese(88,2,34);
OLED_ShowCHinese(108,2,35); //显示"两次指纹不同"
OLED_ShowCHinese(8,4,18);
OLED_ShowCHinese(28,4,36);
OLED_ShowCHinese(48,4,37);
OLED_ShowCHinese(68,4,14);
OLED_ShowCHinese(88,4,15); //"请重新录入"
delay_ms(1500);
OLED_ShowString(8,2," ",16);
OLED_ShowString(8,4," ",16);
i=0;
processnum=0; //跳回第一步
}
break;
case 3:
ensure=PS_RegModel(); //产生一个指纹模板
if(ensure==0x00) //生成指纹模板成功
{
processnum=4; //跳到第五步
}
else
{
processnum=0;
}
break;
case 4:
i++;
ensure=PS_StoreChar(CharBuffer2,ID); //储存模板
if(ensure==0x00)
{
Audio(6);
MYUSART_SendData1(0x3A);
MYUSART_SendData1(0x01);
MYUSART_SendData1(0x00);
MYUSART_SendData1(0x01);
MYUSART_SendData1(0x01);
MYUSART_SendData1(0x00);
MYUSART_SendData1(0x04);
MYUSART_SendData1(0x3A);
OLED_ShowString(8,2," ",16);
OLED_ShowCHinese(28,2,14);
OLED_ShowCHinese(48,2,15);
OLED_ShowCHinese(68,2,6);
OLED_ShowCHinese(88,2,7); //显示"录入成功"
ZW_ID[0]=ID;
SPI_Flash_Write((u8*)ZW_ID,FLASH_SIZE-100,1); //写入当前ID
a=0;
break;
}
else
{
processnum=0;
}
break;
}
delay_ms(1000);
if(i>5)
{
i=0;
a=0;
Audio(7); //播报"录入失败"
MYUSART_SendData1(0x3A);
MYUSART_SendData1(0x01);
MYUSART_SendData1(0x00);
MYUSART_SendData1(0x01);
MYUSART_SendData1(0x00);
MYUSART_SendData1(0x00);
MYUSART_SendData1(0x04);
MYUSART_SendData1(0x3A);
OLED_ShowCHinese(28,2,14);
OLED_ShowCHinese(48,2,15);
OLED_ShowCHinese(68,2,4);
OLED_ShowCHinese(88,2,5); //显示"录入失败"
delay_ms(1000);
OLED_ShowString(28,2," ",16);
}
}
}
3、刷指纹
void press_FR(void)
{
SearchResult seach;
u8 ensure;
if(audio_flag)
{
audio_flag=0;
Audio(11);//播报“开始签到,请按手指”
}
OLED_ShowString(0,0," ",16);
OLED_ShowString(104,0," ",16);
OLED_ShowString(0,2," ",16);
OLED_ShowString(0,4," ",16);
OLED_ShowString(0,6," ",16);
OLED_ShowCHinese(28,0,10);
OLED_ShowCHinese(48,0,11);
OLED_ShowCHinese(68,0,12);
OLED_ShowCHinese(88,0,13); //显示"开始签到"
// printf("开始签到\n");
if(PS_Sta==1)
{
ensure=PS_GetImage();
if(ensure==0x00)//获取图像
{
OLED_ShowCHinese(28,2,69);
OLED_ShowCHinese(48,2,70);
OLED_ShowCHinese(68,2,71);
OLED_ShowString(88,2,"...",16); //显示"比对中..."
// printf("获取图像成功 \n");
ensure=PS_GenChar(CharBuffer1);
if(ensure==0x00) //生成特征
{
// printf("生成特征成功 \n");
ensure=PS_HighSpeedSearch(CharBuffer1,0,300,&seach);
if(ensure==0x00)//搜索成功
{
// printf("搜索指纹成功\n ");//搜索指纹成功
// delay_ms(100);
// sprintf(str,"Match ID:%d Match score:%d\n",seach.pageID,seach.mathscore);//显示匹配指纹的ID和分数
// printf(" 匹配指纹的ID和分数 %s\n",str);
// printf("%d",seach.pageID);
// OLED_ShowNum(0,6,seach.pageID,6,16);
Up_stu_num(seach.pageID);
ID=seach.pageID;
OLED_ShowCHinese(28,4,12);
OLED_ShowCHinese(48,4,13);
OLED_ShowCHinese(68,4,6);
OLED_ShowCHinese(88,4,7); //显示"签到成功"
Audio(12);//播报“签到成功”
delay_ms(1000);
}
else
{
Audio(13);
OLED_ShowCHinese(28,4,12);
OLED_ShowCHinese(48,4,13);
OLED_ShowCHinese(68,4,4);
OLED_ShowCHinese(88,4,5); //显示"签到失败"
delay_ms(1000);
// ShowErrMessage(ensure);
}
// delay_ms(50);
}
// else
// ShowErrMessage(ensure);
// delay_ms(50);
}
}
}
4、清空指纹
这个函数在底层驱动里面有,直接调用即可
PS_Empty();
5、上传和下载指纹
如果没有用到这个功能可以跳过
u16 Up_FR(u16 ID) //上传指纹
{
u8 ensure;
Audio(15);
OLED_Clear();
OLED_ShowCHinese(28,0,60);
OLED_ShowCHinese(48,0,61);
OLED_ShowCHinese(68,0,16);
OLED_ShowCHinese(88,0,17); //显示"上传指纹"
ensure=PS_LoadChar(CharBuffer2,ID); //读出模板
if(ensure==0x00)
{
PS_UpChar(CharBuffer2); //上传特征
if(ensure==0x00)
{
// printf("上传指纹成功");
Audio(16);
OLED_ShowCHinese(8,2,60);
OLED_ShowCHinese(28,2,61);
OLED_ShowCHinese(48,2,16);
OLED_ShowCHinese(68,2,17);
OLED_ShowCHinese(88,2,6);
OLED_ShowCHinese(108,2,7); //显示"上传指纹成功"
}
else
{
Audio(17);
OLED_ShowCHinese(8,2,60);
OLED_ShowCHinese(28,2,61);
OLED_ShowCHinese(48,2,16);
OLED_ShowCHinese(68,2,17);
OLED_ShowCHinese(88,2,4);
OLED_ShowCHinese(108,2,5); //显示"上传指纹失败"
delay_ms(1000);
}
}
else
{
OLED_ShowCHinese(8,2,60);
OLED_ShowCHinese(28,2,61);
OLED_ShowCHinese(48,2,16);
OLED_ShowCHinese(68,2,17);
OLED_ShowCHinese(88,2,6);
OLED_ShowCHinese(108,2,7); //显示"上传指纹失败"
delay_ms(1000);
}
}
u16 Down_FR(u16 ID) //下载指纹
{
u8 ensure;
u16 i;
Audio(8);
OLED_Clear();
OLED_ShowCHinese(28,0,58);
OLED_ShowCHinese(48,0,59);
OLED_ShowCHinese(68,0,16);
OLED_ShowCHinese(88,0,17); //显示"下载指纹"
ensure=PS_DownChar(CharBuffer2);
if(ensure==0x00)
{
for(i=4;i<838;i++)
{
while((USART2->SR&0X40)==0);
USART2->DR = USART_RX_BUF[i];
}
ensure=PS_StoreChar(CharBuffer2,ID);
if(ensure==0x00)
{
MYUSART_SendData1(0x3A);
MYUSART_SendData1(0x03);
MYUSART_SendData1(0x00);
MYUSART_SendData1(0x01);
MYUSART_SendData1(0x01);
MYUSART_SendData1(0x00);
MYUSART_SendData1(0x03);
MYUSART_SendData1(0x3A);
// printf("下载指纹成功");
ZW_ID[0]=ID;
SPI_Flash_Write((u8*)ZW_ID,FLASH_SIZE-100,1); //写入当前ID
OLED_ShowCHinese(8,2,58);
OLED_ShowCHinese(28,2,59);
OLED_ShowCHinese(48,2,16);
OLED_ShowCHinese(68,2,17);
OLED_ShowCHinese(88,2,6);
OLED_ShowCHinese(108,2,7); //显示"下载指纹成功"
delay_ms(1000);
show_main_page();
}
else
{
Audio(10);
OLED_ShowCHinese(8,2,58);
OLED_ShowCHinese(28,2,59);
OLED_ShowCHinese(48,2,16);
OLED_ShowCHinese(68,2,17);
OLED_ShowCHinese(88,2,4);
OLED_ShowCHinese(108,2,5); //显示"下载指纹失败"
}
}
}
6、main函数
我这份程序是做一个学生签到系统,上位机通过串口给单片机发送指令,单片机执行相应的操作
int main(void)
{
u16 t=0;
delay_init();
Audio_Init();//语音模块初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
TIM4_Int_Init(9999,7199);//10Khz的计数频率,1s定时中断
uart_init(115200); //初始化串口1波特率为115200,用于与上位机通讯
usart2_init(57600);//初始化串口2,用于与指纹模块通讯
PS_StaGPIO_Init(); //初始化FR读状态引脚
OLED_Init(); //OLED初始化
OLED_Clear();//OLED清屏
SPI_Flash_Init(); //外接FLASH芯片W25Q64初始化,用来存学生学号(指纹是存到AS608芯片里面的)
LED_Init();
while(SPI_Flash_ReadID()!=W25Q64) //检测W25Q64
{
delay_ms(100);
LED0=!LED0; //DS0闪烁
}
while(PS_HandShake(&AS608Addr)) //与AS608模块握手
{
OLED_ShowCHinese(8,0,0);
OLED_ShowCHinese(28,0,1);
OLED_ShowCHinese(48,0,2);
OLED_ShowCHinese(68,0,3);
OLED_ShowString(88,0,"...",16); //显示"正在连接..."
}
OLED_ShowCHinese(8,0,2);
OLED_ShowCHinese(28,0,3);
OLED_ShowCHinese(48,0,6);
OLED_ShowCHinese(68,0,7);
OLED_ShowString(88,0," ",16); //显示"连接成功"
// printf("\n连接成功\n");
OLED_Clear();
show_main_page();//OLED显示主界面
while(1)
{
command_rx();//等待串口接收指令(我这里是用上位机给单片机串口发指令的)
delay_ms(100);
if(command==0x01) //收到录入指纹命令
{
command=0;
SPI_Flash_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,1);//从倒数第100个地址处开始,写入SIZE长度的数据
SPI_Flash_Read(ZW_ID,FLASH_SIZE-100,1); //读取当前已存的最大指纹ID号
ID=ZW_ID[0]+1;
Add_FR(ID); //录指纹
show_main_page(); //显示首页界面
}
else if(command==0x02) //上传指纹
{
command=0;
SPI_Flash_Read(ZW_ID,FLASH_SIZE-100,1); //读取当前已存的最大指纹ID号
ID=ZW_ID[0];
// MYUSART_SendData1(0x3A);
// MYUSART_SendData1(0x02);
// MYUSART_SendData1(0x03);
// MYUSART_SendData1(0x42);
Up_FR(ID); //获取指纹信息
for(t=0;t<841;t++)
{
while((USART1->SR&0X40)==0)
;
USART1->DR = zw_up[t]; //向上位机上传指纹信息
}
// MYUSART_SendData1(0x03);
// MYUSART_SendData1(0x45);
// MYUSART_SendData1(0x3A);
show_main_page();
}
else if(command==0x03) //下载指纹
{
command=0;
SPI_Flash_Read(ZW_ID,FLASH_SIZE-100,1); //读取当前已存的最大指纹ID号
ID=ZW_ID[0]+1;
Down_FR(ID); //下载指纹
show_main_page();
}
else if(command==0x04) //上传学号/开始签到
{
press_FR();
}
else if(command==0x05) //下载学号
{
command=0;
Down_stu_num(ID);
show_main_page();
}
else if(command==0x06) //结束签到
{
command=0;
Audio(14);
OLED_Clear();
OLED_ShowCHinese(28,0,12);
OLED_ShowCHinese(48,0,13);
OLED_ShowCHinese(68,0,62);
OLED_ShowCHinese(88,0,63);//显示"结束签到"
delay_ms(1500);
show_main_page();
}
else if(command==0x07) //清空数据
{
command=0;
PS_Empty();
ZW_ID[0]=0;
SPI_Flash_Write((u8*)ZW_ID,FLASH_SIZE-100,1); //写入当前ID
OLED_Clear();
MYUSART_SendData1(0x3A);
MYUSART_SendData1(0x07);
MYUSART_SendData1(0x00);
MYUSART_SendData1(0x01);
MYUSART_SendData1(0x00);
MYUSART_SendData1(0x04);
MYUSART_SendData1(0x3A);
OLED_ShowCHinese(12,4,64);
OLED_ShowCHinese(30,4,65);
OLED_ShowCHinese(48,4,66);
OLED_ShowCHinese(66,4,67);
OLED_ShowCHinese(84,4,68);//"已清空数据"
Audio(18); //播放"播放已清空数据"
delay_ms(1500);
show_main_page();
}
}
}