毕设-基于STM32刷卡、指纹的门禁系统(进阶版-指纹模块的驱动)

news/2025/3/4 21:15:36/

目录

  • 简介
  • 模块介绍
    • 引脚介绍
  • 指令
  • 驱动方式
  • 代码编写
    • 串口初始化
    • 串口发送数据
    • 发送指令
      • 删除flash库中指定ID号开始的N个数据
      • 录取指纹图像
      • 生成特征 存储在缓冲区1中
      • 生成特征 存储在缓冲区2中
      • 合并指纹特征
      • 将buffer1和buffer2中的模板文件存储到指定的地址中
      • 搜索指纹
    • 接受数据验证
      • 是否删除指定缓存
      • 是否录入指纹数据
      • 生成特征码是否成功
      • 生成特征码是否成功
      • 合成特征数据是否成功
      • 存储数据是否成功
      • 是否搜索到指纹
  • 指纹录入
  • 指纹搜索

简介

基础版本做完了,感觉很平淡所以这次来了一个很好玩的模块——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.9GPIO_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;//PA10GPIO_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);
}

删除flash库中指定ID号开始的N个数据

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);
}

生成特征 存储在缓冲区1中

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);
}

生成特征 存储在缓冲区2中

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);
}

将buffer1和buffer2中的模板文件存储到指定的地址中

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);//生成特征放在buff1while(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);//生成特征放在buff2while(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;
}

源码链接
有疑问加群、评论或私信!


http://www.ppmy.cn/news/362343.html

相关文章

看chatGPT如何回答opencv用于图像缩放resize中的inter_area

中文介绍opencv的inter_area ChatGPT OpenCV中的inter_area是指图像缩放时使用的插值方法之一。在图像缩放过程中,inter_area插值方法通过对图像进行平均采样来减小图像的尺寸,即在目标图像中每个像素的值由源图像中对应区域像素的平均值来确定。这种插…

gaussian理论化学计算初级进阶高阶文献解读

c 一、理论计算化学理论及相关程序入门 1理论计算化学简介1.1理论计算化学概述 1.2HF理论及后HF方法(高精度量化方法) 1.3密度泛函理论和方法 1.4不同理论计算方法的优缺点及初步选择 1.5基组及如何初步选择基组 2Gaussian安装及GaussView安装及基本操作 2.1Gaussian安装及设置…

Python3数据分析与挖掘建模(11)多因子:复合分析-分组分析与实现示例

1. 分组分析 1.1 概述 分组与钻取是数据分析中常用的技术,用于对数据进行聚合和细分分析。它可以帮助我们从整体数据中获取特定维度的汇总信息,并进一步钻取到更详细的子集数据中进行深入分析。 分组(Grouping)是指根据某个或多…

数学之美-隐马尔科夫模型

雅各布森的通信6要素: 发送者(信息源)、信道、接受者、信息、上下文、编码 其中:s1、s2....表示信息源发出的信号,比如手机发送的信号 o1、o2...是接收器接收到的信号。通信中的解码就是根据接收到的信号o1、o2....还…

国外摄影网站

一、摄影公司Companies 1. 尼康公司美国主页:http://www.nikonusa.com/default.htm 2. 佳能照相机博物馆:http://www.canon.co.jp/camera-museum/index2.html 3. 美能达澳大利亚公司:http://www.minoltacamera.com.au/ 4. 美能达美国公司&…

(链表) 143. 重排链表 ——【Leetcode每日一题】

❓143. 重排链表 难度:中等 给定一个单链表 L 的头节点 head ,单链表 L 表示为: L 0 L_0 L0​ → L 1 L_1 L1​ → … → L n − 1 L_{n-1} Ln−1​ → L n L_n Ln​ 请将其重新排列后变为: L 0 L_0 L0​ → L n L_n Ln​ →…

【Maven】私服

文章目录 私服一、Nexus的下载与安装二、仓库分类三、手动资源上传四、本地仓库访问私服五、Idea访问私服及组件上传 私服 之前提到过私服(仓库)的概念: 仓库:顾名思义就是用于存储资源的地方 - 包含各种jar包本地仓库&#xff1…

Maven私服地址

一、问题 现在maven项目非常流行,因为我们可以在pom.xml文件中配置项目所需要的jar包对应的坐标,maven就会自动管理jar包,但如果使用maven的中央仓库,因为其仓库服务器在国外,因此jar下载的速度非常慢,这时…