基于stm32f103c8t6及AS608-----指纹锁项目

article/2025/8/29 9:08:28

一、关于本项目:

        本文适合于初学者,大佬还请勿喷,欢迎提出意见,有纰漏之处将及时纠正。在浅学了stmf103c8t6后,想着依据现在所拥有的知识和能力做一个小项目。在了解到有关宿舍指纹锁的相关内容,对该功能的实现产生了极大的兴趣。于是着手准备学习研究.....|ू・ω・` )。

eadad54c723549f791d6b8c9d5a259ae.webp

注:工程代码在文章末尾。

二、准备工作:

  1. 掌握C语言基础....这个最基础啦...
  2. 接触过类似单片机,稍微看得懂芯片手册,会烧录啥的...
  3. 画板子,焊接(这个项目里自己给画了一个板子,整体更整洁美观一点,其实也不是必要的,个人选择)..
  4. 需要用到的一些硬件...

三、硬件需要:(大概估计了一下成本在100左右)

1.stm32f103c8t6核心板:某宝上十几元一块吧,如下

2f60e49286664b5c9d9230d836d8aa37.jpeg

 2.AS608指纹模块:(价格的话四十几元左右)

d867b145b3b8418fab75a1d0d2e8b164.jpeg

 3.0.96寸OLED模块:用的IIC,要想用spi的话可以自己改改代码。主要用于菜单选择。

6f8465dd381446e3acca53c0723274b8.png

 4.蜂鸣器:我这直接买的模块,给个低电平就能响,用于判断指纹锁的状态

3a1b0bbb88424705b7807267abdf3ff0.jpeg

 5.薄膜按键:用来对应oled,进行操作的选择。

c8710659c4fb498ea7ebad3f9e739481.jpeg

 6.SG90舵机

1d1e7a74431443ea8813cf3ffb023eca.jpeg

 四、功能介绍及大致流程

      先来个整体图:这是开机通电情况下的,从这个图基本能看到所有的外设。右上角的是总开关。这个开关用的一种金属按钮,这个按钮接线确实需要研究一下,最快的办法就是用万用表测按钮按下和未按下时各个脚的连通情况。左上角的的是一个小型的直流电压表,因为我用的是两节18650输出7.4v电压供电,为了实时监测电池状态,所以加了一个电压表。注:18650电池放电电压低到一定值会对电池造成不可逆的伤害,一般6.3v左右吧。

       中间的oled可以用下面的按键来显示不同功能,无操作时就会显示Main Menu,也就是主菜单的意思。按下2,则进入刷指纹功能,将已经录入过的指纹按在AS608上扫描可以被识别出来,使下面的舵机转动,舵机带动IC进行刷卡动作,(这个透明盒子的下面是留有缺口的的,IC卡能伸出去)宿舍门解锁成功。按下1,会进入录入指纹操作,我这里设置了一个安全防护,也就是说如果需要录入新的指纹,需要先进行一次刷指纹,才能进行录入指纹,也就是说必须要有之前录入过的人去刷一下,然后才能录入新指纹。这里的话安全防护我不是采用输入密码的方式,用指纹解锁替代。3键是返回主菜单键,例如要执行刷指纹操作,按下2后,中途不想继续了,可以按下3直接回到主菜单。4键这里没有用到(其实是当时设计板子的时候没有看这个薄膜按键的实物,如果需要用全四个键,则需要五个接口,而我却以为只需要四个接口ᕙ༼ ͝°益° ༽ᕗ,属实是大意了)。

其实本来想用3D打印做个外壳,但是这价格确实有点高(๑ó﹏ò๑)啊。d49450f85f1544aa82bfb7e8f6902fac.webp

16b16630643e4ea385628f82486914b1.jpeg

46579b541ea24c1e951cfb14b9d2c0ea.jpg

出发点的话比较简单:考虑到宿舍的门锁的类型(我们宿舍门是需要用学生IC卡刷一下解锁才能推进来),所以就想用这个东西来代替人为刷卡。其实说明白点,就是如果没带IC卡,还能通过这一方式来解锁门禁。

五、PCB设计

这个项目的板子是用嘉立创eda画的,毕竟可以免费打板๑乛◡乛๑。项目要求不是很高,随便布的线,如下图

2856b4f2419a48bcb3aa4304d8dbc083.png

0eb83625902c4319884e4a603df8498e.png

具体实物: 

22a46ec3dcde4dd9ba0ed682f1568a7c.jpeg

其实就是几个电源模块,因为输入的是7.4v,用的AMS1117转化为5v或者3.3v供外设使用。 然后引出了一些脚。

40216f109716402c880ad6892920b942.png

 之前在开发板上测试,然后才打的板子。

六、功能代码实现

 (一)本项目AS608使用:

对于这个模块它是有现成的函数去操作它,只需要知道怎么使用即可(可以用资料里的软件测试一下AS608)。下面进行说明:

用到的现成函数
PS_GetImage(void); //录入图像 
PS_GenChar(u8 BufferID);//生成特征 
PS_Match(void);//精确比对两枚指纹特征 
PS_Search(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p);//搜索指纹 
PS_RegModel(void);//合并特征(生成模板)
PS_StoreChar(u8 BufferID,u16 PageID);//储存模板
PS_DeletChar(u16 PageID,u16 N);//删除模板
PS_Empty(void);//清空指纹库
PS_ValidTempleteNum(u16 *ValidN);//读有效模板个数
PS_HandShake(u32 *PS_Addr); //与AS608模块握手

其实大致流程是这样的:

1.用PS_HandShake( )函数与AS608握手,这个握手类似于单片机向AS608发送一些信息,如果AS608能正常工作的话它会返回一些对应东西来回答你。这个函数用于确认AS608能正常运行,开始运行时先执行这个函数。

2.用PS_GetImage( )来获取按在模块上手指的指纹图像,然后执行PS_GenChar( )来获取这个图像里面的特征,将这个特征存在CharBuffer1或CharBuffer2,是AS08中存储的两个区域。这边的两个函数要执行两次,也就是说录入一个指纹的时候需要按两次,然后AS608执行PS_Match( )去对比刚刚采集到的两个特征,如果符合条件,则执行生成模板PS_RegModel( )函数,然后再存储PS_StoreChar( );具体的每个函数需要的变量大家还是参考keil工程里的文件。

 3.PS_DeletChar(u16 PageID,u16 N)这个是用来删除指定位置的指纹,PS_Empty(void);这个函数是清空指纹库。

4.读取内部指纹个数:PS_ValidTempleteNum(u16 *ValidN),可以令一个数等于ValidN,再打印出来即可看见。

5.PS_Search(u8 BufferID,u16 StartPage,u16 PageNum,SearchResult *p);搜索函数,也就是搜索现在刷的指纹在指纹库库中有没有对应的指纹,如果有的话,可以自定义输出一些提示文字啥的,比如find。

关于AS608:

3f61d1c22d154ba6a193a618b51f950f.png

 引脚接线:一共有八个接口,这里只用到了前六个,一般也不会用到后两个。

  1. Vi 模块电源正输入端,3.3。
  2. Tx 串行数据输出。 TTL 逻辑电平,接c8t6的Rx。
  3. Rx 串行数据输入。 TTL 逻辑电平,接c8t6的Tx。
  4. GND 信号地。内部与电源地连接
  5. WAK 感应信号输出,默认高电平有效(用户可读取状态引脚(WAK)判断有无手指按下,该项目中提供一个中断)
  6. Vt 触摸感应电源输入端,3.3v 供电(没看错,如果需要检测是否有指纹在上面,就需要接入3.3v,总共两个3.3v
  7. U+ USB D+
  8. U- USB D-

 资源分布:

1.缓冲区与指纹库
    系统内设有一个 72K 字节的图像缓冲区与二个 512bytes 大小的特征文件缓冲区,名字分别称为:ImageBuffer,CharBuffer1 和 CharBuffer2。用户可以通过指令读写任意一个缓冲区。
CharBuffer1 或 CharBuffer2 既可以用于存放普通特征文件也可以用于存放模板特征文件。通过 UART 口上传或下载图像时为了加快速度,只用到像素字节的高 4 位,即将两个像素合
成一个字节传送。通过 USB 口则是整 8 位像素。
指纹库容量根据挂接的 FLASH 容量不同而改变,系统会自动判别。指纹模板按照序号存放,序号定义为:0—(N-1)(N 为指纹库容量)。用户只能根据序号访问指纹库内容。
2.用户记事本
    系统在 FLASH 中开辟了一个 512 字节的存储区域作为用户记事本,该记事本逻辑上被分成 16 页,每页 32 字节。上位机可以通过 PS_WriteNotepad 指令和 PS_ReadNotepad 指令
访问任意一页。注意写记事本某一页的时候,该页 32 字节的内容被整体写入,原来的内容被覆盖。
3.随机数产生器
    系统内部集成了硬件 32 位随机数生成器(不需要随机数种子),用户可以通过指令让模块产生一个随机数并上传给上位机。
4.软件开发
模块地址 (大小:4bytes ,属性:读写) )
    模块的默认地址为0xFFFFFFFF,可通过指令修改,数据包的地址域必须与该地址相配,命令包/数据包才被系统接收。 注:与上位机通讯必须是默认地址 0xFFFFFFFF !
   模块口令 (大小:4bytes ,属性:写)
    系统默认口令为 0,可通过指令修改。若默认口令未被修改,则系统不要求验证口令,
上位机和 MCU 与芯片通讯;若口令被修改,则上位机与芯片通讯的第一个指令必须是验证
口令,只有口令验证通过后,芯片才接收其它指令。 注:不建议修改口令!
数据包大小设置(大小:1bytes ,属性:读写)
    发送数据包和接收数据包的长度根据该值设定。
波特率数 系数 N 设置 (大小:1bytes ,属性:读写)
    USART 波特率=N×9600,N=1~12。
安全等级 level 设置(大小:1bytes ,属性:读写)
    系统根据安全等级设定比对阀值,level=1~5。安全等级为 1 时认假率最高,拒认率最低。
安全等级为 5 时认假率最低,拒认率最高。
原文链接:https://blog.csdn.net/qq_44629109/article/details/108582138

此处为as608.c代码:对应功能,代码以注释上。部分函数并没有用到。

#include <string.h>
#include "delay.h" 	
#include "usart2.h"
#include "as608.h"
#include "stm32f10x.h"
#include "OLED_I2C.h"
#include "sys.h"
#include "BEEP.h"//extern u16 user_ID;
u32 AS608Addr = 0XFFFFFFFF; //默认
u8 Have;  //手指按下标志
extern u16 ValidN;//模块内有效模板个数
extern SysPara AS608Para;//指纹模块AS608参数//刷指纹void press_FR(void)
{SearchResult seach;u8 ensure;ensure=PS_GetImage();if(ensure==0x00)//获取图像成功 {	ensure=PS_GenChar(CharBuffer1);if(ensure==0x00) //生成特征成功{		ensure=PS_HighSpeedSearch(CharBuffer1,0,300,&seach);if(ensure==0x00)//搜索成功{	OLED_ShowStr(64,3," --INSERT OK--",1);				
//				printf("\r\n  指纹匹配成功		 \r\n");//搜索指纹成功	
//				open_door_flag=1;
//				printf("Match ID:%d  Match score:%d",seach.pageID,seach.mathscore);//显示匹配指纹的ID和分数//user_ID=seach.pageID;}else {OLED_ShowStr(0,1," --NO finger--",1);				//
//				BEEP=1;DelayMs(300);BEEP=0;DelayMs(300);}}else{OLED_ShowStr(0,6," --NO finger--",1);				//DelayMs(1000);printf("\r\n%s",EnsureMessage(ensure));}}
}//初始化PA6为下拉输入		    
//读摸出感应状态(触摸感应时输出高电平信号)
void PS_StaGPIO_Init(void)
{   EXTI_InitTypeDef EXTI_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef  GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//外部中断,需要使能AFIO时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟//打开PA端口时钟,并且打开复用时钟//初始化读状态引脚GPIOAGPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//输入下拉模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//50MHzGPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO//GPIOA.6	  中断线以及中断初始化配置GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);EXTI_InitStructure.EXTI_Line=EXTI_Line6;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);		//根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器NVIC_InitStructure.NVIC_IRQChannel =EXTI9_5_IRQn;			//使能按键所在的外部中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;	//抢占优先级2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;					//子优先级1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;								//使能外部中断通道NVIC_Init(&NVIC_InitStructure);  	  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器	}void EXTI9_5_IRQHandler(void)
{	if(EXTI_GetITStatus(EXTI_Line6)!=RESET){	Have=1;}EXTI_ClearITPendingBit(EXTI_Line6);    //清除LINE5上的中断标志位  
}
//串口发送一个字节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){DelayMs(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_UPImage
//功能:将图像缓冲区中的数据上传给上位机
//模块返回确认字
u8 PS_UpImage(void)
{u16 temp;u8  ensure;u8  *data;SendHead();SendAddr();SendFlag(0x01);//命令包标识SendLength(0x03);   //数据包长度Sendcmd(0x0a);      //命令包temp =  0x01+0x03+0x0a;SendCheck(temp);     //校验和data=JudgeStr(8000);if(data)ensure=data[9];elseensure=0xff;return ensure;
}//录入图像 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(2000);if(data)ensure=data[9];elseensure=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(2000);if(data)ensure=data[9];elseensure=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(2000);if(data)ensure=data[9];elseensure=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];	}elseensure = 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];elseensure=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];elseensure=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];elseensure=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];elseensure=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];elseensure=0xff;if(ensure==0)printf("\r\n设置参数成功!");elseprintf("\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(1000);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];}		elseensure=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];elseensure=0xff;	AS608Addr = PS_addr;if(ensure==0x00)printf("\r\n设置地址成功!");elseprintf("\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];elseensure=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];}}elseensure=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];}elseensure=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];}		elseensure=0xff;if(ensure==0x00){printf("\r\n有效指纹个数=%d",(data[10]<<8)+data[11]);//user_ID=(data[10]<<8)+data[11];}elseprintf("\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);	DelayMs(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;	
}

(二)按键控制菜单,返回主菜单

其实主函数里就是三种情况,用了几个标志位,Pre_flag是进行刷指纹的标志位,Add_flag是添加指纹功能的标志位,当这两个标志位都为0的话,则OLED显示屏显示Main Menu主菜单界面;如果Pre_flag=1并且Add_flag=0,则开始刷指纹操作;如果Pre_flag=0并且Add_flag=1,此时执行添加指纹模块,在下面的代码中也可看到,添加模式中需要先进行指纹验证才能继续添加新指纹。

不论是那种情况,执行之后都需要将它的标志位清0,循环使用。

	while(1){//		for(m=50;m>0;m--){
//		OLED_ShowStr(m,3,"Main Menu",2);
//		DelayUs(10);
//		OLED_CLS();
//		}DelayMs(300); while((Pre_flag==0)&&(Add_flag==0)){OLED_ShowStr(23,3,"Main  Menu",2);}if(m==0){  m=50; }//---------------刷指纹模式-------------------//while(Pre_flag==1 && Add_flag==0){OLED_CLS();DelayMs(100);press_fr();        //刷指纹Pre_flag=0;   //标志去除}//------------------- 添加模式-------------------//while(Add_flag==1 && Pre_flag==0){OLED_CLS();DelayMs(100);OLED_ShowStr(15,3,"verification",2);DelayMs(300);mark=0;OLED_CLS();while((Continue==0) && (mark1==0)){Pre_flag=1;        //满足刷指纹的条件Add_flag=0;       //如上press_fr();        //添加之前先按指纹验证}mark1==0;mark=1;Pre_flag=0;        //标志去除Add_flag=1; Add_FR();     Add_flag==0; //添加指纹标志去除}}//PS_Empty();	//要清指纹的时候用}

返回主菜单的功能是用中断实现的,将薄膜按键3对应的IO口中断优先级设为最高,按下3后Pre_flag和Add_flag都会被置为0。可是返回主菜单这个按键一般是在执行某个操作中使用的,那如何实现退出当前的操作?这里我用了一个比较暴力的方式(效率不高),如有好的建议可以在评论区积极提出来呀:例如在刷指纹函数的代码中,隔几行代码就执行一次检查,检查Pre_flag是否还是为1,如果还是为1,则继续执行下去,如果为0,则直接退出。

//----------------按键中断函数----------------//void  EXTI0_IRQHandler(void){if(EXTI_GetITStatus(EXTI_Line0)!=RESET){    //按一键可以刷指纹
//		OLED_CLS();
//		OLED_ShowStr(40,3,"PRESS",2);EXTI_ClearITPendingBit(EXTI_Line0);Pre_flag=0;DelayMs(10);
//		OLED_CLS();if(KEY1==0){Pre_flag=1;}}return;
}void  EXTI1_IRQHandler(void){if(EXTI_GetITStatus(EXTI_Line1)!=RESET){    //按二键可以加指纹Add_flag=0; EXTI_ClearITPendingBit(EXTI_Line1);DelayMs(10);
//		OLED_CLS();if(KEY2==0){Add_flag=1;      //进行添加标志位}}return;
}void  EXTI4_IRQHandler(void){if(EXTI_GetITStatus(EXTI_Line4)!=RESET){    //按三键可以加回到主菜单
//		OLED_CLS();
//		OLED_ShowStr(42,3," ADD ",2);EXTI_ClearITPendingBit(EXTI_Line4);DelayMs(10);if(KEY3==0){
//			OLED_CLS();Add_flag=0;      //进行添加标志位Pre_flag=0;mark1=1;        //打断添加指纹中的刷指纹}}return;
}
void press_fr(void){SearchResult seach;u8 ensure;char *str;Continue=0;OLED_CLS();OLED_ShowStr(15, 3, "Press Finger",2);  //提示可以刷指纹了while(Pre_flag==0){goto end1;    //高频次扫描}while(1){                     //检测到有指纹按下后继续执行while(Pre_flag==0){goto end1;}	 if(Have==1) break;}Have=0;ensure=PS_GetImage();while(Pre_flag==0){goto end1;}

(三)OLED(0.96寸)显示代码等外设使用就不一一在此叙述,详见工程文档。

七、感想

开始做一个小项目之前,要有一个具体的框架,需要实现怎么样的功能?需要用到什么外设?需要用到哪些引脚?需不需要画板子?等等等等都要考虑清楚。最好不要走一步看一步,提前做好规划吧。AS608模块我也是刚接触,确实也有很多不知道的地方,可能以上的说明中会出现部分错误,望谅解。٩( 'ω' )و 。关于引脚的分配等使用都在资料里。

8d8a58644f844ddeb3df81353170e751.jpg

演示视频:http://【大二学牲用stm32做的指纹锁】 https://www.bilibili.com/video/BV1Td4y1w7dK/?share_source=copy_web&vd_source=4d84f07cf9b4e331ed12b31fc1240391

工程资料链接:链接:https://pan.baidu.com/s/1rMt2Nyh3zBiefdZ4V-sBDQ?pwd=1111 
                         提取码:1111 
                          --来自百度网盘超级会员V3的分享


http://chatgpt.dhexx.cn/article/GG1oZNSY.shtml

相关文章

【基于STM32F103+AS608的智能打卡系统】

基于STM32F103AS608的智能打卡系统 工程源码链接 链接&#xff1a;https://pan.baidu.com/s/1RRc03nTrcTp--xfQnv2r9Q?pwdin9p 提取码&#xff1a;in9p --来自百度网盘超级会员V5的分享一、功能描述基于STM32F013AS608实行的智能打卡系统 1》进行打卡功能 2》指纹添加&…

as608指令c语言,单片机+AS608的使用,并带程序

之前在AS608的使用上走了许多弯路,这次希望身为初学者的我们可以吸取经验,特此介绍AS608. 指纹模块AS608的使用: 1脚红线:Vcc,连接3.3V电源供电,勿接3.3V以上电源 2脚黄线:接单片机串口TX端 3脚白线:接单片机串口RX端 4脚黑线:GND,3.3V电源的GND 5脚蓝线:模块触摸…

STM32+AS608指纹模块

前言&#xff1a;这个串口逻辑有点问题&#xff0c;要是用串口做命令输入端的话要自己改一下串口逻辑&#xff0c;串口只是调试能不能用指纹模块的功能&#xff0c;后期不需要串口发命令所以没怎么写串口的命令逻辑 1.AS608的资料挺全的&#xff0c;就是按照他给的来写命令&…

AS608与单片机握手失败、与上位机通讯失败

简言&#xff1a;本人在使用AS608模块时遇到了握手失败、通讯失败的问题&#xff0c;尝试网上各种方法未果后&#xff0c;换了一个AS608模块&#xff0c;问题解决。 一、与单片机握手失败 检查代码中的波特率是否为9600&#xff0c;再通过上位机将AS608的波特率改为与代码相同…

vue+vantUI实现单选、多选题(上一题、下一题)

vue+vantUI实现单选、多选题(上一题、下一题) <template><div class="exam-online"><div class="table-list2"><div v-for="(item,index) in questionList" :key="index"><div v-if="currentNum===…

答题微信小程序实现(4):数据库题库的调用/上一题、下一题/题量length的获取

不积跬步无以至千里&#xff0c;不计小流无以成江海。 本来想直接上选择题的&#xff0c;但步子就有点太大了&#xff0c;不是一点点来的。选择题除了题库的调用外&#xff0c;还需要判断答案对错&#xff0c;涉及到if语句&#xff0c;这个if语句应该单独写一个&#xff0c;对…

vue实现调查问卷一页一题,上一题下一题形式

使用了vant组件的radio 单选框 官网https://youzan.github.io/vant/#/zh-CN/radio 效果图 效果图 做出来的效果图就是上面那样 一页一题 附上代码&#xff1a; <template><div class"second"><!-- 问卷题 --><div v-for"(item,index) in…

leetcode每日一题

目录 &#x1f3c6;重新格式化电话号码 &#x1f453;①麻烦解法 &#x1f453;②使用库函数降维打击 每日鸡汤&#xff1a; &#x1f680;有志者自有千计万计&#xff0c;无志者只感千难万难。 &#x1f3c6;重新格式化电话号码 给你一个字符串形式的电话号码 number 。n…

题题题题题1

js 基本数据类型 7 种原始类型Boolean、Number、String、Null、Undefined、Symbol&#xff08;es6&#xff09;、BigInt、Object Symbol的理解 symbol是在es6中引入的基本数据类型、它表示的是一个独一无二的值&#xff0c;主要是来解决命名冲突的问题&#xff0c;它的使用场…

下午第一题

2017上半年 阅读下列说明和图&#xff0c;回答问题1至问题4&#xff0c;将解答填入答题纸的对应栏内。 【说明】 某医疗器械公司作为复杂医疗产品的集成商&#xff0c;必须保持高质量部件的及时供应。为了实现这一目标&#xff0c;该公司欲开发一采购系统。系统的主要功能如下:…

JS实现答题上一题下一题

以下是实现效果图 JS块代码 var TRUE_COUNT 0//正确的题数var count 10;//当前所在题数var thisURL document.URL;var getval thisURL.split(?)[1];var titleType getval.split("")[1];//获取题目typevar class_radio "selection";//选择按钮var ur…

js、jq实现答题上一题下一题

一、以下是效果图 说明&#xff1a;第一题只有一个“下一题”按钮&#xff0c;中间题目有两个按钮“上一题”“下一题”&#xff0c;最后一题按钮改为“提交”&#xff1b; 二、代码 js代码&#xff08;jquery需要自行引入&#xff09; var number document.getElementsByCl…

vue 简单实现上一题下一题问答效果

1.先来看下效果&#xff08;左上展示题目数量以及当前题序号&#xff0c;当第一题时只有下一题按钮&#xff0c;未回答问题时无法点击下一题&#xff0c;且回答结果展示错误或正确的颜色&#xff09; ​​​​ 2.接下来我们来看下html是怎么写的吧 <!-- 左边数据 --><…

MybatisPlus自动填充(建立、更新时间)

准备 1、建立user表 DROP TABLE IF EXISTS user;CREATE TABLE user (id BIGINT(20) NOT NULL COMMENT 主键ID,name VARCHAR(30) NULL DEFAULT NULL COMMENT 姓名,age INT(11) NULL DEFAULT NULL COMMENT 年龄,email VARCHAR(50) NULL DEFAULT NULL COMMENT 邮箱,PRIMARY KEY …

Jpa配置实体类创建时间更新时间自动赋值,@CreateDate,@LastModifiedDate

操作数据库映射实体类时&#xff0c;通常需要记录createTime和updateTime&#xff0c;如果每个对象新增或修改去都去手工操作创建时间、更新时间&#xff0c;会显得比较繁琐。 Springboot jpa提供了自动填充这两个字段的功能&#xff0c;简单配置一下即可。CreatedDate、LastM…

win10如何关闭自动更新及修改更新时间

关于win10如何关闭自动更新&#xff0c;有以下三个方法&#xff0c;但其效果明显不同。 当然&#xff0c;可用度最高的肯定放到最前面。 其中方法一实用性最强&#xff0c;方法二有时候没什么用&#xff0c;方法三可以延缓较长一段时间更新&#xff0c;方法四可以设置更新时间…

mybatis自动生成更新时间和创建时间

开发中&#xff0c;每次修改数据或者添加一条数据都要手动改变一次创建时间或者更新时间&#xff0c;有一说一挺麻烦的 数据表里面的创建时间和更改时间字段 更新update和insert要想自动生成的话实体类要加上TableField 然后创建MyMetaObjectHandler类&#xff0c;实现Meta…

Ubuntu修改时区和更新时间

更新时区 先查看当前系统时间 rootubuntu:/# date -R Thu, 12 Aug 2021 01:41:48 0000 结果时区是&#xff1a;-0000 我需要的是东八区&#xff0c;这儿显示不是&#xff0c;所以需要设置一个时区 运行tzselect tzselect在这里我们选择亚洲 Asia&#xff0c;确认之后选择中…

MySQL 设置 创建时间 和 更新时间

在实际应用中&#xff0c;我们时常会需要用到创建时间和更新时间这两个字段&#xff0c;下面记录一下&#xff1a; 第一种 &#xff1a; 命令行创建&#xff08;推荐&#xff09; CREATE TABLE user_info (uid varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT…

MySQL中创建时间和更新时间的自动更新

一、需求 当新增记录的时候&#xff0c;MySQL自动将系统的当前时间 set 到创建时间和更新时间这两个字段中。 当更新记录的时候&#xff0c;MySQL 只 update 更新时间字段的时间&#xff0c;而不修改创建时间字段对应的值。 二、方案 找到表中对应的创建时间和更新时间的字段…