ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AS608指纹模块方案详解:51单片机1:1验证与1:N识别源码解析

AS608指纹模块方案详解:51单片机1:1验证与1:N识别源码解析 简介基于AS608指纹模块的单片机指纹识别源码包提供方案1和方案2两套Keil4工程核心由STC89C52单片机配合AS608光学指纹传感器完成指纹录入、存储与验证适用于嵌入式入门者、指纹锁开发及生物识别技术学习者。资源共108个文件压缩包约241KB主要包含C源文件、头文件、Keil4工程文件.uvproj、可烧录的HEX文件以及编译生成的OBJ/LST等中间文件既便于直接打开工程阅读代码也方便重新编译与烧录验证。已有1076人学习下载使用者可对比两套方案在指纹存储管理、匹配流程或串行通信处理上的差异理解AS608模块的数据帧格式与单片机控制逻辑。压缩包内保留的备份文件也能辅助排查调试过程中的工程配置变化是一份内容紧凑、适合边学边改的嵌入式指纹锁参考资料。1. AS608指纹模块“方案1和方案2”到底差在哪做51单片机指纹锁的人第一次拿到AS608基本都会遇到这个困惑模块支持一大堆指令教程里却总说“方案1”“方案2”到底选哪个其实两条路对应的是两种完全不同的使用习惯——方案1叫1:1验证方案2叫1:N识别。落到开锁场景上方案1的逻辑是“这个手指是不是ID为5的那个人”方案2则是“这个手指是库里的哪一个人”。这两个方案在AS608内部走的是不同的指令通道录入流程也有区别代码结构、变量分配、参数阈值都不一样。说白了一句话锁只给一个人开用方案1给好几个人开用方案2。下文从协议层讲起把两个方案的Keil4 C51源码拆开说明白顺便把51上最容易踩的波特率、粘包、应答校验这几个坑一起处理掉。2. 先看懂AS608的数据帧和51侧的硬件准备AS608是一个自带DSP的光学指纹传感器所有的操作都是通过UART往模块里发指令帧。和读温湿度、读GPS这类“发一条指令收一段数据”的传感器不一样指纹模块的指令有严格的帧格式而且每一帧都要算校验和应答里还有确认码。把这些基础打牢后面两个方案的代码就是一通百通。2.1 AS608数据包格式包头、地址、包标识、长度、指令、校验和AS608的指令包最小结构如下所有指令都是这套壳子字段长度说明包头4字节固定 0xEF 0xAB 0xCD 0xEF地址4字节默认 0xFFFFFFFF包标识1字节0x01命令包0x07应答包0x02数据包0x08结束包包长度2字节指令码 参数 校验和的字节数指令码1字节见下面的指令表参数N字节按指令不同可以是0到5字节校验和2字节从包标识累加到参数末字节取低16位高字节在前新手最容易写错的就是校验和。网上很多例程的校验和算法五花八门有的是从包头开始累加有的是从地址开始累加。实际验证下来AS608固件认的是“从包标识开始累加”包头和地址不参与计算。举个例子发一条“采集指纹”指令完整帧是EF AB CD EF FF FF FF FF 01 00 03 01 00 05这里的01 00 03 01四项加起来是 0x05所以校验和是00 05。后文代码里的校验和函数就按这个范围写。2.2 方案1/2都要用到的核心指令AS608指令集有二十多条但两个方案真正高频用到的只有下面这些指令指令码参数用途GenImg0x01无采集手指图像GenChar0x02BufferID0x01/0x02从图像生成特征存入指定缓冲区Match0x03无比较缓冲区1和缓冲区2的特征Search0x04BufferID StartPage PageNum在模板库中1:N搜索RegModel0x05BufferID1 BufferID2合并两个特征生成模板StoreChar0x06BufferID PageID将模板存入库中指定IDLoadChar0x07BufferID PageID将指定ID模板读入缓冲区ReadValidTempNum0x1D无读取已录入的模板总数方案1走的是 0x07 LoadChar 0x03 Match方案2走的是 0x04 Search。这两条路径在指令层面是完全分开的这也是两个方案能独立实现的原因。2.3 51接线与串口初始化晶振和波特率先对齐AS608模块串口是TTL电平可以直接和STC89C52的RXD、TXD连接不需要MAX232。接线就四根AS608 VCC - 5V AS608 GND - GND AS608 TXD - P3.0 (RXD) AS608 RXD - P3.1 (TXD)注意TXD和RXD要交叉这是串口接线的老规矩。模块上如果有TOUCH引脚可以悬空不接51方案里一般只靠串口指令驱动。串口初始化这里有个关键决策。AS608出厂默认波特率一般是57600但STC89C52这类12T单片机在11.0592MHz晶振下跑57600定时器重载值TH1 256 - 11059200 / 384 / 57600 ≈ 255.5取整后误差超过8%串口会出乱码。所以51平台上最可靠的做法是把模块波特率改到9600后面专门讲怎么改这里先看9600对应的初始化代码/* 11.0592MHz晶振9600bps定时器1方式2 */ void uart_init(void) { SCON 0x50; /* 模式18位UART允许接收 */ TMOD 0x20; /* 定时器1工作在方式28位自动重装 */ TH1 0xFD; /* 9600bps 重载值 */ TL1 0xFD; TR1 1; /* 启动定时器1 */ ES 1; EA 1; }TH10xFD对应9600波特率这个值是11.0592MHz晶振下的精确解误差为0。工程里如果用的是STC12、STC15这类1T单片机波特率计算方式完全不同需要改用独立波特率发生器下文代码默认都是STC89C52 11.0592MHz 9600bps。3. 方案1源码固定ID录入 LoadChar/Match 1:1验证方案1的核心思想是每枚指纹存进模块时绑定一个固定ID验证时指定ID先把该ID的模板读出来再和现场采集的手指特征做一对一对比。这个方案适合管理员指纹、单人指纹锁逻辑简单代码量也小。3.1 指令流程拆解录入两条线验证一条线录入一枚指纹要经过“采集两次、生成两次、合并一次、存储一次”验证则是“读库、采集、生成、比对”四步步骤录入方案1验证方案11GenImg 采集手指1LoadChar(0x02, ID) 读模板到缓冲区22GenChar(0x01) 生成特征到缓冲区1GenImg 采集手指3GenImg 采集同手指第2次GenChar(0x01) 特征到缓冲区14GenChar(0x02) 特征到缓冲区2Match 比较缓冲区1和缓冲区25RegModel(0x01, 0x02) 合并模板应答确认码得分6StoreChar(0x01, ID) 存入指定ID这里最容易被绕晕的是缓冲区编号。AS608内部有CharBuffer1和CharBuffer2两个缓冲区合成模板时两个缓冲区都要有有效特征合成结果覆盖到缓冲区1。验证时用LoadChar把库里模板读到缓冲区2现场采集生成的特征放缓冲区1两边数据来源不同Match才有意义。3.2 收发基础函数发指令、等应答、查校验先把通用函数写好两个方案共用。发送函数负责拼帧、算校验和、清空接收缓冲/* 发送一条AS608指令 */ void as608_send_cmd(unsigned char cmd, unsigned char *params, unsigned char n) { unsigned char buf[16]; unsigned char i; unsigned int sum 0; buf[0] 0xEF; buf[1] 0xAB; buf[2] 0xCD; buf[3] 0xEF; /* 包头 */ buf[4] 0xFF; buf[5] 0xFF; buf[6] 0xFF; buf[7] 0xFF; /* 地址默认FFFFFFFF */ buf[8] 0x01; /* 包标识命令包 */ buf[9] 0x00; buf[10] n 3; /* 包长度 指令1 参数n 校验和2 */ buf[11] cmd; /* 指令码 */ for (i 0; i n; i) { buf[12 i] params[i]; /* 参数 */ } for (i 8; i 12 n; i) { sum buf[i]; /* 从包标识累加到最后一个参数 */ } buf[12 n] (unsigned char)(sum 8); buf[13 n] (unsigned char)(sum 0xFF); recv_len 0; /* 清空接收缓冲避免粘包 */ for (i 0; i 14 n; i) { SBUF buf[i]; while (!TI); TI 0; } }核心是校验和那一段。sum使用Keil C51的unsigned int是16位累加溢出时自动截断正好是协议要求的低16位。填充顺序先高字节后低字节对应帧尾的00 05形式。等待应答函数要处理三件事收够长度、校验和、确认码。串口中断把字节收进recv_buf这里做超时轮询unsigned char as608_wait_ack(unsigned char cmd) { unsigned int timeout 0; unsigned int sum 0; unsigned char ack_len; unsigned char i; while (recv_len 11) { /* 先收到包长度字段 */ if (timeout 30000) return 0xFF; /* 超时 */ } ack_len ((unsigned int)recv_buf[9] 8) | recv_buf[10]; if (ack_len 4) return 0xFD; /* 应答包最短4字节 */ while (recv_len 11 ack_len) { /* 收完整包 */ if (timeout 50000) return 0xFF; } for (i 8; i 11 ack_len - 2; i) { sum recv_buf[i]; /* 校验和包标识到数据末 */ } if (recv_buf[11 ack_len - 2] ! (unsigned char)(sum 8)) return 0xFA; if (recv_buf[11 ack_len - 1] ! (unsigned char)(sum 0xFF)) return 0xFA; if (recv_buf[8] ! 0x07) return 0xFC; /* 必须应答包 */ if (recv_buf[12] ! cmd) return 0xFB; /* 回显指令匹配 */ return recv_buf[11]; /* 确认码 */ }注意应答包结构recv_buf[11]是确认码recv_buf[12]是回显指令码数据区从recv_buf[13]开始。返回值就是确认码0x00表示成功其他值对应各种错误。3.3 录入函数enroll_fp两次采集合成后存入指定ID录入函数接收一个16位ID参数把模板存到这个ID下。采集阶段要循环等待因为手指可能还没放上去模块会返回0x02无指纹/* 录入指纹到指定ID */ unsigned char enroll_fp(unsigned int id) { unsigned char p[3]; unsigned char st; while (1) { /* 第一次采集 */ as608_send_cmd(0x01, 0, 0); /* GenImg */ st as608_wait_ack(0x01); if (st 0x00) break; /* 采集成功 */ if (st ! 0x02) return st; /* 02无指纹继续等其他错误退出 */ } p[0] 0x01; as608_send_cmd(0x02, p, 1); /* GenChar 缓冲区1 */ st as608_wait_ack(0x02); if (st) return st; delay_ms(300); /* 提示换手指位置再按一次 */ while (1) { /* 第二次采集 */ as608_send_cmd(0x01, 0, 0); st as608_wait_ack(0x01); if (st 0x00) break; if (st ! 0x02) return st; } p[0] 0x02; as608_send_cmd(0x02, p, 1); /* GenChar 缓冲区2 */ st as608_wait_ack(0x02); if (st) return st; p[0] 0x01; p[1] 0x02; as608_send_cmd(0x05, p, 2); /* RegModel 合并特征 */ st as608_wait_ack(0x05); if (st) return st; p[0] 0x01; p[1] (unsigned char)(id 8); p[2] (unsigned char)(id 0xFF); as608_send_cmd(0x06, p, 3); /* StoreChar 存入指定ID */ return as608_wait_ack(0x06); }两次采集之间加300ms延时是让用户抬起手指重新按压提高两次采样的差异度。如果两次采集的特征完全一样合成模板时反而容易失败。3.4 验证函数verify_fpLoadChar后现场采集再Match验证函数先按ID把模板读到缓冲区2再现场采集特征到缓冲区1最后Match/* 验证指纹1:1比对结果通过score返回得分 */ unsigned char verify_fp(unsigned int id, unsigned int *score) { unsigned char p[3]; unsigned char st; p[0] 0x02; /* 读进缓冲区2 */ p[1] (unsigned char)(id 8); p[2] (unsigned char)(id 0xFF); as608_send_cmd(0x07, p, 3); /* LoadChar */ st as608_wait_ack(0x07); if (st) return st; while (1) { as608_send_cmd(0x01, 0, 0); /* 现场采集 */ st as608_wait_ack(0x01); if (st 0x00) break; if (st ! 0x02) return st; } p[0] 0x01; as608_send_cmd(0x02, p, 1); /* 现场特征放缓冲区1 */ st as608_wait_ack(0x02); if (st) return st; as608_send_cmd(0x03, 0, 0); /* Match */ st as608_wait_ack(0x03); if (st) return st; *score ((unsigned int)recv_buf[13] 8) | recv_buf[14]; return 0x00; /* 匹配成功 */ }Match成功时确认码为0x00应答数据区返回2字节的匹配得分。AS608的得分范围一般在0到255之间分数越高匹配度越好实际项目里可以在后又加一道阈值判断比如得分低于80就认为不够安全。3.5 确认码速查应答返回非0时先看这张表模块返回的确认码含义比较统一调试时对照这张表能少走很多弯路确认码含义常见原因0x00成功一切正常0x01收包错误帧格式不对校验和算错0x02无指纹手指没放或按压力度不够0x03录入图像失败手指太干或按放位置不对0x04图像太干/太淡手指干燥搓一下再按0x05图像太湿/太糊手指有汗或水渍0x07无此IDLoadChar或StoreChar的ID越界0x08指纹不匹配Match或Search时特征比对不上0x09未搜索到Search范围内没有匹配模板0x0A特征合并失败两次采集差异太大0x01是比较隐蔽的很多人改了指令但校验和没跟着变模块就回0x01。先查校验和函数再看地址字节是不是被改过。4. 方案2源码自动分配ID Search 1:N识别方案2解决的是“库里几十个指纹不知道是谁”的问题典型场景是多人考勤、多用户指纹锁。录入时不需要指定ID模块按顺序自动分配验证时用Search在库里搜索返回匹配的ID和得分。4.1 方案1和方案2的差异一张表看全对比项方案1方案2验证指令LoadChar MatchSearch录入时是否指定ID指定自动分配验证结果匹配/不匹配返回ID 得分指纹库规模几枚到十几枚几十枚到300枚典型应用单管理员、单用户锁多用户门禁、考勤机验证速度快与库大小无关随模板数量线性增长方案2的搜索范围是整个模板库AS608库容量上限一般是300枚Search指令的PageNum参数直接填300即可模块会跳过空页。4.2 自动分配IDReadValidTempNum指令方案2录入的关键是“该存到哪个ID”。常见做法是先查当前有效模板数把新模板存到序号等于模板总数的位置。比如库里已有5枚指纹新指纹就存到ID5。/* 读取当前有效模板数返回0x00成功num为模板总数 */ unsigned char read_valid_num(unsigned int *num) { unsigned char st; as608_send_cmd(0x1D, 0, 0); /* ReadValidTempNum */ st as608_wait_ack(0x1D); if (st) return st; *num ((unsigned int)recv_buf[13] 8) | recv_buf[14]; return 0x00; }如果模块固件对0x1D支持不理想可以用ReadSysPara0x0F读系统参数应答数据里同样包含已注册模板数字段但偏移位置因模块批次略有差异手上要有模块手册对照着看。4.3 录入函数 enroll_fp_auto先查重再自动分配方案2录入在合成模板前可以加一步查重先用Search搜一遍当前指纹如果搜到了说明已经录入过直接返回错误避免库里出现重复模板unsigned char enroll_fp_auto(unsigned int *new_id) { unsigned char p[3]; unsigned char st; unsigned int num; unsigned int old_id; unsigned int old_score; st search_fp(old_id, old_score); /* 先搜索当前手指 */ if (st 0x00) return 0xEE; /* 已录入过拒绝重复 */ /* 采集两次、生成特征、合并模板流程与方案1录入相同 */ while (1) { as608_send_cmd(0x01, 0, 0); st as608_wait_ack(0x01); if (st 0x00) break; if (st ! 0x02) return st; } p[0] 0x01; as608_send_cmd(0x02, p, 1); st as608_wait_ack(0x02); if (st) return st; delay_ms(300); while (1) { as608_send_cmd(0x01, 0, 0); st as608_wait_ack(0x01); if (st 0x00) break; if (st ! 0x02) return st; } p[0] 0x02; as608_send_cmd(0x02, p, 1); st as608_wait_ack(0x02); if (st) return st; p[0] 0x01; p[1] 0x02; as608_send_cmd(0x05, p, 2); st as608_wait_ack(0x05); if (st) return st; read_valid_num(num); /* 查询模板总数 */ p[0] 0x01; p[1] (unsigned char)(num 8); p[2] (unsigned char)(num 0xFF); as608_send_cmd(0x06, p, 3); /* 存到自动分配的ID */ st as608_wait_ack(0x06); if (st 0x00) *new_id num; return st; }查重这个步骤通过调用后面的search_fp实现如果库里本来就没有模板Search会返回0x09未搜索到这时直接继续录入即可。0xEE这个自定义错误码是为了和模块返回码区分调用方好判断。4.4 识别函数 search_fp采集 生成特征 Search全库方案2验证的核心函数是Search指令参数五个字节BufferID固定0x01、起始页码0x0000、搜索长度0x012C即300unsigned char search_fp(unsigned int *page_id, unsigned int *score) { unsigned char p[5]; unsigned char st; while (1) { /* 采集手指 */ as608_send_cmd(0x01, 0, 0); st as608_wait_ack(0x01); if (st 0x00) break; if (st ! 0x02) return st; } p[0] 0x01; as608_send_cmd(0x02, p, 1); /* 生成特征到缓冲区1 */ st as608_wait_ack(0x02); if (st) return st; p[0] 0x01; p[1] 0x00; p[2] 0x00; /* 起始页0 */ p[3] 0x01; p[4] 0x2C; /* 搜300页 */ as608_send_cmd(0x04, p, 5); /* Search */ st as608_wait_ack(0x04); if (st) return st; /* 0x09未搜到 */ *page_id ((unsigned int)recv_buf[13] 8) | recv_buf[14]; *score ((unsigned int)recv_buf[15] 8) | recv_buf[16]; return 0x00; }Search应答的数据区是4字节2字节页码对应匹配的模板ID2字节得分。返回值0x00表示搜到了0x09表示库里没有这枚指纹。注意页码是0开始的所以存进去的第0号模板Search返回的page_id就是0显示给用户时自行加1。4.5 Search参数到底怎么填Search的StartPage和PageNum这两组参数值得单独说。StartPage0表示从库的最开始搜PageNum300表示最多搜索300个ID模块遇到空页会自动跳过。如果确定库里模板都集中在前面也可以缩小PageNum来提速但一般没必要AS608搜索300枚模板的耗时在0.4秒左右对门锁场景足够了。这里有一个容易犯的逻辑错误不要把PageNum当成“库里有几枚就填几”。如果你填了当前模板数n而库里恰好有删除留下的空洞IDSearch会漏掉尾部模板。直接固定填300最稳妥空页不耗时。5. Keil4工程里跑AS608的3个坑和1个验证技巧最后这部分不是算法问题而是Keil4 C51工程里最影响“能跑起来”的实操细节。很多人在源代码移植阶段就卡住其实坑都很固定。5.1 坑一Keil C51和MDK是两套东西AS608这种单片机指纹识别源码用的是Keil C51工程文件后缀是uvproj编译51内核代码。如果你电脑上装的是Keil MDKARM版打开C51工程会提示找不到设备或者出现“keil4 mdk unknown product”这类注册相关的报错。解决办法是安装Keil C51版本使用C51的注册机生成LicenseMDK的License在C51里不生效。STC89C52在器件选择里没有直接列出选Atmel的AT89C52即可烧录用STC-ISP工具不影响寄存器操作。5.2 坑二57600改9600用串口助手发一条指令前面说过AS608默认波特率绝大多数是57600而STC89C5211.0592MHz跑57600误差太大。把模块改成9600的做法是先接USB-TTL模块电脑串口助手选57600发送下面这条十六进制帧设置波特率EF AB CD EF FF FF FF FF 01 00 08 0E 04 00 00 25 80 00 C0拆解一下0x0E是设置系统参数指令0x04是参数地址波特率控制0x00 0x00 0x25 0x80是9600的32位十六进制值0x00 0xC0是校验和。发送成功后先把模块断电再上电波特率才会真正生效。之后串口助手和51程序都改用9600。5.3 坑三上电自检数据会污染接收缓冲AS608上电后会主动通过串口发一段自检信息如果程序下载后立刻发指令这段数据会留在接收缓冲里导致as608_wait_ack收到错误字节校验和永远对不上。解决办法是main函数上电后延时500ms以上再执行一次recv_len 0清空缓冲然后再开始主循环。另外每次发指令前as608_send_cmd里已经做了清空这是对付应答粘包的双保险。5.4 验证技巧不用单片机的模块自检在Keil4下调试前先确认模块是好的。用USB-TTL模块接AS608串口助手选9600发送这条采集指纹指令EF AB CD EF FF FF FF FF 01 00 03 01 00 05手指放在模块上发送正常应答是EF AB CD EF FF FF FF FF 07 00 04 00 01 00 0C其中07是应答包标识00是确认码成功01是回显指令码00 0C是应答校验和。如果不放手指确认码位置会变成02。这一步能把模块好坏、波特率、校验算法全部验证一遍再回到51板子上调代码问题范围直接缩小一半。本文还有配套的精品资源点击获取
返回列表