拓冰建站拓冰建站
首页 / 资讯中心 / 正文

STM32 HAL库实战:AS608指纹模块串口驱动与协议解析

简介这是一份面向STM32开发者的AS608指纹识别模块HAL库驱动资源源自西西菜鸟STM32_HAL库开发专栏。资源聚焦AS608与STM32之间的串口通信涵盖模块初始化、指纹图像采集、模板匹配及数据管理等关键环节可直接调用HAL库函数替代底层寄存器操作显著提升开发效率。压缩包共2个文件包含1个头文件和1个源文件整体仅6KB代码紧凑便于移植至已有工程。当前已有739人学习下载适合正在搭建指纹门禁、考勤系统或安全验证装置的嵌入式开发者参考。通过阅读源码可掌握UART参数配置、命令帧封装与解析、指纹模板存储等具体实现思路为快速集成AS608提供一套可直接复用的底层驱动模板。 直接以博文内容开始1. 为什么我在指纹方案里选了AS608和HAL库先说结论AS608这款指纹模块应该是入门级指纹方案里量产最多、资料最全、踩坑成本最低的一个选择。配合STM32F103C8T6这种几块钱的MCU整套方案的成本能压到二十块钱以内但功能上该有的都有了——指纹录入、搜索比对、删除模板、读指纹库状态全都能通过串口指令完成。我最早接触这个东西是在一个门禁小项目上需求很简单按一下录入指纹再按一下验证指纹匹配成功就开锁。当时第一版用的是某宝上最常见的AS608模块带一个蓝色背光指纹头供电5V串口默认波特率57600板子上还引出了TXD、RXD、TOUCH、VCC、GND这几个关键引脚。整个模块核心就干了三件事采集指纹图像、提取特征点、比对特征值MCU完全不参与算法只需要通过串口收发协议包就行。这也正是我选择AS608而不是自己用传感器加算法实现的原因——指纹识别的算法门槛太高什么图像增强、脊线提取、细节点匹配自己做一版没有半年下不来。AS608把这一层全部封装好了MCU侧只需要处理协议非常适合用HAL库快速开发。再说为什么用HAL库而不是标准外设库。很多人是从标准库转过来的觉得HAL库代码又啰嗦又绕。但我的感受是HAL库虽然封装层次多但它把外设的操作抽象得很干净串口、DMA、中断这些资源用结构体句柄管理写应用层逻辑的时候特别顺手。尤其是串口接收这种需要中断加DMA配合的场景HAL库提供的回调机制能让代码结构清晰很多。当然前提是你得先耐住性子把HAL库的套路摸熟一旦跑通一个外设剩下的就是举一反三的事。2. AS608协议拆解不懂数据包结构后面全白费2.1 通信链路它就是一块串口从设备AS608对MCU来说就是一个纯串口从设备你给它发指令包它给你回应答包。物理层就是UART默认波特率57600、8位数据位、无校验、1位停止位。供电方面模块VCC接5VTTL串口电平接STM32的PA9、PA10USART1。关于电平这块多说一句AS608的逻辑电平是3.3V兼容的所以可以直接和STM32的引脚对接不需要电平转换电路实测稳定但前提是共地VCC和GND必须和MCU共地。2.2 指令包结构固定的九字节起手式AS608的协议包结构很有规律格式是包头(2字节) 芯片地址(4字节) 包标识(1字节) 包长度(2字节) 指令/数据(N字节) 校验和(2字节)包头固定是0xEF01高低字节顺序别搞反发送时先发0xEF再发0x01。芯片地址默认是0xFFFFFFFF这个地址可以修改但不建议动用默认值就行。包标识很关键0x01表示命令包0x02表示数据包0x07是应答包0x08是结束包。MCU发指令用0x01模块回复用0x07。包长度是从指令/数据开始到校验和之前的字节数。比如一个指令包只有一个指令码加一个参数包长度就是0x03 0x00高字节在前。校验和是所有除了包头0xEF01之外所有字节的累加和取低16位也是高字节在前。用一个具体的例子验证口令指令码0x01默认密码是0x00000000的完整命令包就是EF 01 FF FF FF FF 01 00 07 01 00 00 00 00 00 09这里01 00 07里的00 07就是包长度7表示后面01 00 00 00 00 00 09一共7个字节其中最后00 09是校验和。2.3 应答包与确认码排查问题的第一把钥匙模块收到指令后会回一帧应答包结构一样但包标识是0x07里面的内容会带一个确认码。确认码是0x00表示成功非零就是各种问题确认码含义对应实际场景0x00成功一切正常0x01收包错误指令格式不对或串口数据错乱0x02传感器无指纹手指没有放到传感器上0x03录入图像失败采集指纹图像时失败0x04指纹太干或太淡手指太干特征点提取不到0x05指纹太湿或太糊手指有水渍或油污0x07特征点太少手指放偏了或指纹太浅0x08指纹不匹配两次按的不是同一个手指0x09未搜索到指纹库里没有这个指纹0x0A合并特征失败两枚特征无法合成模板0x0D指纹库已满模板数量达到上限0x0E未找到对应ID操作不存在的指纹ID这个表建议收藏后续写代码调试的时候80%的问题都能通过确认码快速定位。3. HAL库下串口驱动的搭建初始化、接收中断、空闲DMA3.1 串口初始化一边抄一边理解我用的开发环境是STM32CubeMX Keil这也是HAL库开发的主流姿势。CubeMX生成的初始化代码会自动帮你把时钟树、GPIO模式、串口参数配好。关键配置如下USART1异步模式Asynchronous波特率57600数据位8无校验1位停止位。PA9TX、PA10RX会被自动配成复用推挽模式。使能USART1全局中断优先级根据你的系统情况设一般设个中等优先级就行。如果需要DMA接收在DMA设置里给USART1_RX添加一个DMA通道模式选Circular循环模式数据宽度都是Byte。CubeMX生成完之后main.c里的MX_USART1_UART_Init()函数会自动把这些配置搞定。有一点要注意CubeMX默认不会开启串口的空闲中断这个需要我们自己在代码里手动加。空闲中断IDLE在HAL库里的开启方式是__HAL_UART_ENABLE_IT(huart1, UART_IT_IDLE);DMА接收要先启动HAL_UART_Receive_DMA(huart1, rx_buffer, RX_BUFFER_SIZE);这里rx_buffer是你要接收数据的缓冲区RX_BUFFER_SIZE是缓冲区大小。DMA配成循环模式的含义是数据一直往缓冲区里写写满了从头再写永远不停止。配合空闲中断就能实现不定长一帧数据收完后自动停下来处理的效果。3.2 空闲中断DMA一帧数据完整的正确姿势AS608的应答帧长度是不固定的最短的应答帧可能只有11个字节左右带上模板数据的最长帧能到二百多字节。所以不能用固定长度的串口接收必须用空闲中断DMA循环接收的方案。原理可以这样理解DMA负责默默地把串口收到的每一个字节搬进缓冲区中间不需要CPU干预。当一帧数据发送完毕、串口总线空闲下来的时候硬件会触发一个空闲中断。我们在中断里判断一下DMA挪动了多少字节就能知道这一帧有多长然后去缓冲区里取出完整数据。在HAL库中空闲中断的回调处理逻辑通常在串口中断回调函数或直接写在中断服务函数里。我习惯复用HAL库的中断处理流程重写HAL_UART_IRQHandler之后的回调。常用的做法是在stm32f1xx_it.c里找到USART1_IRQHandler调用HAL库函数后再判断IDLE标志void USART1_IRQHandler(void) { uint32_t isr READ_REG(huart1.Instance-SR); if (isr UART_FLAG_IDLE) { __HAL_UART_CLEAR_IDLEFLAG(huart1); uint16_t len RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(hdma_usart1_rx); if (len 0) { as608_rx_len len; as608_rx_complete_flag 1; } } HAL_UART_IRQHandler(huart1); }__HAL_DMA_GET_COUNTER这个函数拿到的是DMA剩余未传输的字节数用缓冲区总大小减去它就是这一帧已经收到的字节数。这个思路几乎适用于所有不定长串口帧接收场景不只是AS608。3.3 发送侧一个统一的发送函数省很多事发送侧我用一个统一的函数封装命令包发送所有指令都往里塞。核心逻辑是构造好包头、地址、包标识、长度、指令和校验和然后一次性通过HAL_UART_Transmit发出去。void AS608_SendCommand(uint8_t *cmd, uint8_t len) { uint8_t buf[64]; uint16_t sum 0; buf[0] 0xEF; buf[1] 0x01; buf[2] 0xFF; buf[3] 0xFF; buf[4] 0xFF; buf[5] 0xFF; buf[6] 0x01; // 命令包 buf[7] (len 2) 8; buf[8] (len 2) 0xFF; for (int i 0; i len; i) { buf[9 i] cmd[i]; sum cmd[i]; } memcpy(buf, buf, 9); // 前面包头等固定字节不计入sum这里逻辑要注意sum只从命令内容开始累加这里有个容易搞混的地方校验和的计算范围是从芯片地址开始的也就是说0xFFFFFFFF这四个字节也要算进去而不是只累加指令内容。我第一次写的时候就只加了指令部分结果模块一直回确认码0x01排查了好久才发现。严谨的写法应该是uint16_t sum 0; // 地址4字节 for (int i 2; i 6; i) sum buf[i]; // 包标识 sum buf[6]; // 包长度2字节 sum buf[7]; sum buf[8]; // 命令内容 for (int i 0; i len; i) sum buf[9 i]; buf[9 len] sum 8; buf[9 len 1] sum 0xFF;这个细节很多现成的驱动库里都写对了但如果你是从零自己写非常容易踩进去。4. 核心指令逐一实现录指纹、搜指纹、删指纹4.1 录入指纹的完整流程六步一个都不能少AS608录入一枚指纹并不是一条指令就完成的它需要两条指纹图像、两次特征提取、一次模板合成、一次模板存储。完整流程是0x01验证口令确认通信正常。0x02录入图像GenImg手指放到传感器上模块采集图像。此处确认码必须为0x00如果返回0x02说明手指没放好0x04/0x05说明手指干湿状态不对。0x03生成特征缓冲ID为1。把刚才采集的图像转化成特征模板存到模块内部缓冲区的1号位置。延时几百毫秒提示用户移开手指再重新按一次。再次0x02录入图像然后0x03生成特征缓冲ID为2。注意这里缓冲ID必须填2不能又填1。0x04合成模板RegModel把缓冲区1和缓冲区2的两个特征合并成一个完整的指纹模板。返回0x08说明两次按的不是同一个手指返回0x0A说明特征质量太差合并失败。0x05存储模板Store指定存储的指纹ID比如从1开始递增把模板写入模块Flash。其中指令0x05的格式比较特殊它需要两个字节的PageID参数高字节在前。比如要存储到ID 1指令内容就是05 00 01。代码层面我会用一个状态机来管理录入流程。用户按一次键状态往前走一步每一步都打印或上报确认码方便知道卡在哪。模块的处理速度需要时间比如GenImg要几百毫秒每次发完指令不能立刻发下一条最好加100ms以上的延时或者等待模块应答后再进入下一步。4.2 搜索比对一帧完成匹配搜索指纹比录入简单得多只需要两条指令0x01验证口令。0x02录入图像。0x03生成特征缓冲ID随便1就行。0x0B搜索指纹Search参数是起始ID和搜索数量。起始ID通常填0搜索数量填100或芯片容量上限让模块在整个指纹库里找匹配。指令内容0B 00 00 00 63 // 起始ID0搜索99个或全部注意这里的搜索数量如果填的是100但里面实际只有3枚指纹模块一样会正常返回。确认码0x00表示匹配成功应答里会带上匹配到的指纹ID和匹配分数。确认码0x09就是没搜到。拿到ID后去做联动比如开锁、放行整个逻辑就通了。4.3 删除与查询日常维护的三板斧删除指令是0x06DeleteChar参数也是指纹ID。注意它一次只能删一个ID要清空整个指纹库得用0x07Empty这条指令发完直接全部清空没有二次确认测试的时候别手滑。查询指纹库状态可以用0x09ReadSysPara它会返回一堆系统参数其中包含模板容量和已注册指纹数量。我抄作业的时候会直接解析固定偏移量但严谨的做法是把整包应答打出来对着数据手册核对每个字段的偏移。还有一种常见需求是读取指定ID的模板指令是0x0CLoadChar先加载到缓冲区再用0x0D上传特征。这套组合对于做指纹备份、迁移到另一个模块这类功能是必不可少的。5. 实测中的坑与排查思路按确认码一步一步倒推说实话AS608本身不复杂真正让人崩溃的往往是环境问题和细节疏忽。我梳理了几个在最少三次独立项目中都碰到过的坑按排查链路写出来。5.1 模块回0x01收包错误先怀疑自己的帧构造这是我遇到的第一个坑。现象是发什么指令模块都回确认码0x01。排查链路是第一步拿逻辑分析仪或示波器看串口波形确认发送的波特率是不是真的是57600。之前有一次CubeMX里改过系统时钟串口波特率实际失效了。第二步检查发送缓冲区内容重点看包头、地址、长度、校验和。我贴一下我踩过的那个校验和错误——只累加了指令内容漏了地址和长度。这种错误用现成驱动库时基本不会犯但自己手写时防不胜防。第三步确认STMCU的TX引脚是不是真的连到了模块的RX别接反了。模块的TXD要连MCU的RXPA10RXD连MCU的TXPA9共地就这么简单。5.2 模块一直不回包八成不是代码问题如果发送指令后模块完全没动静先别急着调试代码。排查顺序是摸一下模块背面发烫吗不烫说明供电有问题。用万用表量VCC和GND5V供电有没有到位AS608虽然标称5V但我实际遇到过3.3V也能工作的情况只是不稳偶尔会无响应。确认模块有没有自检通过。大多数AS608模块上电瞬间指示灯会闪一下蓝色背光点亮如果完全没反应模块可能坏了。用USB转TTL把模块直接接到电脑串口助手发一条验证口令的Hex指令看模块是否回包。这一招能直接把模块问题和MCU代码问题切分开排查效率最高。5.3 指纹总是录入失败解决干手指和湿手指问题确认码0x04指纹太干是我在冬天最常碰到的。解决方案是让用户手指先哈口气或者用湿毛巾擦一下。0x05指纹太湿则是反过来夏天手心出汗时容易出擦干再按。还有0x07特征点太少一般是手指放的位置不对只压到了传感器的一部分或者按压力度太大导致指纹变形。我在录入流程里加了重试引导逻辑失败时提示用户调整按法最多允许重试3次超过后退出录入门。实际体验比一次失败就全部重来好很多。5.4 搜索匹配率低校准按压方式而不是换模块有些项目反馈搜索时经常返回0x09未匹配排除模块质量问题后大都是录入和搜索时的按压方式不一致导致的。录入时手指是正着放的验证时歪了45度特征点对不上。AS608的传感器面积很小建议在结构设计上加一个定位凹槽引导用户用固定的角度和位置按压。纯软件层面能做的是录入时连续采集两次让用户稍微挪动手指位置再按第二次这样合成出来的模板适应性更强搜索时的容错率会高不少。6. 工程化落地状态管理、掉电存储、用户提示6.1 用全局回调代替串行阻塞体验好一个档次纯阻塞式的流程是这样的MCU发指令死等应答拿到了再发下一条。这在单任务Demo里没问题但一旦系统里有按键扫描、OLED显示、继电器控制等任务死等就会让整个系统卡住。我的做法是在串口空闲中断里解析应答帧把确认码和返回数据存到全局变量里主循环通过轮询标志位来决定下一步动作。等于是把收发逻辑从同步改成了异步体验完全不一样。简单说就是发指令时只负责发收应答靠中断主循环每秒跑几十次检查as608_rx_complete_flag置位了就去处理。处理完清除标志位再发下一条。这样MCU的利用率高很多按键响应也快。6.2 指纹ID分配先查库再定ID别硬写如果你打算把指纹ID固定写死在程序里比如默认第一个用户就是ID1那一定要考虑这个ID已经被占用的情况。稳妥做法是模块上电时读一次系统参数拿到已注册数量新指纹ID从当前数量1开始。极端情况是删除了中间某个ID库里总数和最大ID对不上这时候就需要多读注册ID列表来做精确分配。AS608支持读取已注册ID列表的功能0x0D之后可以配合分页读取整个模板信息但更简单的方案是在MCU侧自己维护一个已使用ID的数组每次录入、删除都同步更新掉电后下次上电再读一次模块状态重新构建。这套逻辑不复杂但能避免很多ID冲突重复覆盖的隐性Bug。6.3 用户提示与重试机制产品感来自这些细节最后说一个容易被忽略的事用户操作反馈。录入指纹时什么时候按第一次、什么时候松开、什么时候按第二次光靠用户猜很容易操作失败。我的做法是在OLED屏上分步提示请按压手指请移开手指请再次按压录入门每个步骤都有明确倒计时或等待检测。检测到指纹按压可以通过模块的TOUCH引脚实现这个引脚会随着手指触碰传感器拉低接到MCU的EXTI中断引脚比轮询省心。TOUCH在HAL库下的配置和普通GPIO外部中断完全一致HAL_GPIO_EXTI_Callback里翻转一下录入状态即可。说到外部中断我在HAL库上踩过一个不大不小的坑HAL_GPIO_EXTI_Callback里不能放耗时操作否则下一次中断很容易丢失。正确做法是回调里只置个标志位具体录入流程挪到主循环里跑。这跟串口中断处理是同一个准则——中断里做最少的事重活累活全交给主循环。6.4 关于西西菜鸟这个名字的建议项目标题里带着西西菜鸟我猜是新手阶段的作品。如果你也是刚接触AS608HAL库我的建议是先把官方数据手册的指令表完整看一遍重点理解缓冲ID、指纹ID和PageID这三个概念的区别然后把串口收发框架跑通用串口助手直接对模块发Hex指令直观感受发一帧收一帧的过程最后再往工程里集成录入状态机。这三步走下来AS608这块基本就是熟练工了。特别提醒一点网上很多教程给的驱动代码是标准库版本和HAL库的命名差别很大。直接抄标准库代码进HAL库工程大概率编译不过需要手动改函数名和结构体。如果实在不想改优先找HAL库版本的驱动或者干脆用CubeMX重新生成工程框架把标准库的协议解析逻辑移植进来。协议层代码本身和库无关搬到HAL库只需要把串口收发接口换成HAL函数即可工作量并不大。另外还有一点经验AS608模块的蓝色背光灯默认是常亮的如果你做的是电池供电的设备这个灯会一直耗电。好在部分模块支持通过指令关闭背光具体指令在数据手册的写寄存器部分不同批次模块支持程度不太一样拿到模块后先发一条试一下。这个细节很少有人提但对实际项目续航影响挺大。我在项目里最后留了一个不算优雅但很实用的小功能长按按键进入管理员模式管理员模式下连续验证已录入的管理员指纹成功后才能执行删除指纹和清空指纹库的操作。这样避免了普通用户误操作把整个指纹库清掉。AS608本身不区分管理员和普通用户这种权限控制完全靠MCU端自己实现算是产品化过程中值得投入的环节。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门