
简介本资源是一套面向嵌入式初学者与51单片机实践者的AS608指纹识别综合实验程序聚焦指纹采集、串行通信与字符液晶显示三大核心功能的协同实现。项目以STC89C52等经典51单片机为主控通过UART接口驱动AS608指纹模块完成指纹录入、比对并将状态信息如“匹配成功”“未注册”“请按手指”等实时输出至LCD1602显示屏适用于门禁原型开发、课程设计及毕业设计验证场景。压缩包共56个文件含7个C源文件main.c、uart.c、lcd_1602.c、FPM10A.c等、6个头文件含通信协议定义与硬件引脚配置、12个OBJ编译中间文件及3个HEX可执行文件辅以LST汇编列表、UVPROJ工程配置与M51内存映射文件结构完整便于调试与二次开发。资源包大小仅131KB轻量易部署已有1276人学习下载提供即用型Keil工程含uvproj/uvopt/uvgui等无需额外配置即可编译烧录显著降低入门门槛。 很多刚接触生物识别项目的朋友第一次拿到AS608指纹模块往往都会先插到一个USB转TTL上用上位机软件试一下录入指纹、比对指纹全都在电脑上跑通了一切正常。结果把这个模块接到51单片机项目里搭配一块lcd1602做显示立刻开始出各种问题——要么LCD没反应要么发指令没应答要么识别到了但屏幕上什么都显示不出来。说到底是缺少一个能把“指纹模块、51单片机、LCD1602显示”这三样东西当成一个完整系统来验证的测试程序。这篇内容就是围绕这个需求来的适合正在做课程设计、毕业设计或者第一次把AS608接入51项目的朋友帮你把串口通信、指纹搜库、液晶显示整条链路彻底跑通。市面上的AS608教程很多但多数只给一个“烧进去能亮”的Demo不讲为什么这样接线、为什么这样配波特率、为什么LCD初始化要按那个顺序写。本文会用实际测试程序为主线把AS608的通信协议、帧格式、LCD1602的底层时序全部拆开讲并且把我实测过程中踩过的几个坑一并列出来。如果你手上正好有一块AS608和一个普中或者任意型号的开发板跟着做一遍基本就能把这块模块吃透。1. 这个测试程序到底在测什么先想清楚三个环节1.1 AS608不是“通电就能用”的传感器很多新手对指纹模块有个误解觉得它像LED一样上电就开始工作然后输出一个“有没有指纹”的电平信号。实际上AS608是一个典型的“串口外设”它内部有光学传感器、DSP处理芯片和指纹库存储区域但所有这些能力都要靠外部MCU通过UART发送指令来调度。你要先发一条“采集图像”的指令它才去拍你按上去的手指再发一条“生成特征”的指令它才会从图像里提取特征值再发一条“搜索”指令它才会把当前特征值和指纹库里的模板做比对然后把匹配到的ID和相似度分数回传。这意味着在51单片机上做AS608的开发本质上是在写串口通信程序而不是在写“指纹识别程序”。指纹识别算法已经固化在模块内部了我们的任务只是把命令按协议拼好、发出去再把应答包解析出来。理解这一点整个项目的难度就下降了一大半。1.2 51单片机在这个项目里的角色用51单片机来做这个测试很多人觉得是不是太弱了会不会跑不动。说实话AS608的指纹比对计算根本不在51上完成51只需要做三件事接收按键输入、通过串口和AS608对话、把结果显示到LCD上。这些工作对主频要求极低标准的STC89C52RC就能轻松胜任。选51而不是STM32还有个现实原因学校实验室、课程设计、大部分入门开发板都基于51资料多、成本低而且把串口协议和数据帧解析跑通了以后换任何MCU都是同样的思路。我见过不少同学上来就用STM32加HAL库结果底层串口收发出问题调了一周最后发现连AS608的应答帧格式都没搞明白。先把51版本的协议层打通再移植到其他平台才是稳妥路线。1.3 LCD1602在测试程序里的真实分工LCD1602在这个测试程序里不只是“显示好看”它承担着调试反馈的核心职责。AS608的通信是异步的指令发出去之后模块什么状态你光看模块本身是不知道的。1602可以把每一步的结果实时显示出来当前是录入模式还是比对模式、手指有没有按上去、图像采集是否成功、搜索到了哪个ID、匹配分数是多少。相当于给整个系统加了一个“可视化日志”。另外1602驱动本身就是51单片机教学里的经典内容把LCD时序和AS608串口时序放在同一个项目里恰好可以一次性验证两个知识点。等这套测试程序跑通你等于同时掌握了液晶显示和串口通信两块基础能力后面的扩展项目都会顺畅很多。2. AS608的通信协议数据包、校验和、应答码一次讲透2.1 串口参数9600还是57600先解决最基础也最容易被卡住的问题波特率。AS608模块出厂默认波特率通常是57600bps但我做测试程序时用的是9600bps这里有两个原因。第一个原因51单片机最经典的11.0592MHz晶振搭配定时器1工作在方式2SMOD设为0TH1和TL1都设为0xFD可以产生误差为0的9600波特率。这是51串口最稳定、最常用的一套配置几乎所有教程和开发板例程都是这个参数。第二个原因如果你的模块出厂是57600你需要先把模块通过USB转TTL接到电脑上用串口助手发指令把模块波特率改成9600再接到51上。AS608支持波特率修改指令具体可以参考模块附带的指令集文档。改完之后一定要重新上电让参数生效。关于57600波特率在STC89C52上的使用我多提醒一句11.0592MHz晶振下确实可以配置出57600但要SMOD1且TH1很接近溢出边界误差和稳定性都不理想。如果一个项目非要跑57600最好把晶振换成22.1184MHz。如果你只是做验证测试老老实实统一改到9600就好。2.2 一帧指令和应答帧的字节级拆解AS608的所有通信都基于固定格式的数据包。理解这个包结构是后面所有代码的基础。我按最常见的协议格式拆开讲。指令包结构如下字节组成占用字节数说明包头2固定为0xEF 0x01模块地址4默认是0xFF 0xFF 0xFF 0xFF包标识10x01表示命令包0x02表示数据包0x07表示应答包包长度2从包标识开始到校验和之前的所有字节总数指令码/数据N具体功能指令或传输的数据校验和2包标识到数据末尾所有字节累加和的低16位举个例子让AS608采集一幅指纹图像指令码是0x01没有附加参数。那么整包就是EF 01 FF FF FF FF 01 00 04 01 00 06这里包长度是0x0004它包含包标识0x01、指令码0x01、校验和高字节0x00、校验和低字节0x06这四个字节。校验和就是从0x01包标识开始到0x01指令码结束累加得到0x02后面两位校验和就是0x00 0x02不对我重新写一下。0x01 0x00 0x04 0x01 0x06所以校验和是0x00 0x06。校验和计算时从包标识开始把包标识、包长度的高字节、包长度的低字节、后面的数据全部累加。上面这个例子里包长度字段本身的两个字节也要算进去所以是0x01 0x00 0x04 0x01 0x06。最终整帧就是EF 01 FF FF FF FF 01 00 04 01 00 06对应关系是包头2字节、地址4字节、包标识0x01、包长度0x0004、指令码0x01、校验和0x0006。2.3 校验和算法与代码级实现AS608用的是累加和校验不是CRC这一点比Modbus简单很多。计算规则就是从包标识字节开始一直到数据区最后一个字节把所有字节加起来取16位累加和的低16位高字节在前发送。用C语言实现一个通用发送函数很直观。假设我们已经有了一个底层发送单字节的函数那么构建命令帧可以这样写void AS608_SendCmd(uint8_t pid, uint8_t *data, uint8_t len) { uint8_t i; uint16_t sum 0; UART_SendByte(0xEF); UART_SendByte(0x01); UART_SendByte(0xFF); UART_SendByte(0xFF); UART_SendByte(0xFF); UART_SendByte(0xFF); UART_SendByte(pid); sum pid; UART_SendByte(((len 3) 8) 0xFF); sum ((len 3) 8) 0xFF; UART_SendByte((len 3) 0xFF); sum (len 3) 0xFF; for (i 0; i len; i) { UART_SendByte(data[i]); sum data[i]; } UART_SendByte((sum 8) 0xFF); UART_SendByte(sum 0xFF); }注意这里的包长度len 3的含义它指包标识、数据区和校验和三部分的总字节数也就是包标识1个字节、数据区len个字节、校验和2个字节加在一起是len 3。这个定义要和接收解析时的长度计算保持一致不然会差一个字节导致模块判你“收包错误”返回确认码0x01。2.4 项目里最常用的四条指令和返回码含义AS608的指令集有二十多条但做录入和比对测试真正需要吃透的就四条指令指令码参数作用PS_GetImage0x01无让模块采集当前手指图像PS_GenImg0x02缓冲区号0x01或0x02从图像生成特征值并放入指定缓冲区PS_Search0x04缓冲区号、起始页、页数在指纹库中搜索匹配模板PS_RegModel0x05无合并两个缓冲区特征生成模板PS_Store0x06缓冲区号、存储页号将模板存入指纹库指定位置这里有个容易混淆的点录入一枚指纹需要采集两次图像分别放到缓冲区1和缓冲区2然后调用PS_RegModel把两个特征合并成一个模板最后用PS_Store存到指纹库里。比对时只需要采集一次图像生成特征后直接用PS_Search在库里搜。每次指令执行后AS608都会回传一个应答包应答包里的确认码就是判断结果的关键。0x00 操作成功 0x01 收包错误 0x02 传感器上无手指 0x03 图像采集失败 0x04 图像太乱或手指太干太湿 0x05 指纹特征不匹配 0x06 搜索不到匹配指纹 0x07 特征合并失败 0x09 指纹库已满调试的时候遇到问题第一件事就是看确认码是多少再对照这个表定位原因。我以前有段时间一直报0x03后来发现是手指放得太快传感器还没稳定就发采集指令加上延时就好了。3. 接线与电平烧过一次模块后我才明白的注意事项3.1 引脚对照表与接线图AS608模块的排针一般是8个引脚和51单片机连接时只需要接其中的5根左右。先给一个标准的接线对照表AS608引脚连接到51单片机说明VCC5V电源模块工作电压注意不要接3.3VGNDGND共地必须接TXP3.0RXD模块发送给单片机RXP3.1TXD单片机发送给模块TOUCH悬空或接IO手指触摸检测输出测试程序不依赖WAKEUP悬空或接高电平休眠唤醒引脚VCC_2悬空部分模块无此引脚具体看板子丝印要注意AS608的TX和RX是相对于模块自身的所以单片机的TXD要接模块的RX单片机的RXD要接模块的TX不能接反。接反的典型现象是发送指令后完全无应答或者收到一堆乱码。3.2 P0口上拉这个问题最容易忽略如果LCD1602的数据口用的是P0口一定要注意标准8051的P0口内部没有上拉电阻是开漏输出。直接接LCD的数据线会导致高电平驱动能力不足LCD初始化很可能失败表现是屏幕白屏、背光亮但无字符或者字符很暗淡。解决办法是在P0口外面接一排10k欧姆的排阻到VCC作用是把P0口的高电平拉上去。很多51开发板上的P0口已经带了排阻如果你用的是裸板这一步一定要处理。不然你花几个小时调LCD初始化最后发现是硬件上拉问题那就太浪费了。我实际测试时用普中的开发板P0口自带排阻所以直接接就正常。后来换成自己焊的板子忘了放排阻LCD完全不出字折腾了很久才发现是上拉问题。这个问题在小项目里特别隐蔽因为Keil编译不会报错程序逻辑看起来也完全正常。3.3 供电余量指纹传感器瞬间电流有点大AS608模块的光学传感器在采集图像的瞬间电流会明显增大峰值可能接近100mA级别。如果用一个质量很差的USB供电或者用51开发板上的AMS1117稳压芯片供电电压跌落就可能导致模块异常复位表现是发指令偶尔没应答或者采集图像总是失败。稳妥的做法是给模块单独用外部5V电源供电或者至少保证电源能提供300mA以上的电流。如果手里只有USB转TTL模块也要用带供电能力的不要用那种只有几毫安输出的CH340小板直接硬撑。3.4 晶振与波特率的关系51单片机用11.0592MHz晶振最主要的理由就是它能整除出标准的串口波特率。9600波特率下定时器1溢出频率是307200Hz除以32正好得到9600误差为0。这个数字不是随便定的它是从经典的8051串口波特率计算公式反推出来的。如果换用12MHz晶振算出来的波特率是9600的话会有误差长时间通信可能出现偶发乱码。AS608这种数据包通信对位错误特别敏感一个字节错了整帧校验和就对不上表现出来就是“时好时坏”。所以做这个项目晶振一定要用11.0592MHz不要图省事从别的板子上拆一个12MHz换上。4. LCD1602手写驱动先把时序搞对再讲封装4.1 驱动初始化序列的每一步在干什么LCD1602基于HD44780控制器上电后必须按照手册规定的初始化顺序操作否则显示屏就是白屏或花屏。网络上很多代码都把初始化写得花花绿绿但核心其实就是“三次0x30唤醒 功能设置 显示控制 清屏”这几步。以8位数据模式为例完整初始化流程如下上电后等待40ms以上让LCD内部电路稳定。连续写入0x30三次每次之间加若干毫秒延时这是为了唤醒控制器进入8位模式。写入0x38设置8位数据口、2行显示、5x8点阵字符。写入0x08关闭显示防止初始化过程闪烁。写入0x01清屏等待约2ms。写入0x06设置写入数据后光标右移、画面不滚动。写入0x0C打开显示、关闭光标和闪烁。每次写命令之前都必须把RS引脚拉低表示当前写入的是命令写数据之前把RS拉高。写操作之前还要把RW拉低然后通过E引脚产生一个下降沿脉冲数据在E下降沿被锁存。4.2 忙检查还是延时到底用哪个LCD1602初始化之后每次写命令或数据都需要等待控制器内部处理完成。标准做法是读取忙标志BF也就是读DB7引脚如果BF为1说明控制器忙不能写入新数据。读取忙标志能最大化利用LCD的处理时间不会出现“上一个命令还没完成就写下一个”的问题。但在51这种速度不高的单片机上延时法其实更简单也更可靠。因为51本身跑得慢LCD处理一条命令的时间大约是40微秒到1.6毫秒你写一个几十微秒的延时函数基本就能覆盖大部分命令的处理时间。唯一要注意的是清屏指令需要等待约2ms延时要给足。我推荐在测试程序里直接使用延时法把代码量减下来逻辑也更清晰。忙检查适合对速度有要求的场景比如以后用STM32驱动LCD实时刷新大量数据时再去做优化。4.3 显示层封装让业务代码只关心字符串驱动层的函数设计得再好如果业务代码里到处是LCD_WriteCommand和LCD_WriteData读起来会非常痛苦。实际开发中我更习惯封装出几个高层函数LCD_Clear()、LCD_SetCursor(row, col)、LCD_PrintString(char *str)、LCD_PrintHex(uint8_t val)。这样一来主程序里处理AS608结果时代码可以写成LCD_SetCursor(0, 0); LCD_PrintString(Match OK); LCD_SetCursor(0, 1); LCD_PrintString(ID:01 Score:125);至于底层怎么实现业务代码完全不关心。这种“驱动层与业务层分离”的思路在单片机小项目里一样适用而且对后续维护特别有帮助。1602只能显示ASCII字符中文显示不了测试程序里就用英文提示这也是实测经验别试图在一颗不带字库的1602上直接显示“录入成功”四个字。5. 完整测试程序拆解录入和比对两条链路5.1 串口接收状态机把AS608的应答包完整接住发送指令相对简单难点在接收。AS608应答包长度不固定短的只有确认码长的比如读系统参数会有几十个字节所以不能用简单的“收到3个字节就处理”的思路。我一般用串口中断配合状态机一个字节一个字节地解析收满一整帧后再置标志位主循环轮询这个标志。状态机的工作流程是先找包头0xEF 0x01然后跳过4字节地址再收包标识、包长度最后根据包长度收完剩余字节。由于我们已经知道整帧总长度是9 packet_len所以收满这么多字节就认为一帧完成。用中断接收的好处是不会丢字节。51的串口接收缓冲区只有一个字节如果不及时取走后续数据来了就会覆盖导致帧错乱。中断方式可以做到来一个字节处理一个字节稳定性要高很多。5.2 命令帧构建一条通用函数解决所有指令前面在第2章已经给出了AS608_SendCmd的实现它接受包标识、数据指针和数据长度三个参数任何指令都可以复用。比如发“采集图像”指令调用方式就是AS608_SendCmd(0x01, NULL, 0);发“搜索”指令参数是一个包含缓冲区号、起始页、页数的数组uint8_t param[6]; param[0] 0x01; // 使用缓冲区1 param[1] 0x00; // 起始页高字节 param[2] 0x00; // 起始页低字节 param[3] 0x00; // 搜索页数高字节 param[4] 0xA0; // 搜索页数低字节160页 param[5] 0x00; // 校验和会在发送函数里补 AS608_SendCmd(0x04, param, 5);注意AS608_SendCmd会自动计算校验和不需要自己在参数里填校验和。这里的搜索范围是0x00A0也就是160页如果指纹库里存了多枚指纹搜索时会把整个库都查一遍。5.3 主流程按键、消抖、超时、LCD反馈主程序可以设计成两个按键一个是“录入”一个是“比对”。每次按下按键都做一次软件消抖然后进入对应的流程。这里有一个容易被忽略的点发送完AS608指令之后不能立刻发下一条必须等模块应答。模块处理图像采集、特征提取通常需要几百毫秒所以每一条指令都要有超时保护超时后返回错误并显示在LCD上。录入流程是调用PS_GetImage等手指放上去确认码为0x02说明没有手指显示“Put finger again”。调用PS_GenImg把图像生成特征存入缓冲区1。再次调用PS_GetImage和PS_GenImg把第二枚特征存入缓冲区2。调用PS_RegModel合成模板。调用PS_Store把模板存到指定页。显示“Enroll OK”和存储页号。比对流程是PS_GetImage采集当前手指。PS_GenImg生成特征到缓冲区1。PS_Search在指纹库里搜索。如果确认码是0x00解析应答包中的页码和分数显示在LCD上。如果确认码是0x06说明库里没有匹配显示“No Match”。每一条指令之间都要加适当延时特别是PS_GetImage之后要给传感器留足曝光和处理时间不要立刻发PS_GenImg。我实测加200到300毫秒延时最稳妥。5.4 完整代码参考下面给出一份可以直接参考的测试程序骨架结构是按模块拆分的完整工程可以在这个基础上继续补全。#include STC89C5xRC.H #include string.h #define LCD_RS P2^0 #define LCD_RW P2^1 #define LCD_E P2^2 #define LCD_DATA P0 #define KEY_ENROLL P3^2 #define KEY_MATCH P3^3 #define FRAME_HEAD1 0xEF #define FRAME_HEAD2 0x01 #define DEFAULT_ADDR 0xFFFFFFFF volatile uint8_t rx_buf[64]; volatile uint8_t rx_index 0; volatile uint8_t rx_state 0; volatile uint16_t rx_packet_len 0; volatile bit frame_done 0;串口中断和接收状态机后续就是按照之前分解的逻辑逐字节处理。这里我建议把状态机的状态编号写清楚调试的时候可以放几个断点看状态走到哪个阶段。然后LCD底层函数、AS608指令封装、主循环按同样的风格组织。整体代码量在300行左右在Keil C51下编译没有问题。实际烧录时注意使用STC-ISP软件选择对应的单片机型号晶振频率选11.0592MHz然后下载到开发板。6. 实测中的坑与排查方法从白屏到稳定识别6.1 故障现象对照表做一个串口外设项目出问题时最先要做的不是改代码而是根据现象判断问题在哪个层面。我把自己踩过的和帮别人debug遇到的典型现象整理成了一个表现象可能原因优先排查方向LCD白屏对比度电位器没调好调节LCD背面电位器LCD白屏P0口缺少上拉电阻检查P0排阻LCD有显示但字符乱码初始化时序不对检查0x38和初始化延时发指令完全没应答TX/RX接反检查AS608的TX接单片机RXD发指令偶尔有应答波特率不匹配用串口助手确认模块当前波特率采集图像一直确认码0x02手指放得太慢程序加提示和延时等手指放稳比对总是确认码0x06指纹库为空或手指变化先录入再比对多试几个手指识别率很低环境光干扰、手指太干调整手指按压角度保持传感器干净这个表能帮你快速缩小问题范围。比如LCD不出字就不要再花时间查AS608了先单独烧一个LCD跑马灯例程确认显示链路是通的。把问题隔离到最小范围是嵌入式调试的基本原则。6.2 模块地址被改过默认地址不是永远成立AS608默认模块地址是FF FF FF FF大多数例程也按这个固定地址来组帧。但有个很隐蔽的坑模块的地址是可以被改写的。如果之前有人用上位机软件或者调试指令把地址改成了别的值你在程序里写死FF FF FF FF模块就会认为这个包不是发给自己的直接丢弃现象就是“发任何指令都没有应答”。遇到这种情况最稳妥的办法是先用USB转TTL把模块单独接到电脑上用串口助手发一条读系统参数的指令观察应答包里的模块地址字段。如果发现地址不是默认值可以通过写系统参数指令改回来或者直接在程序里把地址改成实际值。这个坑在学校实验室特别常见因为一块模块可能被好几个人用过谁也不知道上一手的人改了什么配置。所以我现在的习惯是拿到模块先接USB转TTL做一次“体检”确认默认地址和默认波特率再往51板子上接。6.3 提升识别率的几个实操细节如果程序逻辑完全正确但实际使用中发现AS608识别不够灵敏问题往往出在物理层。手指按压传感器表面时要保证覆盖面积足够大轻轻按压但不能太用力。太用力会让指纹脊线变形反而降低匹配分数。太干的手指指尖角质层厚图像对比度低可以用嘴哈一点气或者用湿布擦一下指尖再试实测能明显提升识别成功率。环境光对AS608也有影响。传感器窗口如果正对强光采集到的图像背景噪声会变大导致特征提取失败。如果做的外壳上有开孔要避免开孔位置让阳光直射到传感器表面。指纹库搜索页数也不是越大越好。指纹模块的指纹库存储页数有限比如常见的是200到300页但每搜一次索引都要时间。如果指纹库里有100枚指纹搜索范围设到160页就足够没必要设成满库这样还能稍微提高响应速度。6.4 关于这套测试程序的扩展方向把测试程序跑通之后接下来的扩展方向就很清晰了。最典型的做法是加一个继电器模块在比对成功时控制电磁锁开锁这就变成一个简易的指纹门禁系统。再往后可以用STC12或者STC15这类1T单片机性能和资源更强而且老的代码基本不用大改。也可以把LCD1602换成OLED屏幕显示更细腻代码的驱动层替换一下就行AS608协议层完全不用动。这也是当初为什么要把协议封装成独立函数的原因——模块化设计在项目长大后带来的好处谁用谁知道。如果想把AS608接入更复杂的系统比如ESP8266或者蓝牙模块做无线指纹锁那么串口协议部分仍然可以复用只需要把51的处理逻辑换成新平台的上位机即可。整条链路里最值钱的东西其实就是你现在已经掌握的“用单片机和一个异步串口外设通信”这种通用能力。本文还有配套的精品资源点击获取