晟元协议指纹头,单片机开发踩坑指南

一、硬件说明

1、接口

 注意:其中Touch_VCC 3.3V是需要常供电的,用于Touch_Out触摸唤醒供电,VDD3.3V可以检测到Touch_Out高电平信号再上电。如果不需要低功耗设计,可以忽略Touch_Out和Touch_VCC 3.3V引脚,悬空即可。

2、供电

注意: 需要考虑电路驱动能力,电压和电流稳定,避免指纹头运行异常。

3、串口配置

 注意:就算单片机和指纹头设置一样的波特率,但是也有可能有误差注意配置好单片机时钟。

 

 以华大32位单片机为例,可以看出4MHz和24MHz主频下误差率差别很大。

二、软件说明

1、协议格式

主要针对命令包,其他包格式没用过,不做说明,自查手册。

 注意:特别需要注意的是校验和的计算(校验和16bit = 包长度+指令+参数1+参数二+参数N)

每个命令都有应答包响应格式如下:

 2、简单指纹操作

相信使用指纹头做指纹识别,首先肯定是最基础的就是指纹的增删改查了,不过改就是覆盖,和添加指纹是一样,所以就是增删查了!

对指纹头来说,修改数据是高级操作,是需要权限的,所以就需要先验证密码,来获取权限。

2.1、验证口令FP_VfyPwd

 芯片地址一般默认0xffffffff,口令默认0x00000000,通过查询手册参数表可以得到。

 接下来就可以愉快的拼凑出通信报文进行通信了。

 2.2自动录入指纹

手册提供了指纹录入的一站式命令(自动注册模板 PS_AutoEnroll),命令格式和录入流程如下。

 

 注意:不同指纹头要求最少录入的次数不同,如果设置次数不够在验证指纹时可能无法识别,一般2-6次,如果不清楚6次最稳妥。

2.3 清空指纹

会添加指纹了,也要回删除指纹,先从最简单的清空指纹命令开始, 清空指纹库 PS_Empty

参数都不用设置,直接发,省事。 

2.3自动指纹验证

手册提供了,自动验证指纹的一站式命令(自动验证指纹 PS_AutoIdentify),格式和识别流程如下。 

注意:安全等级1-5,我使用的0x1,id号使用0xffff,参数使用0x0000 就好了,可以根据自己需求修改。

2.4 整体操作流程图

 注意:这里指纹识别只识别一次,如果不用触摸中断,可以在循环中使用PS_GetImage命令判断手指是否按下,另外由于我录入指纹是需要id自动累加的,所以先读取索引表计算空闲的id,发送休眠指令是为了识别完成给指纹头断电节能,断电之前要先发送休眠指令,直接断电指纹头可能会出现异常。

3、分步指纹操作

一站式命令固然好用,但是很多内部处理步骤我们看不到,使用起来也不够灵活,所以我们尝试手动一步一步来做识别。

为了方便我直接录入和识别的流程全部贴出来

 注意:一个空间存储的指纹是由多个指纹特征合成的,你甚至可以循环录入指纹的时候每次都按不同的手指,就可以一次添加多个手指了,但是识别准确度会下降,并不推荐。

另外PS_GetImage PS_GetEnrollImage作用差不多,PS_GetImage 用于识别, PS_GetEnrollImage用于录入,有些甚至可以串用,但是有些指纹头可能不行,不推荐串用

PS_GetImage是个很好用的命令,可以在循环中使用检测手指是否按下,检测到指纹再生成特征进行指纹识别。

录入指纹大致说来就是获取一次图像生成一个特征,然后将多个特征合并存储就好了。

识别指纹就是录入指纹生成特征,然后用特征去指纹库里检索。

三、代码片段

1、宏定义

//晟元协议
#define DATA_SIZE_MAX		(256-2)		//数据区域最大值 减去校验和2字节
#define CMD_FLAG				1											//命令标志
#define	DATA_FLAG				2											//数据标志
#define	ACT_FLAG				7											//应答标志
#define DATA_END_FLAG			8											//数据包结束标志

