AS608光学指纹模组编程和应用详解

一、前言

上次讲解了两个简单实用的传感器,反馈很多,能够帮助到大家,我也很开心。所以今天继续给大家介绍一个实用易上手的传感器,在这里有些同学可能就会质疑了,指纹识别哪里简单了?别急,容我细细道来,看完整篇文章,你可能就会觉得,不过如此嘛。
需要程序源码,原理图,数据手册等资料可以到文章底部的链接自行下载。因为这个是我大学做毕设时用到的一个模块,内容比较多,有些细节一时记不清了,所以有些地方可能讲的不是特别深入,如果你们有什么地方看不懂,可以留言和私信给我,我再给你们解答。
如果能够帮助到你,请点赞+关注,又快到做毕设的日子了,我后面也会继续写更多的博文,希望能够帮到你们。

二、芯片介绍

1、芯片简介
AS608是一个集成的光学指纹芯片,内部有指纹算法,其实指纹识别最难的地方在于算法,目前的算法是前人不断开发完善得到的,所以在前几年,算法都是某些大企业独有的技术,他们只卖芯片不卖算法。当然现在的话,光学指纹的算法也逐渐放出来了,因为现在用的更多的是半导体,超声波指纹识别,光学指纹他们已经玩腻了。说远了,回归正题,虽然AS608内部的算法我们是看不到的,但是它预留了一个串口和相关的串口指令集,我们可以用这些指令调用指纹算法,从而实现我们需要的功能。

2、引脚定义

android aosp Settings指纹菜单的显示 安卓指纹模块_串口


android aosp Settings指纹菜单的显示 安卓指纹模块_单片机_02

android aosp Settings指纹菜单的显示 安卓指纹模块_经验分享_03

三、通讯原理和通讯的流程讲解

1、通讯方法

通过给AS608串口发送特定的指令,就可以调用里面的算法,进行相应的操作。这些指令有三种格式:命令包格式,数据包格式和接收包格式。命令包是用来控制AS608的,数据包和结束包只在导出(把模块里面的指纹导出到别的设备)和导入(把其他设备的数据导入模块)指纹数据的时候用到的。

android aosp Settings指纹菜单的显示 安卓指纹模块_经验分享_04


我这里以命令包格式为例,给大家讲解一下,包头是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

说明图像录入成功。

android aosp Settings指纹菜单的显示 安卓指纹模块_经验分享_05


2) 生成特征

这个指令的发送和接收跟上面录入图像基本一致,唯一的区别是多了一个缓存区号,缓存区有两个,是用来暂存和比较两个指纹特征的。我们可以把第一次录入的图像生成特征存在缓存区1,把第二次录入的图像生成特征存在缓存区2。

android aosp Settings指纹菜单的显示 安卓指纹模块_缓存_06


3) 精确比对两枚指纹特征

这个指令可以比对2个缓存区的指纹特征。

android aosp Settings指纹菜单的显示 安卓指纹模块_经验分享_07


4) 合并特征(生成模板)

把录入的指纹特征进行合并,生成模板以便于存储,合并之后的特征分别存在缓存区1和缓存区2。

android aosp Settings指纹菜单的显示 安卓指纹模块_缓存_08


5) 储存模板

当生成一个模板的时候,我们可以把这个模板存到AS608内部的Flash里面。存储的时候要选择缓存区号和指纹ID。缓存区号1和缓存区号2分别对应你两次录入的指纹,选择其中一个保存即可。ID是模块内部Flash的存储地址,不同的ID代表存储的位置不同,同一个位置只能存一个指纹,如果录入了两个不同的指纹,但是都存到同一个ID里面,那么第二次录入的指纹就会覆盖掉第一次录入的。

android aosp Settings指纹菜单的显示 安卓指纹模块_数据_09


6) 搜索指纹

这个指令是用来识别指纹的。调用这个指令就会将已经存在Flash里面的指纹模板和缓存区的指纹特征一一比对,如果有搜索到,会返回这个指纹的ID。要注意的是,调用的时候需要指明比对的特征是在缓存区1还是缓存区2的,你在调用这个指令之前就应该把要识别的指纹的特征存到缓存区1或缓存区2。

android aosp Settings指纹菜单的显示 安卓指纹模块_经验分享_10


7) 删除模板

调用的时候需要输入要删除的起始ID号和删除个数,调用之后就会把Flash里面ID号对应位置的数据清除掉。如:起始ID号是3,删除个数是4,那么就会把3,4,5,6这四个ID号对应的指纹删掉。

android aosp Settings指纹菜单的显示 安卓指纹模块_经验分享_11

四、编程讲解

我这里以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();
		}		
	}		
}