基于STM32的指纹识别系统实现方案
一、系统架构与硬件选型
1.1 硬件连接表(以AS608光学指纹模块为例)
| 指纹模块引脚 | STM32F103C8T6引脚 | 说明 |
|---|---|---|
| VCC | 3.3V / 5V | AS608支持3.3V-5V,建议5V供电以保证LED亮度 |
| GND | GND | 共地 |
| TXD | PA3 (USART2_RX) | 模块发送,STM32接收 |
| RXD | PA2 (USART2_TX) | 模块接收,STM32发送 |
| Touch | PA0 | 触摸感应输出(高电平有效,可选) |
1.2 系统工作流程
[用户触摸] -> [STM32唤醒] -> [发送“采集指纹”指令]
-> [模块采集图像] -> [生成特征值] -> [搜索指纹库]
-> [返回结果] -> [控制继电器/LED显示]
二、核心代码实现
2.1 指纹模块协议定义(fingerprint.h)
#ifndef __FINGERPRINT_H
#define __FINGERPRINT_H
#include "stm32f10x.h"
#include <string.h>
// AS608 指令定义
#define FP_HEAD 0xEF01
#define FP_ADDR 0xFFFFFFFF
// 指令包标识
#define CMD_GENIMG 0x01 // 采集图像
#define CMD_GENCHAR 0x02 // 生成特征
#define CMD_MATCH 0x03 // 精确比对(1:1)
#define CMD_SEARCH 0x04 // 搜索指纹(1:N)
#define CMD_REG_MODEL 0x05 // 合并特征(生成模板)
#define CMD_STORE 0x06 // 储存模板
#define CMD_DELETE 0x0C // 删除模板
#define CMD_UP_CHAR 0x08 // 上传特征
#define CMD_DOWN_CHAR 0x09 // 下载特征
#define CMD_UP_IMAGE 0x0A // 上传图像
#define CMD_DOWN_IMAGE 0x0B // 下载图像
#define CMD_EMPTY 0x0D // 清空指纹库
#define CMD_READ_CONF 0x0E // 读系统参数
// 确认码(返回值)
#define ACK_OK 0x00 // 指令执行完毕或OK
#define ACK_FAIL 0x01 // 数据包接收错误
#define ACK_NO_FINGER 0x02 // 没有手指
#define ACK_ENTRY_FAIL 0x03 // 录入指纹图像失败
#define ACK_DISORDER 0x06 // 指纹库满
#define ACK_NOT_FOUND 0x09 // 没搜索到
// 缓冲区定义
#define CHAR_BUFFER_A 0x01
#define CHAR_BUFFER_B 0x02
// 全局变量
extern uint8_t fp_rx_buffer[64];
extern uint8_t fp_tx_buffer[64];
extern uint8_t fp_rx_flag;
// 函数声明
void FP_Init(uint32_t baudrate);
void FP_SendCmd(uint8_t cmd, uint8_t *data, uint8_t len);
uint8_t FP_GetResponse(void);
uint8_t FP_GetImage(void);
uint8_t FP_GenChar(uint8_t buffer_id);
uint8_t FP_Search(uint8_t buffer_id, uint16_t start_page, uint16_t page_num);
uint8_t FP_Store(uint8_t buffer_id, uint16_t page_id);
uint8_t FP_Delete(uint16_t page_id, uint16_t num);
uint8_t FP_Empty(void);
void FP_ShowSystemParams(void);
#endif
2.2 串口驱动与指令发送(fingerprint.c)
#include "fingerprint.h"
#include "usart.h"
#include "delay.h"
uint8_t fp_rx_buffer[64];
uint8_t fp_rx_flag = 0;
static uint8_t rx_cnt = 0;
/**
* @brief 指纹模块串口初始化
*/
void FP_Init(uint32_t baudrate)
{
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// PA2 -> TX, PA3 -> RX
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_Cmd(USART2, ENABLE);
printf("Fingerprint Module Init OK\r\n");
}
/**
* @brief 发送指令包
* @param cmd: 指令码
* @param data: 附加数据
* @param len: 数据长度
*/
void FP_SendCmd(uint8_t cmd, uint8_t *data, uint8_t len)
{
uint16_t checksum = 0;
uint8_t i;
// 包头
USART_SendData(USART2, 0xEF); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0x01); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
// 地址
USART_SendData(USART2, 0xFF); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0xFF); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0xFF); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, 0xFF); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
// 包标识 (0x01 命令包)
USART_SendData(USART2, 0x01); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
// 包长度 (指令码1字节 + 数据长度 + 校验和2字节)
uint16_t pkg_len = 1 + len + 2;
USART_SendData(USART2, pkg_len >> 8); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, pkg_len & 0xFF); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
// 指令码
USART_SendData(USART2, cmd); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
checksum += cmd;
// 数据
for(i = 0; i < len; i++)
{
USART_SendData(USART2, data[i]); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
checksum += data[i];
}
// 校验和
USART_SendData(USART2, checksum >> 8); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, checksum & 0xFF); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}
/**
* @brief 获取图像
*/
uint8_t FP_GetImage(void)
{
FP_SendCmd(CMD_GENIMG, NULL, 0);
return FP_GetResponse();
}
/**
* @brief 生成特征
* @param buffer_id: 缓冲区号 (1 or 2)
*/
uint8_t FP_GenChar(uint8_t buffer_id)
{
uint8_t data[1] = {buffer_id};
FP_SendCmd(CMD_GENCHAR, data, 1);
return FP_GetResponse();
}
/**
* @brief 搜索指纹
* @param buffer_id: 缓冲区号
* @param start_page: 起始页
* @param page_num: 页数
*/
uint8_t FP_Search(uint8_t buffer_id, uint16_t start_page, uint16_t page_num)
{
uint8_t data[5];
data[0] = buffer_id;
data[1] = start_page >> 8;
data[2] = start_page & 0xFF;
data[3] = page_num >> 8;
data[4] = page_num & 0xFF;
FP_SendCmd(CMD_SEARCH, data, 5);
return FP_GetResponse();
}
/**
* @brief 存储模板
*/
uint8_t FP_Store(uint8_t buffer_id, uint16_t page_id)
{
uint8_t data[3];
data[0] = buffer_id;
data[1] = page_id >> 8;
data[2] = page_id & 0xFF;
FP_SendCmd(CMD_STORE, data, 3);
return FP_GetResponse();
}
/**
* @brief 删除指纹
*/
uint8_t FP_Delete(uint16_t page_id, uint16_t num)
{
uint8_t data[4];
data[0] = page_id >> 8;
data[1] = page_id & 0xFF;
data[2] = num >> 8;
data[3] = num & 0xFF;
FP_SendCmd(CMD_DELETE, data, 4);
return FP_GetResponse();
}
/**
* @brief 清空指纹库
*/
uint8_t FP_Empty(void)
{
FP_SendCmd(CMD_EMPTY, NULL, 0);
return FP_GetResponse();
}
/**
* @brief 获取系统参数
*/
void FP_ShowSystemParams(void)
{
FP_SendCmd(CMD_READ_CONF, NULL, 0);
Delay_ms(200);
// 解析fp_rx_buffer中的状态寄存器、容量等信息
printf("Status: 0x%02X\r\n", fp_rx_buffer[7]);
}
/**
* @brief 获取响应
*/
uint8_t FP_GetResponse(void)
{
uint32_t timeout = 0;
while(fp_rx_flag == 0)
{
Delay_ms(10);
timeout++;
if(timeout > 200) // 2秒超时
{
printf("FP Response Timeout!\r\n");
return 0xFF;
}
}
fp_rx_flag = 0;
return fp_rx_buffer[7]; // 返回确认码
}
2.3 中断接收(stm32f10x_it.c)
#include "fingerprint.h"
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
uint8_t data = USART_ReceiveData(USART2);
if(rx_cnt < sizeof(fp_rx_buffer))
{
fp_rx_buffer[rx_cnt++] = data;
}
// 简单判断是否接收完成(通常看最后两个字节校验和,这里简化为固定长度)
if(rx_cnt >= 12) // 假设最小包长度为12
{
fp_rx_flag = 1;
rx_cnt = 0;
}
}
}
2.4 主程序逻辑(main.c)
#include "stm32f10x.h"
#include "fingerprint.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "relay.h"
int main(void)
{
SystemInit();
Delay_Init();
USART1_Init(115200); // 调试串口
LED_Init();
Relay_Init();
// 初始化指纹模块,波特率57600(AS608默认)
FP_Init(57600);
uint8_t result;
uint16_t user_id = 0;
while(1)
{
printf("Place your finger...\r\n");
LED_Blink(500); // LED闪烁提示
// 1. 采集图像
result = FP_GetImage();
if(result != ACK_OK)
{
if(result == ACK_NO_FINGER) printf("No finger detected.\r\n");
else printf("Get Image Failed: 0x%02X\r\n", result);
Delay_ms(1000);
continue;
}
printf("Image captured.\r\n");
// 2. 生成特征
result = FP_GenChar(CHAR_BUFFER_A);
if(result != ACK_OK)
{
printf("Generate Char Failed: 0x%02X\r\n", result);
Delay_ms(1000);
continue;
}
// 3. 搜索指纹库
printf("Searching...\r\n");
result = FP_Search(CHAR_BUFFER_A, 0, 100); // 搜索前100个指纹
if(result == ACK_OK)
{
// 搜索成功,fp_rx_buffer中包含页码和匹配度
user_id = (fp_rx_buffer[8] << 8) | fp_rx_buffer[9];
printf("Match Found! User ID: %d, Score: %d\r\n", user_id, fp_rx_buffer[10]);
// 控制继电器开门
Relay_ON();
LED_ON();
Delay_ms(3000);
Relay_OFF();
LED_OFF();
}
else if(result == ACK_NOT_FOUND)
{
printf("No Match Found.\r\n");
LED_Blink(100); // 快闪表示拒绝
}
else
{
printf("Search Error: 0x%02X\r\n", result);
}
Delay_ms(2000); // 防止重复识别
}
}
三、指纹录入流程(需在上位机或按键触发)
录入通常需要两步(录入两次以确认):
uint8_t Enroll_Fingerprint(uint16_t user_id)
{
uint8_t res;
printf("Enroll: Place finger first time\r\n");
res = FP_GetImage(); if(res != ACK_OK) return res;
res = FP_GenChar(CHAR_BUFFER_A); if(res != ACK_OK) return res;
printf("Remove finger\r\n");
Delay_ms(2000);
printf("Enroll: Place finger second time\r\n");
res = FP_GetImage(); if(res != ACK_OK) return res;
res = FP_GenChar(CHAR_BUFFER_B); if(res != ACK_OK) return res;
// 精确比对两次特征是否一致
// 这里省略精确比对指令,直接存储
res = FP_Store(CHAR_BUFFER_A, user_id);
if(res == ACK_OK) printf("Enroll Success! ID: %d\r\n", user_id);
return res;
}
参考代码 基于STM32的指纹识别 www.youwenfan.com/contentcsu/56232.html
四、调试与避坑指南
4.1 常见问题排查
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 无响应 | 波特率不匹配 | AS608默认57600,STM32串口初始化需一致 |
| 图像采集失败 | 手指太干/太湿 | 提示用户湿润手指或擦拭传感器 |
| 搜索速度慢 | 搜索范围过大 | 缩小搜索页码范围,或升级为1:N搜索算法 |
| 误识率高 | 指纹质量差 | 增加录入时的图像质量检测,录入时旋转手指 |
4.2 硬件优化建议
- 电源稳定性:指纹模块工作时电流较大(>100mA),建议在VCC并联100uF电容。
- 抗干扰:指纹模块尽量远离电机、继电器等强干扰源。
- 触摸唤醒:使用模块的Touch引脚连接STM32外部中断,平时STM32休眠,触摸时唤醒,降低功耗。
五、总结
这套方案实现了基于STM32的指纹录入、存储、1:N搜索全流程。核心在于串口协议的正确封装和对返回状态码的严谨处理。
应用场景:
- 智能门锁
- 考勤打卡机
- 保险柜/枪柜
- 身份授权终端