typedef enum {
	CMD_VERIFY_PASSWORD = 1,
	CMD_VERIFY_DEFPASSWORD,
	CMD_SET_PASSWORD,
	CMD_SET_DEFPASSWORD,
	CMD_GET_IMAGE,
	CMD_GEN_TEMPLET1,
	CMD_GEN_TEMPLET2,
	CMD_GEN_TEMPLET3,
	CMD_SEARCH_FINGER,
	CMD_MERGE_TEMPLET,
	CMD_STORE_TEMPLET,
	CMD_GETENROLLIMAGE,
	CMD_CMP_TEMPLET,
	CMD_GET_NUMBERS,
	CMD_READINDEXTABLE,
	CMD_CONTROLLED,
	CMD_DEFINELED,
	CMD_READSYSPARA,
	CMD_SETBAUDRATE,
	CMD_SETDEFADDR,
	CMD_HANDSHAKE,
	CMD_READ_NOTE,
	CMD_CLEAR_NOTE,
	CMD_DELETCHAR,
	CMD_EMPTY,
	CMD_SLEEP,
	CMD_AUTOENROLL,
	CMD_AUTOIDENTIFY
}COMMON_CMD;

struct fp_package {
	uint16_t		header;
	uint32_t		addr;
	uint8_t			flag;
	uint16_t		length;
	uint8_t			data[DATA_SIZE_MAX];
	uint16_t		sum;
};

struct fp_send_package {
	uint16_t		header;
	uint32_t		addr;
	uint8_t			flag;
	uint16_t		length;
	uint8_t			code;
	uint8_t			data[DATA_SIZE_MAX-1];
	uint16_t		sum;
};

struct fp_recv_package {
	uint16_t		header;
	uint32_t		addr;
	uint8_t			flag;
	uint16_t		length;
	uint8_t			ret;
	uint8_t			data[DATA_SIZE_MAX-1];
	uint16_t		sum;
};

struct uart_rcbuf_s{
	uint8_t u8ResponeData[FP_USART_BUFF_MAX];
	uint16_t u8ResponeCnt;
	uint16_t u8finish;
};

//一站式注册指纹
#define USE_AUTOENROLL(N)							{N.length=8;N.data[0]=0x31;N.data[1]=0x0;N.data[2]=0x01;N.data[3]=0x6;}
//自动采集指纹
#define USE_AUTOIDENTIFY(N)					{N.length=8;N.data[0]=0x32;N.data[1]=0x1;N.data[2]=0xff;N.data[3]=0xff;}
//验证设备握手口令
#define USE_VERIFY_PASSWORD(N)				{N.length=7;N.data[0]=0x13;N.data[1]=0x12;N.data[2]=0x34;N.data[3]=0x43;N.data[4]=0x21;}
#define USE_VERIFY_DEFPASSWORD(N)			{N.length=7;N.data[0]=0x13;N.data[1]=0;N.data[2]=0;N.data[3]=0;N.data[4]=0;}
//设置设备握手口令
#define USE_SET_PASSWORD(N)					{N.length=7;N.data[0]=0x12;N.data[1]=0x12;N.data[2]=0x34;N.data[3]=0x43;N.data[4]=0x21;}
#define USE_SET_DEFPASSWORD(N)				{N.length=7;N.data[0]=0x12;N.data[1]=0;N.data[2]=0;N.data[3]=0;N.data[4]=0;}
//探测手指并从传感器上读入图像
#define USE_GET_IMAGE(N)								{N.length=3;N.data[0]=0x1;}
//根据原始图像生成指纹特征1
#define USE_GEN_TEMPLET1(N)					{N.length=4;N.data[0]=0x2;N.data[1]=0x1;}
//根据原始图像生成指纹特征2
#define USE_GEN_TEMPLET2(N)					{N.length=4;N.data[0]=0x2;N.data[1]=0x2;}
//根据原始图像生成指纹特征3
#define USE_GEN_TEMPLET3(N)					{N.length=4;N.data[0]=0x2;N.data[1]=0x3;}
//以CharBufferA或CharBufferB中的特征文件搜索整个或部分指纹库
#define USE_SEARCH_FINGER(N)				{N.length=8;N.data[0]=0x04;N.data[1]=0x1;N.data[2]=0x0;N.data[3]=0x0;N.data[4]=0x00;N.data[5]=0x64;}
//将CharBufferA与CharBufferB中的特征文件合并生成模板,结果存于ModelBuffer
#define USE_MERGE_TEMPLET(N)				{N.length=3;N.data[0]=0x5;}
//将ModelBuffer中的文件储存到flash指纹库中
#define USE_STORE_TEMPLET(N)				{N.length=6;N.data[0]=0x6;N.data[1]=0x1;N.data[2]=0x0;N.data[3]=0x0;}
//GetEnrollImage
#define USE_GETENROLLIMAGE(N)					{N.length=3;N.data[0]=0x29;}
//cmp
#define USE_CMP_TEMPLET(N)					{N.length=3;N.data[0]=0x3;}
//GET NUMBERS
#define USE_GET_NUMBERS(N)					{N.length=3;N.data[0]=0x1d;}
//READ INDEXTABLE
#define USE_READINDEXTABLE(N)					{N.length=4;N.data[0]=0x1f;}
//set baudrate
#define USE_SETBAUDRATE(N)					{N.length=5;N.data[0]=0x0E;N.data[1]=0x04;N.data[2]=0x06;}
//Control LED
#define USE_CONTROLLED(N)					{N.length=4;N.data[0]=0x40;N.data[1]=0x0f;}
//Define LED
#define USE_DEFINELED(N)					{N.length=7;N.data[0]=0x3c;N.data[1]=0xf5;N.data[2]=0x0;N.data[3]=0x00;}
//读系统基本参数
#define USE_READSYSPARA(N)					{N.length=3;N.data[0]=0x0f;}
//设置默认地址
#define USE_SETDEFADDR(N)					{N.length=7;N.data[0]=0x15;N.data[1]=0xFF;N.data[2]=0xFF;N.data[3]=0xFF;N.data[4]=0xFF;}
//握手
#define USE_HANDSHAKE(N)					{N.length=3;N.data[0]=0x35;}
//Read Note
#define USE_READ_NOTE(N)					{N.length=4;N.data[0]=0x19;}
//Clear Note
#define USE_CLEAR_NOTE(N)					{N.length=36;N.data[0]=0x18;N.data[1]=0x2;}
//删除一个指纹
#define	USE_DELETCHAR(N) 	{N.length=7;N.data[0]=0x0C;N.data[4]=0x1;}
//清空指纹库
#define USE_EMPTY(N)		{N.length=3;N.data[0]=0x0D;}
//休眠
#define USE_SLEEP(N)		{N.length=3;N.data[0]=0x33;}
//错误标志

