基于STM32的多模态智能门锁系统设计与实现

基于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 电源管理

智能锁对功耗有一定要求,特别是待机状态下。我们设计了三种工作模式:

  1. 激活模式:所有模块上电,功耗约2W
  2. 待机模式:仅保持键盘和触摸唤醒,功耗约200mW
  3. 休眠模式:深度休眠,仅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 指纹识别流程

指纹验证的完整流程如下:

  1. 检测手指按压
  2. 采集指纹图像
  3. 提取特征点
  4. 在指纹库中搜索匹配
  5. 返回匹配结果和得分

这里有个坑要注意:AS608对手指的湿度比较敏感,太干或太湿都会影响识别率。我们在实际使用中加了一个重试机制,允许用户在3秒内尝试3次。

四、人脸识别模块实现

4.1 K210人脸识别方案

人脸识别我们使用K210芯片,它内置了KPU神经网络处理器,可以运行YOLO等模型。我们采用的方案是:

  1. OV2640摄像头采集图像(320x240分辨率)
  2. K210运行人脸检测模型(基于YOLO v2改进)
  3. 提取128维人脸特征向量
  4. 与数据库中的特征向量进行余弦相似度比对

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 调试技巧

在开发过程中,我遇到了一些问题,总结几个调试技巧:

  1. 串口日志:所有关键操作都输出日志,便于定位问题
  2. LED指示:用LED指示各模块工作状态
  3. 单步测试:先单独测试每个模块,再整合
  4. 电源质量:确保电源稳定,不要用劣质电源

12.2 常见问题

问题1:指纹模块通信失败

  • 检查波特率是否正确
  • 检查RX/TX是否接反
  • 确认供电电压(3.3V或5V)

问题2:人脸识别误识别率高

  • 增加训练样本数量
  • 调整阈值参数
  • 改善光照条件

问题3:电机不转或抖动

  • 检查步进顺序是否正确
  • 调整步进延时
  • 确认驱动电流是否足够

十三、性能优化

13.1 响应速度优化

  • 指纹识别:< 1秒
  • 人脸识别:< 2秒
  • 密码验证:即时

通过RTOS可以进一步优化多任务响应。

13.2 功耗优化

待机模式下功耗优化:

  • 关闭摄像头
  • 降低MCU频率
  • 使用低功耗模式

十四、总结与展望

这套智能门锁系统实现了指纹、人脸、密码三种认证方式,具备较高的安全性和便利性。整个项目涉及的技术点包括:

  1. 嵌入式硬件设计
  2. STM32底层驱动开发
  3. 通信协议解析
  4. 人工智能算法应用
  5. 系统架构设计

后续可以扩展的功能:

  • 增加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等专业调试器
  • 生产环境需要加密存储和通信

有问题欢迎留言交流!

相关推荐
xiaohai@Linux2 小时前
STM32在LVGL上实现移植FatFs文件系统(保姆级详细教程)
stm32·单片机·嵌入式硬件·lvgl·fatfs
Jerry丶Li2 小时前
二十二、STM32的ADC(二)(ADC单通道)
stm32·单片机·嵌入式硬件
飞睿科技2 小时前
超越蓝牙与Wi-Fi,UWB技术如何解锁手机下一波创新浪潮?
嵌入式硬件·物联网·智能手机·uwb
点灯小铭2 小时前
基于单片机的交流功率测量仪设计与实现
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
星辰pid4 小时前
STM32基于OLED的多级菜单(控制步进/无刷电机/舵机,含FLASH存储数据掉电不丢失)
stm32·单片机·嵌入式硬件
飞睿科技5 小时前
乐鑫ESP32-C2小尺寸高性价比,物联网应用的理想无线连接方案
嵌入式硬件·物联网·智能路由器
RFID舜识物联网5 小时前
NFC与RFID防伪标签:构筑产品信任的科技防线
大数据·人工智能·科技·嵌入式硬件·物联网·安全
FanXing_zl5 小时前
在整数MCU上实现快速除法计算:原理、方法与优化
单片机·嵌入式硬件·mcu·算法·定点运算
Dunkle.T7 小时前
单片机中NRST引脚复用为GPIO
单片机·嵌入式硬件·复用·py32f002bl15s7·nrst