AS608指纹模块高级功能实现(一):底层数据传输——指纹特征库上传给上位机
一、写在前面
最近突发其想,想利用两个AS608模块实现数据共享,也就是利用其中一个录入指纹,另外一个也能够读取到录入的指纹。但是笔者找遍全网,也只有实现了基本的录入、删除、验证功能的例程,以及一个具有全功能的上位机,如果要实现数据共享,那么必须要实现文件底层数据的传输,而上位机根本无法看到内部的具体实现。于是,笔者还是决定通过串口调试、翻阅手册,探究AS608数据传输的实现方法。
AS608作为一款比较成熟的指纹芯片,SoC已经封装好了各种指令,我们只要发送我们所需的指令包,便可完成一系列的操作。笔者使用了STC15系列单片机进行试验,加入了LCD辅助显示程序运行情况,本文主要目的还是在于探讨,对于一些交互并没有十分重视,对于AS608的基本操作也没有过多的介绍,希望大家理解。
二、实现目标、主要难点
目标
使用串口调试助手,获得芯片一份完整指纹特征模板,并生成.mb文件
难点
滤除包头包尾,UART串口通讯
三、芯片通讯方式
1、通讯方法
通过给AS608串口发送特定的指令,就可以调用里面的算法,进行相应的操作。这些指令有三种格式:命令包格式,数据包格式和接收包格式。命令包是用来控制AS608的,数据包和结束包只在导出(把模块里面的指纹导出到别的设备)和导入(把其他设备的数据导入模块)指纹数据的时候用到的。
上图介绍了芯片三种包的格式,其中数据包和结束包是本文重点用到的,也是最容易被大家忽略掉的。
四、实验流程
一、芯片配置
也可以自行设置,涉及到串口通信问题,笔者串口1连接上位机,串口2连接AS608模块。
二、指纹录入,生成出该指纹的特征模板存放于Buffer1
emmm自己画的,忽视水印
两次按指纹完成一次指纹录入。
三、模板保存于缓冲区并通过串口发送至上位机
关键在于滤除包头包尾,包头使用逐层判断,判断包头后开始接收数据,包头后面跟64字节有效+2字节校验和,所以我们只需要for循环接收64字节,后面2字节不管,完成后存放特征寄存数组,随后进入下一次数据包头判断。
五、主要实现代码
串口2:UART2.h
#ifndef __UART2_H
#include <string.h>
#include "uart.h"
#define __UART2_H
#define uchar unsigned char
#define uint unsigned int
#define S2RI 0x01 //串口2接收中断请求标志位
#define S2TI 0x02 //串口2发送中断请求标志位
#define S2RB8 0x04
#define S2TB8 0x08
#define UART2_MAX_RECV_LEN 768 //最大接收缓存字节数
#define UART2_MAX_SEND_LEN 1028 //最大发送缓存字节数
//串口接收缓存区
uchar xdata UART2_RX_BUF[24]; //命令接收缓冲,不需要太大
uchar xdata UART2_TX_BUF[UART2_MAX_SEND_LEN]; //发送缓冲,最大USART3_MAX_SEND_LEN字节
uchar xdata CHAR_TEMP_BUF[64];
uchar xdata CHARBUF[800];
uint UART2_RX_STA = 0; //命令接收缓冲指针
uint CHAR_TEMP_STA = 0; //数据包缓冲区临时指针
uint CHARBUF_STA = 0; //特征模板存储指针
uchar Head_Flag = 0; //数据包包头检测标志
//串口2初始化
void UART2_Init()
{
TMOD |= 0x20; // 0010 0000 定时器2工作于方式2(8位自动重装方式)
T2H = 0xfd; // 波特率:37600 /22.1184MHZ
T2L = 0xc0; // 波特率:37600 /22.1184MHZ
AUXR = 0x14; // 使用定时器2作为波特率发生器
S2CON = 0x50;
// 设置中断
IE2 =0x01;
EA = 1;
}
//串口2发送一个字节
void UART2_SendData(uchar c)
{
S2BUF = c;
while(!(S2CON&S2TI)); //若S2TI=0,在此等待
S2CON&=~S2TI; //S2TI=0
}
void show_rx_buff(void)
{
uint i=0;
for(i;i<UART2_MAX_RECV_LEN;i++)
{
UART_Send_Byte(CHARBUF[i]);
}
}
/************串行口2中断处理函数*************/
void UART2_Interrupt(void) interrupt 8
{
uchar Res;
if(S2CON&S2RI)
{
S2CON&=~S2RI;
Res=S2BUF;
UART_Send_Byte(Res);
if(UART2_RX_STA<24)
{
UART2_RX_BUF[UART2_RX_STA++]=Res; //命令存储
}else{}
if(Head_Flag == 0) //包头检测,采用了最传统的判断算法
{
if(Res == 0xEF)
Head_Flag++;
else
Head_Flag = 0;
}
else if(Head_Flag == 1)
{
if(Res == 0x01)
Head_Flag++;
else
Head_Flag = 0;
}
else if(Head_Flag == 2)
{
if(Res == 0xFF)
Head_Flag++;
else
Head_Flag = 0;
}
else if(Head_Flag == 3)
{
if(Res == 0xFF)
Head_Flag++;
else
Head_Flag = 0;
}
else if(Head_Flag == 4)
{
if(Res == 0xFF)
Head_Flag++;
else
Head_Flag = 0;
}
else if(Head_Flag == 5)
{
if(Res == 0xFF)
Head_Flag++;
else
Head_Flag = 0;
}
else if(Head_Flag == 6)
{
if(Res == 0x02||Res == 0x08) //包括了结束包
Head_Flag++;
else
Head_Flag = 0;
}
else if(Head_Flag == 7)
{
if(Res == 0x00)
Head_Flag++;
else
Head_Flag = 0;
}
else if(Head_Flag == 8)
{
if(Res == 0x42)
Head_Flag++;
else
Head_Flag = 0;
}
else if(Head_Flag == 9)
{
if(CHAR_TEMP_STA<64)
CHAR_TEMP_BUF[CHAR_TEMP_STA++]=Res;
else if(CHAR_TEMP_STA==64)
{
for(CHAR_TEMP_STA=0;CHAR_TEMP_STA<64;CHAR_TEMP_STA++)
{
CHARBUF[CHARBUF_STA++]=CHAR_TEMP_BUF[CHAR_TEMP_STA];
}
CHAR_TEMP_STA=0;
Head_Flag=0;
}
}
}
}
#endif
AS608.h
#ifndef __AS608_H
#define __AS608_H
#include <string.h>
#include "UART.h"
#include "UART2.h"
#include "lcd.h"
#define uchar unsigned char
#define uint unsigned int
#define ulong unsigned long
#define CharBuffer1 0x01
#define CharBuffer2 0x02
ulong AS608Addr = 0XFFFFFFFF; //??????
char str2[6]={0};
void Delay_Ms(uint ms)
{
uint a,b,c;
for(a=ms;a>0;a--)
for(b=10;b>0;b--)
for(c=85;c>0;c--);
}
//发送包头
static void SendHead(void)
{
UART2_SendData(0xEF);
UART2_SendData(0x01);
}
//发送地址
static void SendAddr(void)
{
UART2_SendData(AS608Addr>>24);
UART2_SendData(AS608Addr>>16);
UART2_SendData(AS608Addr>>8);
UART2_SendData(AS608Addr);
}
//发送包标识
static void SendFlag(uchar flag)
{
UART2_SendData(flag);
}
//发送包长度
static void SendLength(int length)
{
UART2_SendData(length>>8);
UART2_SendData(length);
}
//发送指令码
static void Sendcmd(uchar cmd)
{
UART2_SendData(cmd);
}
//发送BuffID
static void SendBuffID(uchar BuffID)
{
UART2_SendData(BuffID);
}
//发送校验和
static void SendCheck(uint check)
{
UART2_SendData(check>>8);
UART2_SendData(check);
}
//判断中断接收数组有没有应答包
//waittime为等待中断接收数组等待时间
//返回值:数据包首地址
static uchar *JudgeStr(uint waittime)
{
// uint temp;
char *rdata;
uchar str[8];
str[0]=0xef;str[1]=0x01;str[2]=0xFF;
str[3]=0xFF;str[4]=0xFF;
str[5]=0xFF;str[6]=0x07;str[7]='\0';
UART2_RX_STA=0;
while(--waittime)
{
Delay_Ms(1000);
if(1)
{
UART2_RX_STA=0;
rdata=strstr((const char*)UART2_RX_BUF,(const char*)str);
if(rdata)
return (uchar*)rdata;
}
}
return 0;
}
//按指纹,指令号:01
uchar PS_GetImage(void)
{
uint temp;
uchar ensure;
uchar *rdata;
SendHead();
SendAddr();
SendFlag(0x01);
SendLength(0x03);
Sendcmd(0x01);
temp = 0x01+0x03+0x01;
SendCheck(temp);
rdata=JudgeStr(8000);
if(rdata)
ensure=rdata[9];
else
ensure=0xff;
return ensure;
}
//生成特征,指令号:02
uchar PS_GenChar(uchar BufferID)
{
uint temp;
uchar ensure;
uchar *rdata;
SendHead();
SendAddr();
SendFlag(0x01);
SendLength(0x04);
Sendcmd(0x02);
UART2_SendData(BufferID);
temp = 0x01+0x04+0x02+BufferID;
SendCheck(temp);
rdata=JudgeStr(8000);
if(rdata)
ensure=rdata[9];
else
ensure=0xff;
return ensure;
}
//合成buffer1和buffer2中的特征,指令号:05
uchar PS_RegModel(void)
{
uint temp;
uchar ensure;
uchar *rdata;
SendHead();
SendAddr();
SendFlag(0x01);
SendLength(0x03);
Sendcmd(0x05);
temp = 0x01+0x03+0x05;
SendCheck(temp);
rdata=JudgeStr(8000);
if(rdata)
ensure=rdata[9];
else
ensure=0xff;
return ensure;
}
//上传特征函数,指令号:08
void PS_Upchar()
{
uint temp;
//uchar ensure;
//uchar *rdata;
SendHead();
SendAddr();
SendFlag(0x01);
SendLength(0x04);
Sendcmd(0x08);
UART2_SendData(0x01);
temp = 0x01+0x04+0x08+0x01;
SendCheck(temp);
/*rdata=JudgeStr(8000);
if(rdata)
ensure=rdata[9];
else
ensure=0xff;
return ensure;*/
}
//函数功能:完成两次录入指纹并生成特征模板存于Buffer1和Buffer2
void Create_FR_Char(void)
{
uchar i,ensure ,processnum=0;
uint ID_NUM=0;
while(1)
{
switch (processnum)
{
case 0:
i++;
LCD_show(0x00,"please press1");
ensure=PS_GetImage();
if(ensure==0x00)
{
ensure=PS_GenChar(CharBuffer1);
if(ensure==0x00)
{
Delay_Ms(1);
LCD_show(0x00,"Press1 Finish");
Delay_Ms(100);
i=0;
processnum=1;//跳到第二步
}else {};
}else {};
break;
case 1:
i++;
LCD_show(0x00,"try again");
Delay_Ms(100);
ensure=PS_GetImage();
if(ensure==0x00)
{
ensure=PS_GenChar(CharBuffer2);//????
if(ensure==0x00)
{
Delay_Ms(1);
LCD_show(0x00,"Press2 Finish");
Delay_Ms(100);
i=0;
processnum=2;//跳往第三步
}else {};
}else {};
break;
case 2:
LCD_show(0x00,"Creating Press");
Delay_Ms(500);
ensure=PS_RegModel();
if(ensure==0x00)
{
LCD_show(0x00,"Creat Success");
return ;
}
else
{
LCD_show(0x00,"Cmp defeat");
LCD_show(0x40,"Try again");
processnum=0;//返回第一步
}
Delay_Ms(1000);
break;
}
Delay_Ms(400);
if(i==10)//超过5次未按手指退出
break;
}
}
#endif
main.c
#include "STC15W4k.h"
#include "UART.h"
#include "UART2.h"
#include "AS608.h"
#include "lcd.h"
sbit key1 = P2^4;
sbit key2 = P2^3;
void port_mode() //STC15系列引口有三种模式,此为设置成开漏模式
{
P0M1=0x00; P0M0=0x00;P1M1=0x00; P1M0=0x00;P2M1=0x00; P2M0=0x00;P3M1=0x00; P3M0=0x00;
P4M1=0x00; P4M0=0x00;P5M1=0x00; P5M0=0x00;P6M1=0x00; P6M0=0x00;P7M1=0x00; P7M0=0x00;
}
void main()
{
port_mode();
Init_LCD();
UART_Init();
UART2_Init();
UART_Send_Str("初始化完毕");
LCD_show(0x00,"Init Success");
while(1)
{
if(key1==0)
{
Delay_Ms(5);
if(key1==0)
{
Create_FR_Char();
PS_Upchar();
}
}
if(key2==0)
{
Delay_Ms(5);
if(key2==0)
{
LCD_show(0x00,"Sending");
show_rx_buff();
}
}
}
}
六、实验结果
为操作方便,加入了按键,按键1完成录入,显示Creat Success,在完成第一步后按下按键2,给串口调试助手发送特征模板数据。实验结果以及对比如图:
这是未剔除包头包尾的数据,可以看到包头结尾的0x42表示后面有64字节有效数据+2字节的校验和数据。
剔除后的串口数据。
为了更直观看到对比,笔者将原数据剔除包头包尾后进行对比,看一看到是完全一样的。
真的真的是一个个手动删除!(加鸡腿)
最后,对数据进行打包成.mb文件,使用上位机上传指纹特征到芯片,同时进行指纹验证。
对比通过,大功告成!
七、后记
本实验换言之就是把数据拎了出来又放回去,但是我们实现了在底层进行了数据操作,这样的操作对于我们进行文件转移、数据共享是非常有意义的。同时也加深了对文件组成原理、通信原理的理解。
本实验的代码也有不足的地方,算法有待优化,也有部分bug,希望借此抛砖引玉,欢迎大家提供意见。
最后还要感谢几位在本实验过程中给予过帮助的朋友。
源代码下载:https://pan.baidu.com/s/1hfwgWqE66ruNBTlR7FC3EQ
提取码:8fb7