enum {
	FP_ERROR = -1,
	FP_OK = 0,
	FP_TIMEOUT = 1,
};

enum FP_RES_CODE{			
	RES_OK = 0,			//		00H:表示指令执行完毕或 OK; 
	RES_RCERR,			//		01H:表示数据包接收错误; 
	RES_NOTOUCH,		//		02H:表示传感器上没有手指; 
	RES_TINERR,			//		03H:表示录入指纹图像失败; 
	RES_PTOOWATER,		//		04H:表示指纹图像太干、太淡而生不成特征; 
	RES_PTOODRY,		//		05H:表示指纹图像太湿、太糊而生不成特征; 
	RES_PTOOMESS,		//		06H:表示指纹图像太乱而生不成特征; 
	RES_PFTOOFEW,		//		07H:表示指纹图像正常,但特征点太少(或面积太小)而生不成特征; 
	RES_FPNOPASS,		//		08H:表示指纹不匹配; 
	RES_FPNOSEARCH,		//		09H:表示没搜索到指纹; 
	RES_FMERERR,		//		0aH:表示特征合并失败; 
	RES_IDXOVER,		//		0bH:表示访问指纹库时地址序号超出指纹库范围; 
	RES_RDMODERR,		//		0cH:表示从指纹库读模板出错或无效; 
	RES_ULFUERR,		//		0dH:表示上传特征失败; 
	RES_CNRDDPAK,		//		0eH:表示模块不能接收后续数据包; 
	RES_ULPICERR,		//		0fH:表示上传图像失败; 
	RES_DELMODERR,		//		10H:表示删除模板失败; 
	RES_ENPFPFAULT,		//		11H:表示清空指纹库失败; 
	RES_CNENLPMOD,		//		12H:表示不能进入低功耗状态; 
	RES_PWDERR,			//		13H:表示口令不正确; 
	RES_RESETFAULT,		//		14H:表示系统复位失败; 
	RES_BUFNORPIC,		//		15H:表示缓冲区内没有有效原始图而生不成图像; 
	RES_FPNOMOVE,		//		17H:表示残留指纹或两次采集之间手指没有移动过; 
	RES_RWFLAERR,		//		18H:表示读写 FLASH 出错; 
	RES_SHDPAK,			//		f0H:有后续数据包的指令,正确接收后用 0xf0 应答; 
	RES_SHCPAK,			//		f1H:有后续数据包的指令,命令包用 0xf1 应答; 
	RES_ENROLLERR,		//		1eH:自动注册(enroll)失败; 
	RES_FPLIBFULL,		//		1fH:指纹库满;
};

2、关键命令发送处理函数

//协议包接收和发送缓冲区
struct fp_package send_pack;
struct fp_recv_package recv_pack;

