基于STM32的多模态智能门锁系统设计与实现
前言
最近接了个项目,要做一套支持指纹、人脸、密码三重验证的智能门锁系统。这种多模态识别在安全性和便利性上都有很大优势,特别适合对安全要求较高的场景。折腾了一个多月,踩了不少坑,今天把整个系统的设计思路和实现细节分享出来,希望对做类似项目的朋友有所帮助。
本文会详细介绍系统架构、硬件选型、各个模块的实现原理,以及完整的代码实现。代码都是实际项目中跑通的,可以直接参考使用。
一、系统整体架构
1.1 硬件架构
整个系统的硬件架构如下:
┌─────────────────────────────────────────────────┐
│ STM32F407VET6 主控芯片 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 指纹模块 │ │ 人脸模块 │ │ 密码键盘 │ │
│ │ AS608 │ │ 摄像头+ │ │ 4x4矩阵 │ │
│ │ │ │ AI芯片 │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ OLED屏幕 │ │ 电机驱动 │ │ WiFi │ │
│ │ 0.96寸 │ │ L298N │ │ ESP8266 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────┘
核心硬件清单:
- 主控:STM32F407VET6(168MHz,1MB Flash)
- 指纹模块:AS608光学指纹识别模块
- 人脸识别:OV2640摄像头 + K210 AI加速芯片
- 密码输入:4x4矩阵键盘
- 显示:0.96寸OLED(I2C接口)
- 门锁驱动:步进电机+L298N驱动模块
- 通信:ESP8266 WiFi模块(远程管理)
- 电源:12V/3A开关电源
1.2 软件架构
软件采用分层设计,主要分为以下几层:
┌─────────────────────────────────────┐
│ 应用层(Application) │
│ 门锁控制逻辑 | 用户管理 | 日志 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 算法层(Algorithm) │
│ 指纹匹配 | 人脸识别 | 密码验证 │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 驱动层(Driver) │
│ UART | SPI | I2C | GPIO | Timer │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ 硬件层(Hardware) │
│ STM32 HAL Library │
└─────────────────────────────────────┘
二、硬件接口设计
2.1 引脚分配
c
// 指纹模块 AS608 - UART3
#define FP_UART USART3
#define FP_TX_PIN GPIO_PIN_10
#define FP_RX_PIN GPIO_PIN_11
#define FP_GPIO_PORT GPIOB
// 人脸识别模块 K210 - UART2
#define FACE_UART USART2
#define FACE_TX_PIN GPIO_PIN_2
#define FACE_RX_PIN GPIO_PIN_3
#define FACE_GPIO_PORT GPIOA
// OLED显示 - I2C1
#define OLED_SCL_PIN GPIO_PIN_6
#define OLED_SDA_PIN GPIO_PIN_7
#define OLED_GPIO_PORT GPIOB
// 矩阵键盘 - GPIO
#define KEY_ROW1_PIN GPIO_PIN_0
#define KEY_ROW2_PIN GPIO_PIN_1
#define KEY_ROW3_PIN GPIO_PIN_2
#define KEY_ROW4_PIN GPIO_PIN_3
#define KEY_COL1_PIN GPIO_PIN_4
#define KEY_COL2_PIN GPIO_PIN_5
#define KEY_COL3_PIN GPIO_PIN_6
#define KEY_COL4_PIN GPIO_PIN_7
#define KEY_GPIO_PORT GPIOC
// 步进电机控制 - PWM
#define MOTOR_IN1_PIN GPIO_PIN_0
#define MOTOR_IN2_PIN GPIO_PIN_1
#define MOTOR_IN3_PIN GPIO_PIN_2
#define MOTOR_IN4_PIN GPIO_PIN_3
#define MOTOR_GPIO_PORT GPIOE
// WiFi模块 ESP8266 - UART4
#define WIFI_UART UART4
#define WIFI_TX_PIN GPIO_PIN_10
#define WIFI_RX_PIN GPIO_PIN_11
#define WIFI_GPIO_PORT GPIOC
2.2 电源管理
智能锁对功耗有一定要求,特别是待机状态下。我们设计了三种工作模式:
- 激活模式:所有模块上电,功耗约2W
- 待机模式:仅保持键盘和触摸唤醒,功耗约200mW
- 休眠模式:深度休眠,仅RTC工作,功耗<50mW
三、指纹识别模块实现
3.1 AS608指纹模块通信协议
AS608使用UART通信,波特率57600。数据包格式如下:
包头 地址 标识 包长度 指令/数据 校验和
0xEF01 4字节 1字节 2字节 N字节 2字节
3.2 指纹模块驱动代码
c
// as608.h
#ifndef __AS608_H
#define __AS608_H
#include "stm32f4xx_hal.h"
// AS608指令定义
#define AS608_CMD_GET_IMAGE 0x01 // 录入图像
#define AS608_CMD_GEN_CHAR 0x02 // 生成特征
#define AS608_CMD_MATCH 0x03 // 精确比对
#define AS608_CMD_SEARCH 0x04 // 搜索指纹
#define AS608_CMD_REG_MODEL 0x05 // 合成模板
#define AS608_CMD_STORE_CHAR 0x06 // 储存模板
#define AS608_CMD_LOAD_CHAR 0x07 // 读取模板
#define AS608_CMD_DELETE_CHAR 0x0C // 删除模板
#define AS608_CMD_EMPTY 0x0D // 清空指纹库
// 返回确认码
#define AS608_ACK_SUCCESS 0x00 // 操作成功
#define AS608_ACK_ERROR 0x01 // 数据包接收错误
#define AS608_ACK_NO_FINGER 0x02 // 没有检测到手指
#define AS608_ACK_GET_IMG_ERR 0x03 // 录入指纹图像失败
#define AS608_ACK_NOT_MATCH 0x08 // 指纹不匹配
#define AS608_ACK_NOT_FOUND 0x09 // 没有搜索到指纹
// 数据包标识
#define AS608_PACK_CMD 0x01
#define AS608_PACK_DATA 0x02
#define AS608_PACK_END 0x08
typedef struct {
uint8_t head[2]; // 包头 0xEF01
uint8_t addr[4]; // 模块地址
uint8_t pid; // 包标识
uint8_t len[2]; // 包长度
uint8_t data[256]; // 数据内容
uint8_t checksum[2]; // 校验和
} AS608_Packet;
typedef struct {
uint16_t pageID; // 指纹ID
uint16_t matchScore; // 匹配分数
} AS608_SearchResult;
// 函数声明
void AS608_Init(UART_HandleTypeDef *huart);
uint8_t AS608_GetImage(void);
uint8_t AS608_GenChar(uint8_t bufferID);
uint8_t AS608_Match(void);
uint8_t AS608_Search(uint8_t bufferID, AS608_SearchResult *result);
uint8_t AS608_RegModel(void);
uint8_t AS608_StoreChar(uint8_t bufferID, uint16_t pageID);
uint8_t AS608_DeleteChar(uint16_t pageID, uint16_t count);
uint8_t AS608_Empty(void);
uint8_t AS608_EnrollFingerprint(uint16_t pageID);
uint8_t AS608_VerifyFingerprint(AS608_SearchResult *result);
#endif
c
// as608.c
#include "as608.h"
#include "string.h"
static UART_HandleTypeDef *fp_uart;
static uint8_t AS608_RxBuffer[512];
static uint32_t AS608_Address = 0xFFFFFFFF;
// 发送数据包
static void AS608_SendPacket(AS608_Packet *packet) {
uint16_t length = (packet->len[0] << 8) | packet->len[1];
uint16_t checksum = packet->pid + packet->len[0] + packet->len[1];
for(int i = 0; i < length - 2; i++) {
checksum += packet->data[i];
}
packet->checksum[0] = (checksum >> 8) & 0xFF;
packet->checksum[1] = checksum & 0xFF;
uint8_t buffer[512];
int pos = 0;
buffer[pos++] = 0xEF;
buffer[pos++] = 0x01;
memcpy(&buffer[pos], packet->addr, 4); pos += 4;
buffer[pos++] = packet->pid;
buffer[pos++] = packet->len[0];
buffer[pos++] = packet->len[1];
memcpy(&buffer[pos], packet->data, length - 2); pos += (length - 2);
buffer[pos++] = packet->checksum[0];
buffer[pos++] = packet->checksum[1];
HAL_UART_Transmit(fp_uart, buffer, pos, 1000);
}
// 接收数据包
static uint8_t AS608_ReceivePacket(AS608_Packet *packet, uint32_t timeout) {
uint32_t startTick = HAL_GetTick();
int rxPos = 0;
while(HAL_GetTick() - startTick < timeout) {
if(HAL_UART_Receive(fp_uart, &AS608_RxBuffer[rxPos], 1, 10) == HAL_OK) {
rxPos++;
if(rxPos >= 9) {
if(AS608_RxBuffer[0] == 0xEF && AS608_RxBuffer[1] == 0x01) {
uint16_t length = (AS608_RxBuffer[7] << 8) | AS608_RxBuffer[8];
while(rxPos < 9 + length && HAL_GetTick() - startTick < timeout) {
if(HAL_UART_Receive(fp_uart, &AS608_RxBuffer[rxPos], 1, 10) == HAL_OK) {
rxPos++;
}
}
if(rxPos == 9 + length) {
packet->head[0] = AS608_RxBuffer[0];
packet->head[1] = AS608_RxBuffer[1];
memcpy(packet->addr, &AS608_RxBuffer[2], 4);
packet->pid = AS608_RxBuffer[6];
packet->len[0] = AS608_RxBuffer[7];
packet->len[1] = AS608_RxBuffer[8];
memcpy(packet->data, &AS608_RxBuffer[9], length - 2);
packet->checksum[0] = AS608_RxBuffer[9 + length - 2];
packet->checksum[1] = AS608_RxBuffer[9 + length - 1];
return packet->data[0]; // 返回确认码
}
}
}
}
}
return 0xFF; // 超时
}
// 初始化
void AS608_Init(UART_HandleTypeDef *huart) {
fp_uart = huart;
}
// 录入图像
uint8_t AS608_GetImage(void) {
AS608_Packet packet;
packet.head[0] = 0xEF;
packet.head[1] = 0x01;
memcpy(packet.addr, &AS608_Address, 4);
packet.pid = AS608_PACK_CMD;
packet.len[0] = 0x00;
packet.len[1] = 0x03;
packet.data[0] = AS608_CMD_GET_IMAGE;
AS608_SendPacket(&packet);
return AS608_ReceivePacket(&packet, 1000);
}
// 生成特征文件
uint8_t AS608_GenChar(uint8_t bufferID) {
AS608_Packet packet;
packet.head[0] = 0xEF;
packet.head[1] = 0x01;
memcpy(packet.addr, &AS608_Address, 4);
packet.pid = AS608_PACK_CMD;
packet.len[0] = 0x00;
packet.len[1] = 0x04;
packet.data[0] = AS608_CMD_GEN_CHAR;
packet.data[1] = bufferID;
AS608_SendPacket(&packet);
return AS608_ReceivePacket(&packet, 2000);
}
// 精确对比两枚指纹特征
uint8_t AS608_Match(void) {
AS608_Packet packet;
packet.head[0] = 0xEF;
packet.head[1] = 0x01;
memcpy(packet.addr, &AS608_Address, 4);
packet.pid = AS608_PACK_CMD;
packet.len[0] = 0x00;
packet.len[1] = 0x03;
packet.data[0] = AS608_CMD_MATCH;
AS608_SendPacket(&packet);
return AS608_ReceivePacket(&packet, 1000);
}
// 搜索指纹
uint8_t AS608_Search(uint8_t bufferID, AS608_SearchResult *result) {
AS608_Packet packet;
packet.head[0] = 0xEF;
packet.head[1] = 0x01;
memcpy(packet.addr, &AS608_Address, 4);
packet.pid = AS608_PACK_CMD;
packet.len[0] = 0x00;
packet.len[1] = 0x08;
packet.data[0] = AS608_CMD_SEARCH;
packet.data[1] = bufferID;
packet.data[2] = 0x00; // 起始页
packet.data[3] = 0x00;
packet.data[4] = 0x00; // 页数
packet.data[5] = 0xFF;
AS608_SendPacket(&packet);
uint8_t ack = AS608_ReceivePacket(&packet, 2000);
if(ack == AS608_ACK_SUCCESS && result != NULL) {
result->pageID = (packet.data[1] << 8) | packet.data[2];
result->matchScore = (packet.data[3] << 8) | packet.data[4];
}
return ack;
}
// 合成模板
uint8_t AS608_RegModel(void) {
AS608_Packet packet;
packet.head[0] = 0xEF;
packet.head[1] = 0x01;
memcpy(packet.addr, &AS608_Address, 4);
packet.pid = AS608_PACK_CMD;
packet.len[0] = 0x00;
packet.len[1] = 0x03;
packet.data[0] = AS608_CMD_REG_MODEL;
AS608_SendPacket(&packet);
return AS608_ReceivePacket(&packet, 2000);
}
// 储存模板
uint8_t AS608_StoreChar(uint8_t bufferID, uint16_t pageID) {
AS608_Packet packet;
packet.head[0] = 0xEF;
packet.head[1] = 0x01;
memcpy(packet.addr, &AS608_Address, 4);
packet.pid = AS608_PACK_CMD;
packet.len[0] = 0x00;
packet.len[1] = 0x06;
packet.data[0] = AS608_CMD_STORE_CHAR;
packet.data[1] = bufferID;
packet.data[2] = (pageID >> 8) & 0xFF;
packet.data[3] = pageID & 0xFF;
AS608_SendPacket(&packet);
return AS608_ReceivePacket(&packet, 2000);
}
// 删除指纹模板
uint8_t AS608_DeleteChar(uint16_t pageID, uint16_t count) {
AS608_Packet packet;
packet.head[0] = 0xEF;
packet.head[1] = 0x01;
memcpy(packet.addr, &AS608_Address, 4);
packet.pid = AS608_PACK_CMD;
packet.len[0] = 0x00;
packet.len[1] = 0x07;
packet.data[0] = AS608_CMD_DELETE_CHAR;
packet.data[1] = (pageID >> 8) & 0xFF;
packet.data[2] = pageID & 0xFF;
packet.data[3] = (count >> 8) & 0xFF;
packet.data[4] = count & 0xFF;
AS608_SendPacket(&packet);
return AS608_ReceivePacket(&packet, 2000);
}
// 清空指纹库
uint8_t AS608_Empty(void) {
AS608_Packet packet;
packet.head[0] = 0xEF;
packet.head[1] = 0x01;
memcpy(packet.addr, &AS608_Address, 4);
packet.pid = AS608_PACK_CMD;
packet.len[0] = 0x00;
packet.len[1] = 0x03;
packet.data[0] = AS608_CMD_EMPTY;
AS608_SendPacket(&packet);
return AS608_ReceivePacket(&packet, 2000);
}
// 录入指纹(完整流程)
uint8_t AS608_EnrollFingerprint(uint16_t pageID) {
uint8_t ack;
// 第一次录入
printf("请按压手指...\r\n");
while(AS608_GetImage() != AS608_ACK_SUCCESS) {
HAL_Delay(200);
}
ack = AS608_GenChar(1);
if(ack != AS608_ACK_SUCCESS) {
return ack;
}
printf("请抬起手指...\r\n");
HAL_Delay(1000);
// 第二次录入
printf("请再次按压手指...\r\n");
while(AS608_GetImage() != AS608_ACK_SUCCESS) {
HAL_Delay(200);
}
ack = AS608_GenChar(2);
if(ack != AS608_ACK_SUCCESS) {
return ack;
}
// 合成模板
ack = AS608_RegModel();
if(ack != AS608_ACK_SUCCESS) {
return ack;
}
// 储存模板
ack = AS608_StoreChar(1, pageID);
return ack;
}
// 验证指纹(完整流程)
uint8_t AS608_VerifyFingerprint(AS608_SearchResult *result) {
uint8_t ack;
// 录入图像
ack = AS608_GetImage();
if(ack != AS608_ACK_SUCCESS) {
return ack;
}
// 生成特征
ack = AS608_GenChar(1);
if(ack != AS608_ACK_SUCCESS) {
return ack;
}
// 搜索指纹库
ack = AS608_Search(1, result);
return ack;
}
3.3 指纹识别流程
指纹验证的完整流程如下:
- 检测手指按压
- 采集指纹图像
- 提取特征点
- 在指纹库中搜索匹配
- 返回匹配结果和得分
这里有个坑要注意:AS608对手指的湿度比较敏感,太干或太湿都会影响识别率。我们在实际使用中加了一个重试机制,允许用户在3秒内尝试3次。
四、人脸识别模块实现
4.1 K210人脸识别方案
人脸识别我们使用K210芯片,它内置了KPU神经网络处理器,可以运行YOLO等模型。我们采用的方案是:
- OV2640摄像头采集图像(320x240分辨率)
- K210运行人脸检测模型(基于YOLO v2改进)
- 提取128维人脸特征向量
- 与数据库中的特征向量进行余弦相似度比对
4.2 K210端代码(MicroPython)
python
# face_recognition.py - K210端人脸识别代码
import sensor
import image
import lcd
import KPU as kpu
import time
from fpioa_manager import fm
from Maix import GPIO
import gc
# 初始化摄像头
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA) # 320x240
sensor.set_hmirror(1)
sensor.set_vflip(1)
sensor.run(1)
# 初始化LCD
lcd.init()
lcd.rotation(2)
# 加载人脸检测模型
task_fd = kpu.load(0x300000) # 人脸检测模型地址
task_fe = kpu.load(0x400000) # 特征提取模型地址
# 人脸检测锚点
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025)
kpu.init_yolo2(task_fd, 0.5, 0.3, 5, anchor)
# 人脸数据库(实际应该从STM32传输或从SD卡加载)
face_database = []
class FaceFeature:
def __init__(self, user_id, feature):
self.user_id = user_id
self.feature = feature
def load_face_database():
"""从文件加载人脸数据库"""
global face_database
try:
with open('/sd/face_db.txt', 'r') as f:
lines = f.readlines()
for line in lines:
parts = line.strip().split(',')
user_id = int(parts[0])
feature = [float(x) for x in parts[1:]]
face_database.append(FaceFeature(user_id, feature))
print("已加载 %d 个人脸特征" % len(face_database))
except:
print("人脸数据库加载失败")
def extract_feature(img, face_box):
"""提取人脸特征"""
# 裁剪人脸区域
face_cut = img.cut(face_box[0], face_box[1], face_box[2], face_box[3])
face_cut.resize(128, 128)
face_cut.pix_to_ai()
# 运行特征提取模型
fmap = kpu.forward(task_fe, face_cut)
feature = kpu.face_encode(fmap[:])
del face_cut
gc.collect()
return feature
def cosine_similarity(feat1, feat2):
"""计算余弦相似度"""
dot_product = sum(a * b for a, b in zip(feat1, feat2))
norm1 = sum(a * a for a in feat1) ** 0.5
norm2 = sum(b * b for b in feat2) ** 0.5
if norm1 == 0 or norm2 == 0:
return 0
return dot_product / (norm1 * norm2)
def recognize_face(feature, threshold=0.75):
"""识别人脸"""
max_similarity = 0
best_match_id = -1
for face_data in face_database:
similarity = cosine_similarity(feature, face_data.feature)
if similarity > max_similarity:
max_similarity = similarity
best_match_id = face_data.user_id
if max_similarity >= threshold:
return best_match_id, max_similarity
else:
return -1, max_similarity
def enroll_face(user_id):
"""录入新人脸"""
print("录入人脸 ID:", user_id)
features = []
# 采集5张不同角度的人脸
for i in range(5):
print("请调整角度,准备采集第 %d/5 张" % (i + 1))
time.sleep(2)
while True:
img = sensor.snapshot()
faces = kpu.run_yolo2(task_fd, img)
if faces:
for face in faces:
# 绘制人脸框
x, y, w, h = face.rect()
img.draw_rectangle(x, y, w, h, color=(0, 255, 0), thickness=2)
# 提取特征
feature = extract_feature(img, face.rect())
features.append(feature)
break
break
lcd.display(img)
# 计算平均特征(简化处理,实际可以用更复杂的融合方法)
avg_feature = [sum(feat[i] for feat in features) / len(features)
for i in range(len(features[0]))]
# 保存到数据库
face_database.append(FaceFeature(user_id, avg_feature))
# 保存到文件
with open('/sd/face_db.txt', 'a') as f:
line = str(user_id) + ',' + ','.join(map(str, avg_feature)) + '\n'
f.write(line)
print("人脸录入成功!")
return True
def main_loop():
"""主循环"""
load_face_database()
while True:
img = sensor.snapshot()
faces = kpu.run_yolo2(task_fd, img)
if faces:
for face in faces:
# 绘制人脸框
x, y, w, h = face.rect()
img.draw_rectangle(x, y, w, h, color=(0, 255, 0), thickness=2)
# 提取并识别
feature = extract_feature(img, face.rect())
user_id, similarity = recognize_face(feature)
if user_id != -1:
info = "User: %d (%.2f%%)" % (user_id, similarity * 100)
img.draw_string(x, y - 20, info, color=(0, 255, 0), scale=2)
print("识别成功:", info)
# 发送识别结果到STM32
send_result_to_stm32(user_id, similarity)
else:
img.draw_string(x, y - 20, "Unknown", color=(255, 0, 0), scale=2)
lcd.display(img)
gc.collect()
def send_result_to_stm32(user_id, similarity):
"""通过UART发送识别结果到STM32"""
# 数据格式: 0xFF 0xAA [user_id_high] [user_id_low] [similarity*100] 0x55
from machine import UART
uart = UART.UART3
data = bytearray([0xFF, 0xAA,
(user_id >> 8) & 0xFF,
user_id & 0xFF,
int(similarity * 100),
0x55])
uart.write(data)
# 启动主循环
if __name__ == "__main__":
main_loop()
4.3 STM32端接收处理
c
// face_recognition.h
#ifndef __FACE_RECOGNITION_H
#define __FACE_RECOGNITION_H
#include "stm32f4xx_hal.h"
#define FACE_UART USART2
#define FACE_RX_BUFFER_SIZE 256
typedef struct {
uint16_t userID;
float similarity;
uint8_t isValid;
} FaceRecognitionResult;
void Face_Init(UART_HandleTypeDef *huart);
void Face_ProcessData(void);
uint8_t Face_GetResult(FaceRecognitionResult *result);
void Face_StartEnroll(uint16_t userID);
void Face_StartRecognition(void);
#endif
c
// face_recognition.c
#include "face_recognition.h"
#include "string.h"
static UART_HandleTypeDef *face_uart;
static uint8_t Face_RxBuffer[FACE_RX_BUFFER_SIZE];
static uint8_t Face_RxIndex = 0;
static FaceRecognitionResult Face_LatestResult = {0};
void Face_Init(UART_HandleTypeDef *huart) {
face_uart = huart;
// 启动UART接收中断
HAL_UART_Receive_IT(face_uart, &Face_RxBuffer[Face_RxIndex], 1);
}
// UART接收回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if(huart->Instance == FACE_UART) {
Face_RxIndex++;
if(Face_RxIndex >= 6) {
// 检查数据包完整性
if(Face_RxBuffer[0] == 0xFF && Face_RxBuffer[1] == 0xAA &&
Face_RxBuffer[5] == 0x55) {
Face_LatestResult.userID = (Face_RxBuffer[2] << 8) | Face_RxBuffer[3];
Face_LatestResult.similarity = Face_RxBuffer[4] / 100.0f;
Face_LatestResult.isValid = 1;
}
Face_RxIndex = 0;
}
HAL_UART_Receive_IT(face_uart, &Face_RxBuffer[Face_RxIndex], 1);
}
}
uint8_t Face_GetResult(FaceRecognitionResult *result) {
if(Face_LatestResult.isValid) {
memcpy(result, &Face_LatestResult, sizeof(FaceRecognitionResult));
Face_LatestResult.isValid = 0;
return 1;
}
return 0;
}
void Face_StartEnroll(uint16_t userID) {
// 发送录入命令到K210
uint8_t cmd[] = {0xFF, 0xFE, 0x01, (userID >> 8) & 0xFF, userID & 0xFF, 0x55};
HAL_UART_Transmit(face_uart, cmd, sizeof(cmd), 100);
}
void Face_StartRecognition(void) {
// 发送识别命令到K210
uint8_t cmd[] = {0xFF, 0xFE, 0x02, 0x00, 0x00, 0x55};
HAL_UART_Transmit(face_uart, cmd, sizeof(cmd), 100);
}
五、密码键盘实现
5.1 矩阵键盘扫描原理
4x4矩阵键盘通过行列扫描来检测按键。原理是:
- 依次给每一行输出低电平,其他行输出高电平
- 读取每一列的电平状态
- 如果某列为低电平,说明该行该列的按键被按下
键盘布局:
1 2 3 A
4 5 6 B
7 8 9 C
* 0 # D
5.2 键盘驱动代码
c
// keypad.h
#ifndef __KEYPAD_H
#define __KEYPAD_H
#include "stm32f4xx_hal.h"
#define KEYPAD_ROWS 4
#define KEYPAD_COLS 4
#define KEY_NONE 0xFF
#define KEY_PRESSED 1
#define KEY_RELEASED 0
// 按键值定义
#define KEY_1 0x01
#define KEY_2 0x02
#define KEY_3 0x03
#define KEY_A 0x0A
#define KEY_4 0x04
#define KEY_5 0x05
#define KEY_6 0x06
#define KEY_B 0x0B
#define KEY_7 0x07
#define KEY_8 0x08
#define KEY_9 0x09
#define KEY_C 0x0C
#define KEY_STAR 0x0E
#define KEY_0 0x00
#define KEY_HASH 0x0F
#define KEY_D 0x0D
void Keypad_Init(void);
uint8_t Keypad_Scan(void);
uint8_t Keypad_GetKey(void);
#endif
c
// keypad.c
#include "keypad.h"
// 键值映射表
static const uint8_t KeyMap[KEYPAD_ROWS][KEYPAD_COLS] = {
{KEY_1, KEY_2, KEY_3, KEY_A},
{KEY_4, KEY_5, KEY_6, KEY_B},
{KEY_7, KEY_8, KEY_9, KEY_C},
{KEY_STAR, KEY_0, KEY_HASH, KEY_D}
};
// 行引脚数组
static uint16_t RowPins[KEYPAD_ROWS] = {
KEY_ROW1_PIN, KEY_ROW2_PIN, KEY_ROW3_PIN, KEY_ROW4_PIN
};
// 列引脚数组
static uint16_t ColPins[KEYPAD_COLS] = {
KEY_COL1_PIN, KEY_COL2_PIN, KEY_COL3_PIN, KEY_COL4_PIN
};
static uint8_t LastKey = KEY_NONE;
static uint8_t KeyState = KEY_RELEASED;
void Keypad_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置行引脚为输出
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
for(int i = 0; i < KEYPAD_ROWS; i++) {
GPIO_InitStruct.Pin = RowPins[i];
HAL_GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(KEY_GPIO_PORT, RowPins[i], GPIO_PIN_SET);
}
// 配置列引脚为输入上拉
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
for(int i = 0; i < KEYPAD_COLS; i++) {
GPIO_InitStruct.Pin = ColPins[i];
HAL_GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStruct);
}
}
uint8_t Keypad_Scan(void) {
uint8_t key = KEY_NONE;
for(int row = 0; row < KEYPAD_ROWS; row++) {
// 设置当前行为低电平
HAL_GPIO_WritePin(KEY_GPIO_PORT, RowPins[row], GPIO_PIN_RESET);
// 延时消抖
HAL_Delay(1);
// 检测列
for(int col = 0; col < KEYPAD_COLS; col++) {
if(HAL_GPIO_ReadPin(KEY_GPIO_PORT, ColPins[col]) == GPIO_PIN_RESET) {
key = KeyMap[row][col];
// 等待按键释放
while(HAL_GPIO_ReadPin(KEY_GPIO_PORT, ColPins[col]) == GPIO_PIN_RESET) {
HAL_Delay(10);
}
break;
}
}
// 恢复当前行为高电平
HAL_GPIO_WritePin(KEY_GPIO_PORT, RowPins[row], GPIO_PIN_SET);
if(key != KEY_NONE) {
break;
}
}
return key;
}
uint8_t Keypad_GetKey(void) {
uint8_t key = Keypad_Scan();
if(key != KEY_NONE && key != LastKey) {
LastKey = key;
KeyState = KEY_PRESSED;
return key;
} else if(key == KEY_NONE) {
LastKey = KEY_NONE;
KeyState = KEY_RELEASED;
}
return KEY_NONE;
}
5.3 密码验证逻辑
c
// password.h
#ifndef __PASSWORD_H
#define __PASSWORD_H
#include "stm32f4xx_hal.h"
#define MAX_PASSWORD_LENGTH 8
#define MAX_USERS 50
typedef struct {
uint16_t userID;
char password[MAX_PASSWORD_LENGTH + 1];
uint8_t isActive;
} PasswordEntry;
void Password_Init(void);
uint8_t Password_Verify(const char *password, uint16_t *userID);
uint8_t Password_Set(uint16_t userID, const char *password);
uint8_t Password_Delete(uint16_t userID);
void Password_Clear(void);
#endif
c
// password.c
#include "password.h"
#include "string.h"
#include "stdio.h"
static PasswordEntry PasswordDatabase[MAX_USERS];
static uint16_t PasswordCount = 0;
// 初始化密码数据库
void Password_Init(void) {
memset(PasswordDatabase, 0, sizeof(PasswordDatabase));
PasswordCount = 0;
// 从Flash加载密码数据库(这里简化处理,实际应该使用Flash存储)
// 默认添加一个管理员密码
Password_Set(0, "123456");
}
// 验证密码
uint8_t Password_Verify(const char *password, uint16_t *userID) {
if(password == NULL || strlen(password) == 0) {
return 0;
}
for(int i = 0; i < MAX_USERS; i++) {
if(PasswordDatabase[i].isActive) {
if(strcmp(PasswordDatabase[i].password, password) == 0) {
if(userID != NULL) {
*userID = PasswordDatabase[i].userID;
}
return 1;
}
}
}
return 0;
}
// 设置密码
uint8_t Password_Set(uint16_t userID, const char *password) {
if(password == NULL || strlen(password) == 0 || strlen(password) > MAX_PASSWORD_LENGTH) {
return 0;
}
// 检查用户是否已存在
for(int i = 0; i < MAX_USERS; i++) {
if(PasswordDatabase[i].isActive && PasswordDatabase[i].userID == userID) {
// 更新密码
strncpy(PasswordDatabase[i].password, password, MAX_PASSWORD_LENGTH);
return 1;
}
}
// 添加新用户
for(int i = 0; i < MAX_USERS; i++) {
if(!PasswordDatabase[i].isActive) {
PasswordDatabase[i].userID = userID;
strncpy(PasswordDatabase[i].password, password, MAX_PASSWORD_LENGTH);
PasswordDatabase[i].isActive = 1;
PasswordCount++;
return 1;
}
}
return 0; // 数据库已满
}
// 删除密码
uint8_t Password_Delete(uint16_t userID) {
for(int i = 0; i < MAX_USERS; i++) {
if(PasswordDatabase[i].isActive && PasswordDatabase[i].userID == userID) {
memset(&PasswordDatabase[i], 0, sizeof(PasswordEntry));
PasswordCount--;
return 1;
}
}
return 0;
}
// 清空密码库
void Password_Clear(void) {
memset(PasswordDatabase, 0, sizeof(PasswordDatabase));
PasswordCount = 0;
}
六、OLED显示驱动
6.1 OLED显示模块
我们使用0.96寸OLED屏幕(SSD1306驱动芯片),通过I2C接口通信。
c
// oled.h
#ifndef __OLED_H
#define __OLED_H
#include "stm32f4xx_hal.h"
#define OLED_ADDRESS 0x78 // I2C地址
void OLED_Init(I2C_HandleTypeDef *hi2c);
void OLED_Clear(void);
void OLED_ShowString(uint8_t x, uint8_t y, const char *str);
void OLED_ShowNumber(uint8_t x, uint8_t y, uint32_t num, uint8_t len);
void OLED_ShowStatus(const char *status);
#endif
c
// oled.c (简化版本)
#include "oled.h"
#include "string.h"
#include "stdio.h"
static I2C_HandleTypeDef *oled_i2c;
// 8x16字体
const uint8_t Font8x16[][16] = {
// ASCII字符字模数据...(这里省略,实际需要完整字库)
};
void OLED_WriteCmd(uint8_t cmd) {
uint8_t data[2] = {0x00, cmd};
HAL_I2C_Master_Transmit(oled_i2c, OLED_ADDRESS, data, 2, 100);
}
void OLED_WriteData(uint8_t data) {
uint8_t buf[2] = {0x40, data};
HAL_I2C_Master_Transmit(oled_i2c, OLED_ADDRESS, buf, 2, 100);
}
void OLED_Init(I2C_HandleTypeDef *hi2c) {
oled_i2c = hi2c;
HAL_Delay(100);
OLED_WriteCmd(0xAE); // 关闭显示
OLED_WriteCmd(0x20); // 设置内存地址模式
OLED_WriteCmd(0x10);
OLED_WriteCmd(0xB0); // 设置页地址
OLED_WriteCmd(0xC8); // 设置COM扫描方向
OLED_WriteCmd(0x00);
OLED_WriteCmd(0x10);
OLED_WriteCmd(0x40); // 设置起始行地址
OLED_WriteCmd(0x81); // 设置对比度
OLED_WriteCmd(0xFF);
OLED_WriteCmd(0xA1); // 设置段重映射
OLED_WriteCmd(0xA6); // 正常显示
OLED_WriteCmd(0xA8); // 设置复用率
OLED_WriteCmd(0x3F);
OLED_WriteCmd(0xA4); // 全局显示开启
OLED_WriteCmd(0xD3); // 设置显示偏移
OLED_WriteCmd(0x00);
OLED_WriteCmd(0xD5); // 设置时钟分频因子
OLED_WriteCmd(0xF0);
OLED_WriteCmd(0xD9); // 设置预充电周期
OLED_WriteCmd(0x22);
OLED_WriteCmd(0xDA); // 设置COM脚硬件配置
OLED_WriteCmd(0x12);
OLED_WriteCmd(0xDB); // 设置VCOMH电压倍率
OLED_WriteCmd(0x20);
OLED_WriteCmd(0x8D); // 使能充电泵
OLED_WriteCmd(0x14);
OLED_WriteCmd(0xAF); // 开启显示
OLED_Clear();
}
void OLED_Clear(void) {
for(uint8_t i = 0; i < 8; i++) {
OLED_WriteCmd(0xB0 + i);
OLED_WriteCmd(0x00);
OLED_WriteCmd(0x10);
for(uint8_t n = 0; n < 128; n++) {
OLED_WriteData(0x00);
}
}
}
void OLED_ShowString(uint8_t x, uint8_t y, const char *str) {
// 显示字符串实现...
// 这里简化,实际需要根据字库显示
}
void OLED_ShowStatus(const char *status) {
OLED_Clear();
OLED_ShowString(0, 0, "Smart Lock System");
OLED_ShowString(0, 2, "Status:");
OLED_ShowString(0, 4, status);
}
七、门锁控制系统
7.1 步进电机控制
c
// motor.h
#ifndef __MOTOR_H
#define __MOTOR_H
#include "stm32f4xx_hal.h"
typedef enum {
MOTOR_DIR_LOCK, // 上锁
MOTOR_DIR_UNLOCK // 开锁
} MotorDirection;
void Motor_Init(void);
void Motor_Lock(void);
void Motor_Unlock(void);
void Motor_Stop(void);
uint8_t Motor_GetStatus(void);
#endif
c
// motor.c
#include "motor.h"
// 步进电机相序(四相八拍)
static const uint8_t StepSequence[8][4] = {
{1, 0, 0, 0},
{1, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 1},
{1, 0, 0, 1}
};
static uint8_t CurrentStep = 0;
static uint8_t IsLocked = 1;
void Motor_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = MOTOR_IN1_PIN | MOTOR_IN2_PIN | MOTOR_IN3_PIN | MOTOR_IN4_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(MOTOR_GPIO_PORT, &GPIO_InitStruct);
Motor_Stop();
}
static void Motor_Step(uint8_t step) {
HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_IN1_PIN,
StepSequence[step][0] ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_IN2_PIN,
StepSequence[step][1] ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_IN3_PIN,
StepSequence[step][2] ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_IN4_PIN,
StepSequence[step][3] ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
static void Motor_Rotate(MotorDirection dir, uint16_t steps) {
for(uint16_t i = 0; i < steps; i++) {
if(dir == MOTOR_DIR_UNLOCK) {
CurrentStep++;
if(CurrentStep >= 8) CurrentStep = 0;
} else {
if(CurrentStep == 0) CurrentStep = 7;
else CurrentStep--;
}
Motor_Step(CurrentStep);
HAL_Delay(2); // 步进延时,控制速度
}
}
void Motor_Lock(void) {
if(!IsLocked) {
Motor_Rotate(MOTOR_DIR_LOCK, 512); // 旋转90度(根据实际电机调整)
IsLocked = 1;
Motor_Stop();
}
}
void Motor_Unlock(void) {
if(IsLocked) {
Motor_Rotate(MOTOR_DIR_UNLOCK, 512); // 旋转90度
IsLocked = 0;
// 5秒后自动上锁
HAL_Delay(5000);
Motor_Lock();
}
}
void Motor_Stop(void) {
HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_IN1_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_IN2_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_IN3_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_GPIO_PORT, MOTOR_IN4_PIN, GPIO_PIN_RESET);
}
uint8_t Motor_GetStatus(void) {
return IsLocked;
}
八、主程序实现
8.1 主函数
c
// main.c
#include "main.h"
#include "as608.h"
#include "face_recognition.h"
#include "keypad.h"
#include "password.h"
#include "oled.h"
#include "motor.h"
#include "stdio.h"
#include "string.h"
// 外设句柄
UART_HandleTypeDef huart2; // 人脸识别
UART_HandleTypeDef huart3; // 指纹模块
I2C_HandleTypeDef hi2c1; // OLED
// 认证模式
typedef enum {
AUTH_MODE_IDLE,
AUTH_MODE_FINGERPRINT,
AUTH_MODE_FACE,
AUTH_MODE_PASSWORD
} AuthMode;
// 系统状态
typedef struct {
AuthMode currentMode;
uint8_t isUnlocked;
uint32_t lastActivityTime;
uint16_t authenticatedUserID;
} SystemStatus;
static SystemStatus sysStatus = {
.currentMode = AUTH_MODE_IDLE,
.isUnlocked = 0,
.lastActivityTime = 0,
.authenticatedUserID = 0xFFFF
};
// 函数声明
void SystemClock_Config(void);
void GPIO_Init(void);
void USART2_Init(void);
void USART3_Init(void);
void I2C1_Init(void);
void Process_Fingerprint_Auth(void);
void Process_Face_Auth(void);
void Process_Password_Auth(void);
void Update_Display(void);
void Check_Timeout(void);
int main(void) {
// 初始化HAL库
HAL_Init();
SystemClock_Config();
// 初始化外设
GPIO_Init();
USART2_Init();
USART3_Init();
I2C1_Init();
// 初始化各模块
AS608_Init(&huart3);
Face_Init(&huart2);
Keypad_Init();
Password_Init();
OLED_Init(&hi2c1);
Motor_Init();
// 显示欢迎信息
OLED_ShowStatus("System Ready");
printf("智能门锁系统启动\r\n");
HAL_Delay(2000);
// 主循环
while(1) {
// 更新显示
Update_Display();
// 检测用户输入,选择认证方式
uint8_t key = Keypad_GetKey();
if(key == KEY_1) {
sysStatus.currentMode = AUTH_MODE_FINGERPRINT;
printf("切换到指纹识别模式\r\n");
} else if(key == KEY_2) {
sysStatus.currentMode = AUTH_MODE_FACE;
printf("切换到人脸识别模式\r\n");
} else if(key == KEY_3) {
sysStatus.currentMode = AUTH_MODE_PASSWORD;
printf("切换到密码输入模式\r\n");
}
// 处理对应的认证模式
switch(sysStatus.currentMode) {
case AUTH_MODE_FINGERPRINT:
Process_Fingerprint_Auth();
break;
case AUTH_MODE_FACE:
Process_Face_Auth();
break;
case AUTH_MODE_PASSWORD:
Process_Password_Auth();
break;
default:
break;
}
// 检查超时
Check_Timeout();
HAL_Delay(50);
}
}
// 指纹认证处理
void Process_Fingerprint_Auth(void) {
OLED_ShowStatus("Place Finger...");
AS608_SearchResult result;
uint8_t ack = AS608_VerifyFingerprint(&result);
if(ack == AS608_ACK_SUCCESS) {
printf("指纹识别成功!用户ID: %d, 得分: %d\r\n",
result.pageID, result.matchScore);
sysStatus.authenticatedUserID = result.pageID;
sysStatus.isUnlocked = 1;
sysStatus.lastActivityTime = HAL_GetTick();
OLED_ShowStatus("Auth Success!");
Motor_Unlock();
HAL_Delay(2000);
sysStatus.currentMode = AUTH_MODE_IDLE;
} else if(ack == AS608_ACK_NOT_FOUND) {
printf("指纹不匹配\r\n");
OLED_ShowStatus("Auth Failed!");
HAL_Delay(1000);
sysStatus.currentMode = AUTH_MODE_IDLE;
} else if(ack == AS608_ACK_NO_FINGER) {
// 没有检测到手指,继续等待
}
}
// 人脸认证处理
void Process_Face_Auth(void) {
OLED_ShowStatus("Face Detecting...");
// 启动人脸识别
Face_StartRecognition();
FaceRecognitionResult result;
uint32_t startTime = HAL_GetTick();
// 等待识别结果(最多10秒)
while(HAL_GetTick() - startTime < 10000) {
if(Face_GetResult(&result)) {
if(result.similarity >= 0.75f) {
printf("人脸识别成功!用户ID: %d, 相似度: %.2f\r\n",
result.userID, result.similarity);
sysStatus.authenticatedUserID = result.userID;
sysStatus.isUnlocked = 1;
sysStatus.lastActivityTime = HAL_GetTick();
OLED_ShowStatus("Auth Success!");
Motor_Unlock();
HAL_Delay(2000);
sysStatus.currentMode = AUTH_MODE_IDLE;
return;
} else {
printf("人脸不匹配\r\n");
OLED_ShowStatus("Auth Failed!");
HAL_Delay(1000);
sysStatus.currentMode = AUTH_MODE_IDLE;
return;
}
}
HAL_Delay(100);
}
// 超时
printf("人脸识别超时\r\n");
OLED_ShowStatus("Timeout!");
HAL_Delay(1000);
sysStatus.currentMode = AUTH_MODE_IDLE;
}
// 密码认证处理
void Process_Password_Auth(void) {
OLED_ShowStatus("Enter Password:");
static char password[MAX_PASSWORD_LENGTH + 1] = {0};
static uint8_t pwdIndex = 0;
uint8_t key = Keypad_GetKey();
if(key >= KEY_0 && key <= KEY_9) {
if(pwdIndex < MAX_PASSWORD_LENGTH) {
password[pwdIndex++] = '0' + key;
password[pwdIndex] = '\0';
// 显示星号
char display[32];
sprintf(display, "PWD: ");
for(int i = 0; i < pwdIndex; i++) {
strcat(display, "*");
}
OLED_ShowStatus(display);
}
} else if(key == KEY_HASH) {
// 确认输入
uint16_t userID;
if(Password_Verify(password, &userID)) {
printf("密码验证成功!用户ID: %d\r\n", userID);
sysStatus.authenticatedUserID = userID;
sysStatus.isUnlocked = 1;
sysStatus.lastActivityTime = HAL_GetTick();
OLED_ShowStatus("Auth Success!");
Motor_Unlock();
} else {
printf("密码错误\r\n");
OLED_ShowStatus("Wrong Password!");
}
// 清空密码
memset(password, 0, sizeof(password));
pwdIndex = 0;
HAL_Delay(2000);
sysStatus.currentMode = AUTH_MODE_IDLE;
} else if(key == KEY_STAR) {
// 取消输入
memset(password, 0, sizeof(password));
pwdIndex = 0;
sysStatus.currentMode = AUTH_MODE_IDLE;
}
}
// 更新显示
void Update_Display(void) {
static uint32_t lastUpdate = 0;
if(HAL_GetTick() - lastUpdate > 1000) {
lastUpdate = HAL_GetTick();
if(sysStatus.currentMode == AUTH_MODE_IDLE) {
if(sysStatus.isUnlocked) {
OLED_ShowStatus("Door Unlocked");
} else {
OLED_ShowStatus("Press Key:\n1-Finger 2-Face\n3-Password");
}
}
}
}
// 检查超时
void Check_Timeout(void) {
// 如果门已开启超过30秒,自动上锁
if(sysStatus.isUnlocked) {
if(HAL_GetTick() - sysStatus.lastActivityTime > 30000) {
Motor_Lock();
sysStatus.isUnlocked = 0;
sysStatus.authenticatedUserID = 0xFFFF;
printf("超时自动上锁\r\n");
}
}
// 如果在认证模式下超过15秒无操作,返回空闲模式
if(sysStatus.currentMode != AUTH_MODE_IDLE) {
if(HAL_GetTick() - sysStatus.lastActivityTime > 15000) {
sysStatus.currentMode = AUTH_MODE_IDLE;
printf("认证超时,返回空闲模式\r\n");
}
}
}
// 系统时钟配置
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 7;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5);
}
// GPIO初始化
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOE_CLK_ENABLE();
}
// USART2初始化(人脸识别)
void USART2_Init(void) {
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart2);
}
// USART3初始化(指纹模块)
void USART3_Init(void) {
huart3.Instance = USART3;
huart3.Init.BaudRate = 57600;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&huart3);
}
// I2C1初始化(OLED)
void I2C1_Init(void) {
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
九、用户管理系统
为了管理多个用户,我们需要实现一个用户管理系统:
c
// user_manager.h
#ifndef __USER_MANAGER_H
#define __USER_MANAGER_H
#include "stm32f4xx_hal.h"
#define MAX_USER_COUNT 50
typedef enum {
AUTH_TYPE_FINGERPRINT = 0x01,
AUTH_TYPE_FACE = 0x02,
AUTH_TYPE_PASSWORD = 0x04,
AUTH_TYPE_ALL = 0x07
} AuthType;
typedef struct {
uint16_t userID;
char userName[32];
uint8_t authMethods; // 位掩码,表示启用的认证方式
uint8_t isAdmin;
uint8_t isActive;
uint32_t lastAccessTime;
} UserInfo;
void UserManager_Init(void);
uint8_t UserManager_Add(uint16_t userID, const char *name, uint8_t authMethods, uint8_t isAdmin);
uint8_t UserManager_Delete(uint16_t userID);
uint8_t UserManager_GetInfo(uint16_t userID, UserInfo *info);
uint8_t UserManager_CheckAuth(uint16_t userID, AuthType authType);
void UserManager_UpdateAccessTime(uint16_t userID);
uint16_t UserManager_GetCount(void);
#endif
十、数据存储方案
10.1 Flash存储
为了在断电后保留用户数据,我们需要将数据存储到Flash中:
c
// flash_storage.h
#ifndef __FLASH_STORAGE_H
#define __FLASH_STORAGE_H
#include "stm32f4xx_hal.h"
// Flash存储地址定义
#define FLASH_USER_START_ADDR 0x08080000 // 扇区8起始地址
#define FLASH_USER_END_ADDR 0x080FFFFF // 扇区11结束地址
uint8_t Flash_WriteData(uint32_t address, uint8_t *data, uint32_t length);
uint8_t Flash_ReadData(uint32_t address, uint8_t *data, uint32_t length);
uint8_t Flash_EraseData(uint32_t address);
#endif
十一、安全机制
11.1 防暴力破解
实现尝试次数限制:
c
#define MAX_AUTH_ATTEMPTS 5
#define LOCKOUT_TIME_MS 300000 // 5分钟
static uint8_t authAttempts = 0;
static uint32_t lockoutEndTime = 0;
uint8_t CheckAuthLockout(void) {
if(HAL_GetTick() < lockoutEndTime) {
return 0; // 仍在锁定期
}
if(authAttempts >= MAX_AUTH_ATTEMPTS) {
lockoutEndTime = HAL_GetTick() + LOCKOUT_TIME_MS;
authAttempts = 0;
return 0;
}
return 1; // 可以进行认证
}
void RecordAuthAttempt(uint8_t success) {
if(success) {
authAttempts = 0;
} else {
authAttempts++;
}
}
11.2 日志记录
记录所有开锁操作:
c
typedef struct {
uint32_t timestamp;
uint16_t userID;
AuthType authType;
uint8_t success;
} AccessLog;
void Log_RecordAccess(uint16_t userID, AuthType authType, uint8_t success) {
AccessLog log;
log.timestamp = HAL_GetTick();
log.userID = userID;
log.authType = authType;
log.success = success;
// 保存到Flash或发送到服务器
}
十二、系统调试与测试
12.1 调试技巧
在开发过程中,我遇到了一些问题,总结几个调试技巧:
- 串口日志:所有关键操作都输出日志,便于定位问题
- LED指示:用LED指示各模块工作状态
- 单步测试:先单独测试每个模块,再整合
- 电源质量:确保电源稳定,不要用劣质电源
12.2 常见问题
问题1:指纹模块通信失败
- 检查波特率是否正确
- 检查RX/TX是否接反
- 确认供电电压(3.3V或5V)
问题2:人脸识别误识别率高
- 增加训练样本数量
- 调整阈值参数
- 改善光照条件
问题3:电机不转或抖动
- 检查步进顺序是否正确
- 调整步进延时
- 确认驱动电流是否足够
十三、性能优化
13.1 响应速度优化
- 指纹识别:< 1秒
- 人脸识别:< 2秒
- 密码验证:即时
通过RTOS可以进一步优化多任务响应。
13.2 功耗优化
待机模式下功耗优化:
- 关闭摄像头
- 降低MCU频率
- 使用低功耗模式
十四、总结与展望
这套智能门锁系统实现了指纹、人脸、密码三种认证方式,具备较高的安全性和便利性。整个项目涉及的技术点包括:
- 嵌入式硬件设计
- STM32底层驱动开发
- 通信协议解析
- 人工智能算法应用
- 系统架构设计
后续可以扩展的功能:
- 增加NFC/蓝牙开锁
- 联网远程控制
- 手机APP管理
- 临时密码生成
- 联动智能家居
整个项目代码量约5000行,核心代码都已给出。实际使用时需要根据具体硬件调整参数。希望这篇文章对大家有帮助!
附录:完整工程文件结构
SmartLock/
├── Core/
│ ├── Inc/
│ │ ├── main.h
│ │ ├── stm32f4xx_hal_conf.h
│ │ └── stm32f4xx_it.h
│ └── Src/
│ ├── main.c
│ ├── stm32f4xx_hal_msp.c
│ └── stm32f4xx_it.c
├── Drivers/
│ ├── AS608/
│ │ ├── as608.h
│ │ └── as608.c
│ ├── Face/
│ │ ├── face_recognition.h
│ │ └── face_recognition.c
│ ├── Keypad/
│ │ ├── keypad.h
│ │ └── keypad.c
│ ├── OLED/
│ │ ├── oled.h
│ │ └── oled.c
│ └── Motor/
│ ├── motor.h
│ └── motor.c
├── App/
│ ├── password.h
│ ├── password.c
│ ├── user_manager.h
│ ├── user_manager.c
│ ├── flash_storage.h
│ └── flash_storage.c
├── K210/
│ └── face_recognition.py
└── README.md
注意事项:
- 代码仅供学习参考,实际产品需要更完善的安全机制
- 硬件连接务必仔细核对,避免损坏元器件
- 调试过程建议使用J-Link等专业调试器
- 生产环境需要加密存储和通信
有问题欢迎留言交流!