GD32VW553开发板驱动AS608光学指纹识别模块实战指南最近在做一个智能门锁项目需要用到指纹识别功能正好手头有GD32VW553开发板和AS608光学指纹模块。今天我就把整个驱动过程整理出来分享给大家手把手教你如何让这两个设备“对话”。这个教程适合正在学习GD32VW553的嵌入式开发者特别是想做门禁、考勤、保险箱等生物识别应用的朋友。跟着我一步步来你就能掌握指纹模块的硬件连接、串口通信、指纹录入和识别等核心功能。1. 认识AS608指纹模块AS608是深圳指昂科技Synochip推出的一款光学指纹识别芯片它最大的特点就是“省心”——内部集成了DSP运算单元和指纹识别算法咱们不用自己处理复杂的图像处理和指纹特征提取只需要通过简单的串口或USB通信就能控制它。1.1 模块基本参数先看看这个模块的基本规格了解它的“脾气”参数规格工作电压3.0-3.6V工作电流30~60mA指纹容量300枚ID:0~299认假率0.001%搜索时间0.3秒控制方式串口或USB管脚数量8 Pin2.54mm间距排针注意工作电压一定要控制在3.0-3.6V之间超过3.6V可能会烧坏模块我刚开始用5V供电结果模块直接冒烟了这个坑大家一定要避开。1.2 模块内部结构AS608内部有三个重要的缓冲区理解它们对后续编程很重要ImageBuffer72KB存放采集到的原始指纹图像CharBuffer1512字节存放指纹特征模板1CharBuffer2512字节存放指纹特征模板2这三个缓冲区就像三个“工作台”ImageBuffer是拍照的地方CharBuffer1和CharBuffer2是处理指纹特征的地方。用户可以通过指令读写任意一个缓冲区。指纹库容量根据挂接的FLASH容量不同而改变系统会自动识别。指纹模板按照序号存放序号定义为0~(N-1)N为指纹库容量。用户只能根据序号访问指纹库内容。2. 硬件连接硬件连接是第一步也是最容易出错的地方。AS608模块有8个引脚但咱们用串口控制时主要用到其中6个。2.1 引脚定义先看看模块的引脚功能按颜色区分引脚颜色功能连接说明1脚红线模块主电源接3.3V供电千万别接3.3V以上2脚黄线模块串口TX发送端接MCU或TTL串口的RX接收端3脚白线模块串口RX接收端接MCU或TTL串口的TX发送端4脚黑线模块电源地接3.3V电源地负极5脚棕线模块触摸感应信号输出高电平为检测到触摸需接上拉到3.3V6脚绿线模块触摸感应电路电源3.3V可以和1脚红线并接7脚、8脚-USB信号线使用串口控制模块时可以悬空不用2.2 GD32VW553开发板连接根据原始资料GD32VW553开发板与AS608的连接方式如下模块引脚开发板引脚红线3V33V3黄线TXDPB4白线RXDPB3黑线GNDGND棕线WAKPB2绿线VTI3V3这里有个细节要注意棕线WAK是触摸感应输出需要上拉到3.3V。在代码中我们会把PB2配置为输入模式内部不使能上拉因为模块内部已经有上拉了。3. 软件驱动开发硬件连接好后咱们开始写代码。整个驱动分为几个部分串口初始化、指令封装、指纹操作函数。3.1 创建驱动文件首先新建两个文件bsp_as608.c和bsp_as608.h并把头文件路径添加到编译器中。这个步骤在DHT11章节已经讲过可以参考之前的教程。3.2 头文件定义在bsp_as608.h中我们定义引脚、串口配置和函数声明#ifndef BSP_CODE_BSP_AS608_H_ #define BSP_CODE_BSP_AS608_H_ #include gd32vw55x.h #include systick.h #ifndef delay_ms #define delay_ms(x) delay_1ms(x) #endif #ifndef delay_us #define delay_us(x) delay_1us(x) #endif //时钟定义 #define BSP_UART_RCU RCU_UART1 #define BSP_UART_TX_RCU RCU_GPIOB #define BSP_UART_RX_RCU RCU_GPIOB //串口发送引脚定义 #define BSP_UART_TX_PORT GPIOB #define BSP_UART_TX_PIN GPIO_PIN_4 #define BSP_UART_TX_AF GPIO_AF_8 //串口接收引脚定义 #define BSP_UART_RX_PORT GPIOB #define BSP_UART_RX_PIN GPIO_PIN_3 #define BSP_UART_RX_AF GPIO_AF_8 //串口定义 #define BSP_UART UART1 #define BSP_UART_IRQ UART1_IRQn #define BSP_UART_IRQHandler UART1_IRQHandler #define BSP_AS608_WAK_RCU RCU_GPIOB #define BSP_AS608_WAK_PORT GPIOB #define BSP_AS608_WAK_PIN GPIO_PIN_2 /* 串口缓冲区的数据长度 */ #define SERIAL_RECEIVE_LENGTH 255 #define TOUCH_IN gpio_input_bit_get(BSP_AS608_WAK_PORT, BSP_AS608_WAK_PIN) void AS608_Init(void); char get_as608_touch(void); void UsartSendString(uint8_t *ucstr); char Device_Check(void); void FPM10A_Add_Fingerprint(void); //添加指纹 unsigned int FPM10A_Find_Fingerprint(void); //查找指纹 void FPM10A_Delete_All_Fingerprint(void); #endif /* BSP_CODE_BSP_AS608_H_ */3.3 串口初始化在bsp_as608.c中首先实现AS608的初始化函数。这里我设置串口波特率为57600这是AS608模块的默认通信速率void AS608_Init(void) { rcu_periph_clock_enable(BSP_UART_TX_RCU); // 开启端口时钟 rcu_periph_clock_enable(BSP_UART_RX_RCU); // 开启端口时钟 rcu_periph_clock_enable(BSP_AS608_WAK_RCU); // 开启端口时钟 rcu_periph_clock_enable(BSP_UART_RCU); // 开启串口时钟 /* 配置AS608唤醒引脚为输入模式 */ gpio_mode_set(BSP_AS608_WAK_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, BSP_AS608_WAK_PIN); /* connect port to USARTx_Tx */ gpio_af_set(BSP_UART_TX_PORT, BSP_UART_TX_AF, BSP_UART_TX_PIN); /* connect port to USARTx_Rx */ gpio_af_set(BSP_UART_RX_PORT, BSP_UART_RX_AF, BSP_UART_RX_PIN); /* configure USART Tx as alternate function push-pull */ gpio_mode_set(BSP_UART_TX_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, BSP_UART_TX_PIN); gpio_output_options_set(BSP_UART_TX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, BSP_UART_TX_PIN); /* configure USART Rx as alternate function push-pull */ gpio_mode_set(BSP_UART_RX_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, BSP_UART_RX_PIN); gpio_output_options_set(BSP_UART_RX_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_25MHZ, BSP_UART_RX_PIN); /* USART configure */ usart_deinit(BSP_UART); usart_baudrate_set(BSP_UART, 57600U); // 注意波特率是57600不是115200 usart_parity_config(BSP_UART, USART_PM_NONE); usart_word_length_set(BSP_UART,USART_WL_8BIT); usart_stop_bit_set(BSP_UART,USART_STB_1BIT); usart_receive_config(BSP_UART, USART_RECEIVE_ENABLE); usart_transmit_config(BSP_UART, USART_TRANSMIT_ENABLE); usart_interrupt_enable(BSP_UART, USART_INT_RBNE); eclic_irq_enable(BSP_UART_IRQ, 2, 2); usart_enable(BSP_UART); printf(\r\nAS608 Init OK\r\n); }提示AS608模块的默认波特率是57600不是常见的115200。如果通信不上首先检查波特率设置是否正确。3.4 AS608指令集AS608通过串口发送指令包来控制。每个指令包都有固定的格式咱们先看看几个核心指令的定义// 指令定义 const unsigned char FPM10A_Get_Device[10] {0x01,0x00,0x07,0x13,0x00,0x00,0x00,0x00,0x00,0x1b}; // 口令验证 const unsigned char FPM10A_Pack_Head[6] {0xEF,0x01,0xFF,0xFF,0xFF,0xFF}; // 协议包头 const unsigned char FPM10A_Get_Img[6] {0x01,0x00,0x03,0x01,0x00,0x05}; // 获得指纹图像 const unsigned char FPM10A_Search[11] {0x01,0x00,0x08,0x04,0x01,0x00,0x00,0x03,0xE7,0x00,0xF8}; // 搜索指纹搜索范围0-999 const unsigned char FPM10A_Img_To_Buffer1[7] {0x01,0x00,0x04,0x02,0x01,0x00,0x08}; // 将图像放入BUFFER1 const unsigned char FPM10A_Reg_Model[6] {0x01,0x00,0x03,0x05,0x00,0x09}; // 将BUFFER1跟BUFFER2合成特征模板 const unsigned char FPM10A_Delete_All_Model[6] {0x01,0x00,0x03,0x0d,0x00,0x11}; // 删除指纹模块里的所有模板 volatile unsigned char FPM10A_Save_Finger[9] {0x01,0x00,0x06,0x06,0x01,0x00,0x0B,0x00,0x19}; // 将BUFFER1中的特征码存放到指定的位置每个指令包都由包头指令校验和组成。发送指令时要先发送6字节的包头再发送指令内容。3.5 核心功能函数3.5.1 模块检测在开始指纹操作前先检测模块是否正常连接char Device_Check(void) { FPM10A_RECEICE_BUFFER[9] 1; // 串口数组第9位可判断是否通信正常 FPM10A_Cmd_Check(); // 单片机向指纹模块发送校对命令 FPM10A_Receive_Data(12); // 将串口接收到的数据转存 if(FPM10A_RECEICE_BUFFER[9] 0) // 判断数据位第9位是否接收到0 { return 1; // 检测到模块且通信成功 } return 0; // 未检测到模块或通信异常 }这个函数会发送一个验证指令如果模块正常响应说明硬件连接和通信都正常。3.5.2 添加指纹添加指纹是分步骤进行的采集第一次指纹→采集第二次指纹→生成模板→保存模板void FPM10A_Add_Fingerprint(void) { unsigned char key_num key_scanf(); finger_id 0; printf(Do you want to add fingerprints? [Yes/No]\r\n); while(key_num ! 2) // 按返回键直接回到主菜单 { key_num key_scanf(); // 按确认键开始录入指纹信息 if(key_num 1) { printf(start add\r\n); while(key_num ! 2) // 按下返回键退出录入返回fingerID调整状态 { key_num key_scanf(); FPM10A_Cmd_Get_Img(); // 获得指纹图像 FPM10A_Receive_Data(12); // 判断接收到的确认码等于0指纹获取成功 if(FPM10A_RECEICE_BUFFER[9] 0) { delay_ms(100); FINGERPRINT_Cmd_Img_To_Buffer1(); // 转换到特征码 FPM10A_Receive_Data(12); delay_ms(1000); while(key_num ! 2) { key_num key_scanf(); FPM10A_Cmd_Get_Img(); // 获得指纹图像 FPM10A_Receive_Data(12); // 判断接收到的确认码等于0指纹获取成功 if(FPM10A_RECEICE_BUFFER[9] 0) { delay_ms(200); printf(successfully added, ID %d\r\n, finger_id); FINGERPRINT_Cmd_Img_To_Buffer2(); // 第二次转换 FPM10A_Receive_Data(12); FPM10A_Cmd_Reg_Model(); // 合并生成特征模板 FPM10A_Receive_Data(12); // 保存指纹 FPM10A_Cmd_Save_Finger(finger_id); FPM10A_Receive_Data(12); delay_ms(1000); finger_id finger_id 1; break; } } break; } } } } }添加指纹时需要采集两次这是为了提高识别准确率。第一次采集后系统会提示你抬起手指再按下采集第二次。3.5.3 查找指纹查找指纹的过程相对简单采集指纹→转换为特征码→在指纹库中搜索unsigned int FPM10A_Find_Fingerprint(void) { unsigned int find_fingerid 255; if(get_as608_touch() 1) // 有手指触摸识别区 { FPM10A_Cmd_Get_Img(); // 获得指纹图像 FPM10A_Receive_Data(12); // 判断接收到的确认码等于0指纹获取成功 if(FPM10A_RECEICE_BUFFER[9] 0) { delay_ms(100); FINGERPRINT_Cmd_Img_To_Buffer1(); // 转换到特征码 FPM10A_Receive_Data(12); FPM10A_Cmd_Search_Finger(); // 搜索指纹 FPM10A_Receive_Data(16); if(FPM10A_RECEICE_BUFFER[9] 0) // 搜索成功 { // 拼接指纹ID数 find_fingerid FPM10A_RECEICE_BUFFER[10] * 256 FPM10A_RECEICE_BUFFER[11]; printf(ID %d\r\n, find_fingerid); delay_ms(500); } else // 没有找到 { printf(not found\r\n); } } } return find_fingerid; }注意查找指纹时返回的ID是通过两个字节拼接的高字节×256 低字节。如果返回255表示没有找到匹配的指纹。3.5.4 删除所有指纹清空指纹库的功能在调试时很常用void FPM10A_Delete_All_Fingerprint(void) { unsigned char key_num 0; printf(Whether to delete fingerprint ? [Yes/No]\r\n); do { key_num key_scanf(); if(key_num 1) // 点击确认键 { printf(deleting\r\n); delay_ms(300); FINGERPRINT_Cmd_Delete_All_Model(); FPM10A_Receive_Data(12); printf(Have all been cleared\r\n); break; } } while(key_num ! 2); // 没有点击取消键则继续循环 }3.6 串口中断处理串口通信采用中断方式接收数据这里有两个串口中断处理函数一个用于调试串口USART0一个用于AS608模块串口UART1。void UART1_IRQHandler(void) { // 接收缓冲区不为空 if(usart_interrupt_flag_get(BSP_UART, USART_INT_FLAG_RBNE) ! RESET) { /* 清除接收中断标志位 */ usart_interrupt_flag_clear(BSP_UART, USART_INT_FLAG_RBNE); /* 把接收到的数据放到缓冲区中 */ module_recv_buff[module_recv_length] usart_data_receive(BSP_UART); /* 限制接收长度防止数据溢出 */ module_recv_length (module_recv_length 1) % SERIAL_RECEIVE_LENGTH; /* 数据结尾加上字符串结束符 */ module_recv_buff[module_recv_length] \0; /* 接收完成标志位 */ module_recv_flag 1; // 判断空闲中断是否开启 if((USART_CTL0(BSP_UART) (1 4)) 0) { /* 使能IDLE线检测中断 */ usart_interrupt_enable(BSP_UART, USART_INT_IDLE); /* 清除空闲中断标志位 */ usart_interrupt_flag_clear(BSP_UART, USART_INT_FLAG_IDLE); } } // 检测到空闲中断 if(usart_interrupt_flag_get(BSP_UART, USART_INT_FLAG_IDLE) ! RESET) { /* 清除空闲中断标志位 */ usart_interrupt_flag_clear(BSP_UART, USART_INT_FLAG_IDLE); /* 数据结尾加上字符串结束符 */ module_recv_buff[module_recv_length] \0; /* 接收完成标志位 */ module_recv_flag 1; /* 关闭空闲中断 */ usart_interrupt_disable(BSP_UART, USART_INT_IDLE); } }这里使用了空闲中断IDLE来判断一帧数据是否接收完成。当串口总线空闲一段时间后会触发空闲中断这时我们就认为一帧数据接收完成了。4. 主程序实现最后在main.c中调用这些函数实现完整的指纹识别功能#include gd32vw55x.h #include systick.h #include stdio.h #include main.h #include gd32vw553h_eval.h #include bsp_as608.h int main(void) { /* configure systick */ systick_config(); eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL3_PRIO1); /* initilize the LEDs, USART and key */ gd_eval_led_init(LED1); gd_eval_com_init(EVAL_COM0); gd_eval_key_init(KEY_TAMPER_WAKEUP, KEY_MODE_GPIO); /* 打印开发板信息 */ printf(\r\n Welcome to use the LC-GD32VW553-HMQ6 development board \r\n); printf(\r\n www.lckfb.com \r\n); AS608_Init(); // 初始化AS608指纹模块 delay_ms(1000); // 等待模块初始化完成 Device_Check(); // 模块检测 FPM10A_Delete_All_Fingerprint(); // 是否删除全部指纹 FPM10A_Add_Fingerprint(); // 是否添加指纹 while(1) { FPM10A_Find_Fingerprint(); // 查找指纹 delay_ms(100); } }5. 调试技巧和常见问题在实际调试中我遇到了几个坑这里分享给大家电源问题AS608模块对电压很敏感一定要用3.3V供电。我第一次用5V模块直接烧了。波特率不匹配AS608默认波特率是57600不是115200。如果通信不上先用这个波特率试试。触摸检测引脚WAK引脚需要上拉到3.3V模块内部已经有上拉电阻所以代码中配置为无上拉下拉的输入模式即可。指令响应超时发送指令后要等待模块响应原始代码中设置了1秒超时。如果环境干扰大可以适当增加超时时间。指纹采集技巧录入指纹时要确保手指干净、干燥按压时力度适中覆盖整个传感器区域。第一次和第二次采集最好用同一手指的同一部位。通信数据解析AS608返回的数据包中第9个字节是确认码0x00表示成功第10-11字节是指纹ID高字节在前。这个驱动我在实际项目中用了好几次稳定性还不错。指纹识别速度很快基本上按下手指0.3秒内就能出结果。对于门禁、考勤这类应用完全够用。如果你在调试过程中遇到问题可以先检查硬件连接然后用串口助手看看模块是否有数据返回。有时候是接线问题有时候是指令格式不对耐心调试都能解决。