//高字节和低字节转换
void ConvByteToggleHL(uint8_t * dest,uint8_t * src,uint32_t size)
{
	uint32_t lidx;
	if(size<1 || dest==NULL || src==NULL){
		return ;
	}
	for(lidx=0;lidx<size;lidx++)
	{
		dest[lidx]=src[size-lidx-1];
	}
}

/**
*			Name:				FP_SendPack
*			Function:			发送包
*			Param:				void
*			Return:				int:错误代码
*/

int FP_SendPack(void)
{
	int lIdx;
	uint8_t lsendbuf[10]={0};
	send_pack.header = 0xEF01;
	send_pack.addr = 0xFFFFFFFF;	
	//计算校验和
	send_pack.sum = send_pack.flag + (uint16_t)(send_pack.length & 0xff) + (uint16_t)(send_pack.length >> 8);
	for (lIdx = 0; lIdx < send_pack.length-2 && lIdx < DATA_SIZE_MAX; lIdx++) {
		send_pack.sum += send_pack.data[lIdx];
	}
	rt_enter_critical();
	//发送头标志
	ConvByteToggleHL(lsendbuf,(uint8_t *)&send_pack.header,sizeof(send_pack.header));
	Uart0_SendBytes(lsendbuf,sizeof(send_pack.header));
	//发送地址
	ConvByteToggleHL(lsendbuf,(uint8_t *)&send_pack.addr,sizeof(send_pack.addr));
	Uart0_SendBytes(lsendbuf,sizeof(send_pack.addr));
	//发送标志位
	Uart0_SendBytes(&send_pack.flag,sizeof(send_pack.flag));
	//发送数据长度
	ConvByteToggleHL(lsendbuf,(uint8_t *)&send_pack.length,sizeof(send_pack.length));
	Uart0_SendBytes(lsendbuf,sizeof(send_pack.length));
	//发送数据
	Uart0_SendBytes(send_pack.data,send_pack.length-2);
	//发送校验和
	ConvByteToggleHL(lsendbuf,(uint8_t *)&send_pack.sum,sizeof(send_pack.sum));
	Uart0_SendBytes(lsendbuf,sizeof(send_pack.sum));
	rt_exit_critical();
	return 0;
}


/**
*			Name:				FP_RecvPack
*			Function:			接收包
*			Param:				void
*			Return:				int:错误代码
*/

int FP_RecvPack(struct fp_recv_package * recv)
{
	int ret,lto,lIdx;
	uint16_t temp_sum;
	struct fp_package * recv_pack = (struct fp_package *)recv;
	uint8_t * u8SrcAddr = uart_rcbuf.u8ResponeData;
	memset(recv_pack,0,sizeof(struct fp_recv_package));
	if(!uart_rcbuf.u8finish){
		return FP_TIMEOUT;
	}
	uart_rcbuf.u8finish = 0;
	//接收包头
	ConvByteToggleHL((uint8_t *)&recv_pack->header,u8SrcAddr,sizeof(recv_pack->header));
	u8SrcAddr+=sizeof(recv_pack->header);
	if (recv_pack->header != 0xEF01) {
		goto FP_RC_ERROR;
	}
	//接收地址
	ConvByteToggleHL((uint8_t *)&recv_pack->addr,u8SrcAddr,sizeof(recv_pack->addr));
	u8SrcAddr+=sizeof(recv_pack->addr);
	if (recv_pack->addr != 0xFFFFFFFF) {
		goto FP_RC_ERROR;
	}
	//接收包类型标志
	ConvByteToggleHL((uint8_t *)&recv_pack->flag,u8SrcAddr,sizeof(recv_pack->flag));
	u8SrcAddr+=sizeof(recv_pack->flag);
	switch (recv_pack->flag)
	{
	case CMD_FLAG:
	case DATA_FLAG:
	case ACT_FLAG:
	case DATA_END_FLAG:
		break;
	default:
		goto FP_RC_ERROR;
	}
	//接收数据长度
	ConvByteToggleHL((uint8_t *)&recv_pack->length,u8SrcAddr,sizeof(recv_pack->length));
	u8SrcAddr+=sizeof(recv_pack->length);
	if (recv_pack->length > 256) {
		goto FP_RC_ERROR;
	}
	//接收数据
	memcpy(recv_pack->data,u8SrcAddr,recv_pack->length-2);
	//计算校验和
	temp_sum = recv_pack->flag + (recv_pack->length & 0xff) + (recv_pack->length >> 8);
	for (lIdx = 0; lIdx < (recv_pack->length-2); lIdx++) {
		temp_sum += u8SrcAddr[lIdx];
	}
	u8SrcAddr+=recv_pack->length-2;
	//接收校验和
	ConvByteToggleHL((uint8_t *)&recv_pack->sum,u8SrcAddr,sizeof(recv_pack->sum));
	if (recv_pack->sum != temp_sum) {
		goto FP_RC_ERROR;
	}
	return FP_OK;
	FP_RC_ERROR:
	return FP_ERROR;
}

