基础版本做完了,感觉很平淡所以这次来了一个很好玩的模块——AS608指纹模块
该模块的通信方式是串口,驱动方式使用串口指令,模块会根据指令执行。基本原理是内置的图像识别技术,根据存储的指纹特征点和需要对比的指纹进行对比。
接线顺序从左到右依次是:
Vi——接开发板3.3V
Tx——接开发板PA3
Rx——接开发板PA2
GND——接开发板GND
WAK——接开发板PB12
Vi——接开发板3.3V或悬空
1)录入指纹图像 PS_GetImage
2)生成特征 PS_GenChar
3)搜索指纹 PS_Search
4) 合并特征(生成模板) PS_RegModel
5)储存模板 PS_StoreChar
6)删除模板 PS_DeletChar
该模块采用的是串口指令的方式,使用串口与之连接然后发送16进制的数据指令,模块会根据数据指令完成对应的任务。
录入指纹数据的步骤
1)删除指定缓存区的数据
2)录入指纹图像
3) 生成特征放在buffer1中
4)录取指纹图像
5)生成特征放在buffer2中
6)合并特征模板
7)存储特征模板
搜索指纹数据的步骤
1)录取指纹数据
2)生成特征放在buffer1中
3)搜索指纹数据
由于AS608使用的是串口驱动,所以第一步需要初始化串口和串口中断;
我用的是串口二
/************************AS608.h***************************/
#ifndef __AS608__H__
#define __AS608__H__
#define USART2_MAX_RECV_LEN 400 //最大接收缓存字节数
extern u8 USART2_RX_BUF[USART2_MAX_RECV_LEN];
extern int counts ;
#define AS608USART USART2
#endif
/**************************AS608.c**************************/
//串口二初始化
void AS608_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(LORA_GPIO_USART_CLOCK, ENABLE);
RCC_APB1PeriphClockCmd(LORA_USART_CLOCK,ENABLE);
//USART1_TX
GPIO_InitStructure.GPIO_Pin = LORA_USART_TX_GPIO; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(LORA_GPIO_USART, &GPIO_InitStructure);
//USART1_RX
GPIO_InitStructure.GPIO_Pin = LORA_USART_RX_GPIO;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(LORA_GPIO_USART, &GPIO_InitStructure);
//USART
USART_InitStructure.USART_BaudRate = bound;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(AS608USART, &USART_InitStructure);
USART_ITConfig(AS608USART, USART_IT_RXNE, ENABLE);
USART_Cmd(AS608USART, ENABLE);
//Usart1 NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x00;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//串口二中断服务函数
void USART2_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(AS608USART, USART_IT_RXNE) != RESET)//接收到数据
{
res =USART_ReceiveData(AS608USART);
USART2_RX_BUF[counts++] = res;
}
}
将串口二初始化完之后就需要发送16进制的数据,这里需要注意一下由于32的串口数据寄存器只有1个字节 也就是每次只能发2位16进制的数据
//发送1个字节的数据
void AS608_SendData_8bit(u8 Data)
{
while(USART_GetFlagStatus(AS608USART, USART_FLAG_TC) == RESET);
USART2->DR = Data;
}
//发送2个字节数据
void AS608_SendData_16bit(u16 Data){
AS608_SendData_8bit(Data>>8);//发送高第八位数据
AS608_SendData_8bit(Data&0x00FF);//发送低第八位数据
}
//发送4个字节数据
void AS608_SendData_32bit(u32 Data){
AS608_SendData_8bit(Data>>24);
AS608_SendData_8bit((Data>>16)&0x00FF);
AS608_SendData_8bit((Data>>8)&0x0000FF);
AS608_SendData_8bit(Data&0x000000FF);
}
由于每个指令的头和指令都是一样的所以可以封装一下
void Data_Head(void){//数据头
AS608_SendData_16bit(0xEF01);//发送EF01
}
void Address_Data(void){//数据地址
AS608_SendData_32bit(0xFFFFFFFF);//发送0xFFFFFFFF
}
void Package_Identifer(u8 package_data){//包标志
AS608_SendData_8bit(package_data);
}
void Package_len(u16 package_len){//包长度
AS608_SendData_16bit(package_len);
}
void Command_Code(u8 Command_Data){//指令
AS608_SendData_8bit(Command_Data);
}
void PS_DeletChar(u16 Address){
u16 temp;
Data_Head();
Address_Data();
Package_Identifer(0x01);
Package_len(0x0007);
Command_Code(0x0C);
AS608_SendData_16bit(Address); //指定的ID号
AS608_SendData_16bit(0x0001); //删除的个数
temp=0x01+0x0007+0x0C+Address+0x0001;
AS608_SendData_16bit(temp);
}
void PS_GetImage(void){
Data_Head();
Address_Data();
Package_Identifer(0x01);
Package_len(0x0003);
Command_Code(0x01);
AS608_SendData_16bit(0x0005);
}
void PS_GenChar_Buffer1(void){
Data_Head();
Address_Data();
Package_Identifer(0x01);
Package_len(0x0004);
Command_Code(0x02);
AS608_SendData_8bit(0x01);
AS608_SendData_16bit(0x0008);
}
void PS_GenChar_Buffer1(void){
Data_Head();
Address_Data();
Package_Identifer(0x01);
Package_len(0x0004);
Command_Code(0x02);
AS608_SendData_8bit(0x02);
AS608_SendData_16bit(0x0008);
}
void PS_LoadChar(u16 Address){
u16 temp;
Data_Head();
Address_Data();
Package_Identifer(0x01);
Package_len(0x0006);
Command_Code(0x07);
AS608_SendData_8bit(0x02);
AS608_SendData_16bit(Address);
temp=0x01+0x0006+0x07+0x02+Address;
AS608_SendData_16bit(temp);
}
void PS_Match(void){
Data_Head();
Address_Data();
Package_Identifer(0x01);
Package_len(0x0003);
Command_Code(0x03);
AS608_SendData_16bit(0x0007);
}
void PS_Search(void){
Data_Head();
Address_Data();
Package_Identifer(0x01);
Package_len(0x0008);
Command_Code(0x04);
AS608_SendData_8bit(0x01);
AS608_SendData_16bit(0x0000);
AS608_SendData_16bit(0x012C);
AS608_SendData_16bit(0x003B);
}
发送指令后需要知道指令是否正常执行就需要的对返回的数据进行验证,由于返回的数据格式基本一致所以判断数据也就很简单。这里简单介绍一个的思路,判断是否删除指定缓存的思路。
通过开发板发送指令到AS608,AS608会回一个16进制的指令数据指令格式为:
确认码就是可以判断指令是否执行的唯一标准,判断确认码的值我们只需要找到返回数据中的包长度(0x0003)其后面的就是确认码。判断一下第一次搜索到0x070003的位置,然后指针向后移动3位就是确认码的值。
u8 Check_DeletChar(void){
u8 Check[5];
char *p=NULL;
Check[0]=0x07;
Check[1]=0x00;
Check[2]=0x03;
Check[3]='\0';
p=strstr((char *)USART2_RX_BUF,(char *)Check);
if(p!=NULL){
// printf("%d\r\n",*(p+3));
if(*(p+3)==0x00){
printf("删除成功\r\n");
Clear_Data();
return 0;
printf("删除成功1\r\n");
}
else{
printf("删除失败\r\n");
Clear_Data();
return 1;
}
delay_us(10);
}
Clear_Data();
return 1;
}
u8 Check_GetImage(void){
u8 Check[5];
char *p=NULL;
Check[0]=0x07;
Check[1]=0x00;
Check[2]=0x03;
Check[3]='\0';
p=strstr((char *)USART2_RX_BUF,(char *)Check);
if(p!=NULL){
// printf("%d\r\n",*(p+1));
if(*(p+3)==0x00){
Clear_Data();
printf("录入指纹成功\r\n");
return 0;
}
else{
Clear_Data();
printf("录入指纹失败\r\n");
return 1;
}
delay_us(10);
}
Clear_Data();
return 1;
}
u8 Check_GenChar_Buffer1(void){
u8 Check[5];
char *p=NULL;
Check[0]=0x07;
Check[1]=0x00;
Check[2]=0x03;
Check[3]='\0';
p=strstr((char *)USART2_RX_BUF,(char *)Check);
if(p!=NULL){
// printf("%d\r\n",*(p+1));
if(*(p+3)==0x00){
Clear_Data();
printf("生成特征码——1成功\r\n");
return 0;
}else{
Clear_Data();
printf("生成特征码——1失败\r\n");
return 1;
}
delay_us(10);
}
Clear_Data();
return 1;
}
u8 Check_GenChar_Buffer2(void){
u8 Check[5];
char *p=NULL;
Check[0]=0x07;
Check[1]=0x00;
Check[2]=0x03;
Check[3]='\0';
p=strstr((char *)USART2_RX_BUF,(char *)Check);
if(p!=NULL){
// printf("%d\r\n",*(p+1));
if(*(p+3)==0x00){
Clear_Data();
printf("生成特征码——2成功\r\n");
return 0;
}else{
Clear_Data();
printf("生成特征码——2失败\r\n");
return 1;
}
delay_us(10);
}
Clear_Data();
return 1;
}
u8 Check_RegModel(void){
u8 Check[5];
char *p=NULL;
Check[0]=0x07;
Check[1]=0x00;
Check[2]=0x03;
Check[3]='\0';
p=strstr((char *)USART2_RX_BUF,(char *)Check);
if(p!=NULL){
// printf("%d\r\n",*(p+1));
if(*(p+3)==0x00){
Clear_Data();
printf("合成特征成功\r\n");
return 0;
}else{
Clear_Data();
printf("合成特征失败\r\n");
return 1;
}
delay_us(10);
}
Clear_Data();
return 1;
}
u8 Check_StoreChar(void){
u8 Check[5];
char *p=NULL;
Check[0]=0x07;
Check[1]=0x00;
Check[2]=0x03;
Check[3]='\0';
p=strstr((char *)USART2_RX_BUF,(char *)Check);
if(p!=NULL){
// printf("%d\r\n",*(p+1));
if(*(p+3)==0x00){
Clear_Data();
printf("存储特征成功\r\n");
return 0;
}else{
Clear_Data();
printf("存储特征失败\r\n");
return 1;
}
delay_us(10);
}
Clear_Data();
return 1;
}
u8 Check_Search(void){
u8 Check[5];
char *p=NULL;
Check[0]=0x07;
Check[1]=0x00;
Check[2]=0x07;
Check[3]='\0';
p=strstr((char *)USART2_RX_BUF,(char *)Check);
if(p!=NULL){
// printf("%d\r\n",*(p+3));
if(*(p+3)==0x00){
printf("搜索成功\r\n");
Clear_Data();
return 0;
}
else{
printf("搜索失败\r\n");
Clear_Data();
return 1;
}
delay_us(10);
}
Clear_Data();
return 1;
}
依据驱动方式来编写代码,代码如下:
u8 Input_Fingerprint(u16 Address){
u8 temp=1;
//删除录入缓存区的数据
while(temp){
PS_DeletChar(Address);
delay_ms(1000);
temp=Check_DeletChar();
}
temp=1;
Clear_Data();
delay_ms(500);
//录取指纹
while(temp){
PS_GetImage();
delay_ms(1000);
temp= Check_GetImage();
}
temp=1;
Clear_Data();
delay_ms(500);
//生成特征放在buff1
while(temp){
PS_GenChar_Buffer1();
delay_ms(1000);
temp= Check_GenChar_Buffer1();
}
temp=1;
Clear_Data();
delay_ms(500);
//再次录取
while(temp){
PS_GetImage();
delay_ms(1000);
temp=Check_GetImage();
}
temp=1;
Clear_Data();
delay_ms(500);
//生成特征放在buff2
while(temp){
PS_GenChar_Buffer2();
delay_ms(1000);
temp=Check_GenChar_Buffer2();
}
temp=1;
Clear_Data();
delay_ms(500);
//合并特征模板
while(temp){
PS_RegModel();
delay_ms(1000);
temp=Check_RegModel();
}
temp=1;
Clear_Data();
delay_ms(500);
//储存模板
while(temp){
PS_StoreChar(Address);
delay_ms(1000);
temp=Check_StoreChar();
}
return temp;
}
u8 Search_FingerMat(void){
u8 temp=1;
TWO:
//录取指纹
while(temp){
PS_GetImage();
delay_ms(500);
temp=Check_GetImage();
}
temp=1;
Clear_Data();
delay_ms(500);
//生成特征
while(temp){
PS_GenChar_Buffer1();
delay_ms(500);
temp=Check_GenChar_Buffer1();
}
temp=1;
Clear_Data();
delay_ms(500);
//搜索指纹数据
while(temp){
PS_Search();
delay_ms(500);
temp=Check_Search();
if(temp){
printf("换个手指试试\r\n");
delay_ms(1000);
delay_ms(1000);
goto TWO;
}
}
Clear_Data();
delay_ms(500);
return 0;
}
源码链接
有疑问加群、评论或私信!