/**
*			Name:							FP_SendCmdPack
*			Function:			发送常用命令包
*			Param:						cmd:命令名
*			Return:					int:错误代码
*/

int FP_SendCmdPack(COMMON_CMD cmd,uint32_t timeout) 
{
	rt_err_t rt_err;
	int ret,lto;
	if(timeout==0){
		timeout = FP_DEFAULT_TIMEOUT;
	}
	memset(&send_pack, 0, sizeof(send_pack));
	send_pack.flag = CMD_FLAG;
	switch (cmd)
	{
	case CMD_VERIFY_PASSWORD:
		USE_VERIFY_PASSWORD(send_pack);
		break;
	case CMD_VERIFY_DEFPASSWORD:
		USE_VERIFY_DEFPASSWORD(send_pack);
		break;
	case CMD_SET_PASSWORD:
		USE_SET_PASSWORD(send_pack);
		break;
	case CMD_SET_DEFPASSWORD:
		USE_SET_DEFPASSWORD(send_pack);
		break;
	case CMD_GET_IMAGE:
		USE_GET_IMAGE(send_pack);
		break;
	case CMD_GEN_TEMPLET1:
		USE_GEN_TEMPLET1(send_pack);
		break;
	case CMD_GEN_TEMPLET2:
		USE_GEN_TEMPLET2(send_pack);
		break;
	case CMD_GEN_TEMPLET3:
		USE_GEN_TEMPLET3(send_pack);
		break;
	case CMD_SEARCH_FINGER:
		USE_SEARCH_FINGER(send_pack);
		break;
	case CMD_MERGE_TEMPLET:
		USE_MERGE_TEMPLET(send_pack);
		break;
	case CMD_STORE_TEMPLET:
		USE_STORE_TEMPLET(send_pack);
		break;
	case CMD_GETENROLLIMAGE:
		USE_GETENROLLIMAGE(send_pack);
		break;
	case CMD_CMP_TEMPLET:
		USE_CMP_TEMPLET(send_pack);
		break;
	case CMD_GET_NUMBERS:
		USE_GET_NUMBERS(send_pack);
		break;
	case CMD_READINDEXTABLE:
		USE_READINDEXTABLE(send_pack);
		break;
	case CMD_CONTROLLED:
		USE_CONTROLLED(send_pack);
		break;
	case CMD_DEFINELED:
		USE_DEFINELED(send_pack);
		break;
	case CMD_READSYSPARA:
		USE_READSYSPARA(send_pack);
		break;
	case CMD_SETBAUDRATE:
		USE_SETBAUDRATE(send_pack);
		break;
	case CMD_SETDEFADDR:
		USE_SETDEFADDR(send_pack);
		break;
	case CMD_HANDSHAKE:
		USE_HANDSHAKE(send_pack);
		break;
	case CMD_READ_NOTE:
		USE_READ_NOTE(send_pack);
		break;
	case CMD_CLEAR_NOTE:
		USE_CLEAR_NOTE(send_pack);
		break;
	case CMD_DELETCHAR:
		USE_DELETCHAR(send_pack);
		break;
	case CMD_EMPTY:
		USE_EMPTY(send_pack);
		break;
	case CMD_SLEEP:
		USE_SLEEP(send_pack);
		break;
	case CMD_AUTOENROLL:
		USE_AUTOENROLL(send_pack);
		break;
	case CMD_AUTOIDENTIFY:
		USE_AUTOIDENTIFY(send_pack);
		break;
	default:
		return FP_ERROR;
	}
	memset(&uart_rcbuf,0,sizeof(struct uart_rcbuf_s));
	ret = FP_SendPack();
	rt_sem_detach(&usart_rcfi_sem);
	rt_sem_init(&usart_rcfi_sem,"rcfisem",0,RT_IPC_FLAG_FIFO);
	rt_err = rt_sem_take(&usart_rcfi_sem,rt_tick_from_millisecond(timeout));
	ret = FP_RecvPack(&recv_pack);
	return ret;
}

注意:请忽略我rt-thread的函数调用,主要就是为了等待指纹头串口的响应罢了。

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值