STM32实战:基于STM32F103的智能门禁系统(RFID+指纹)

文章目录

一、项目概述

本项目基于STM32F103C8T6最小系统板,实现集RFID射频识别、指纹识别于一体的智能门禁系统。系统支持两种验证方式:刷RFID卡或录入指纹,验证通过后控制电磁锁开锁,同时通过OLED屏显示验证状态;验证失败则提示错误并保持锁闭状态。本教程从硬件选型、电路连接、代码编写到系统调试,全程面向零基础小白,步骤详细可落地。

二、硬件清单

硬件名称 型号/规格 数量 作用
STM32核心板 STM32F103C8T6最小系统板 1 主控单元
RFID模块 RC522 1 射频卡识别
指纹识别模块 AS608 1 指纹采集与验证
OLED显示屏 0.96寸 I2C接口 128*64 1 状态显示
电磁锁+驱动模块 12V电磁锁 + 继电器模块 1 门禁开关执行单元
电源模块 5V/12V双输出 1 给各模块供电
杜邦线 公对公/公对母 若干 电路连接
RFID卡/钥匙扣 M1卡 若干 授权卡
面包板 通用型 1 临时电路搭建

三、系统整体逻辑流程

RFID卡靠近
按下指纹采集键
授权通过
未授权
匹配成功
匹配失败
系统上电初始化
初始化各外设

OLED/RFID/指纹/继电器
OLED显示欢迎界面

智能门禁系统
等待验证触发
读取RFID卡号
采集指纹特征值
验证卡号是否授权
验证指纹是否匹配
继电器吸合-电磁锁打开
OLED显示卡未授权
OLED显示指纹错误
OLED显示验证通过 开门成功
延时5秒后
继电器断开-电磁锁关闭
OLED显示门禁已关闭

四、硬件电路连接

4.1 STM32F103与RC522 RFID模块连接

RC522引脚 STM32F103引脚 说明
VCC 3.3V 供电(禁止接5V)
GND GND 接地
SDA PA4 SPI片选
SCK PA5 SPI时钟
MOSI PA7 SPI数据输出
MISO PA6 SPI数据输入
RST PA3 复位引脚

4.2 STM32F103与AS608指纹模块连接

AS608引脚 STM32F103引脚 说明
VCC 3.3V 供电
GND GND 接地
TX PA10 串口2接收(USART2_RX)
RX PA9 串口2发送(USART2_TX)

4.3 STM32F103与OLED模块(I2C)连接

OLED引脚 STM32F103引脚 说明
VCC 3.3V 供电
GND GND 接地
SCL PB8 I2C时钟
SDA PB9 I2C数据

4.4 STM32F103与继电器模块连接

继电器引脚 STM32F103引脚 说明
VCC 5V 供电
GND GND 接地
IN PB0 控制引脚(低电平触发)

五、开发环境搭建

5.1 软件工具

  1. STM32CubeMX(版本6.9.0):用于生成初始化代码
  2. Keil MDK-ARM(版本5.38):代码编译与下载
  3. ST-Link驱动:用于程序下载和调试

5.2 STM32CubeMX配置步骤

步骤1:新建工程
  • 打开STM32CubeMX,点击Access to MCU Selector
  • 搜索STM32F103C8T6,选择对应型号并创建工程
步骤2:配置系统时钟
  • 进入Clock Configuration
  • 选择HSE(外部高速时钟),设置系统时钟为72MHz
  • PLL倍频系数设为9,AHB、APB1、APB2时钟配置为72MHz、36MHz、72MHz
步骤3:配置外设
  1. USART2(指纹模块)
    • 模式:Asynchronous(异步)
    • 波特率:9600
    • 数据位:8
    • 停止位:1
    • 校验位:None
  2. SPI1(RFID模块)
    • 模式:Full-Duplex Master(全双工主机)
    • 时钟极性(CPOL):Low
    • 时钟相位(CPHA):1 Edge
    • 波特率:256kbps
  3. I2C1(OLED模块)
    • 模式:I2C
    • 速度:Fast Mode(400kHz)
  4. GPIO(继电器、RFID复位)
    • PA3:Output(RFID复位),默认高电平
    • PB0:Output(继电器控制),默认高电平(继电器不吸合)
  5. SYS
    • Debug:Serial Wire(SWD调试)
步骤4:生成代码
  • 点击Project Manager,设置工程名(如SmartAccessControl),选择保存路径
  • 工具链/IDE选择MDK-ARM v5
  • 点击GENERATE CODE生成初始化代码

六、代码编写

6.1 代码文件结构

复制代码
SmartAccessControl/
├── Core/
│   ├── Inc/
│   │   ├── main.h
│   │   ├── stm32f1xx_hal_conf.h
│   │   ├── rfid_rc522.h      // RFID驱动头文件
│   │   ├── as608_finger.h    // 指纹驱动头文件
│   │   ├── oled_i2c.h        // OLED驱动头文件
│   │   └── access_control.h  // 门禁逻辑头文件
│   └── Src/
│       ├── main.c
│       ├── stm32f1xx_hal_msp.c
│       ├── stm32f1xx_it.c
│       ├── rfid_rc522.c      // RFID驱动源码
│       ├── as608_finger.c    // 指纹驱动源码
│       ├── oled_i2c.c        // OLED驱动源码
│       └── access_control.c  // 门禁逻辑源码
└── MDK-ARM/
    └── SmartAccessControl.uvprojx  // Keil工程文件

6.2 各模块代码编写

文件名:oled_i2c.h
c 复制代码
#ifndef __OLED_I2C_H
#define __OLED_I2C_H

#include "stm32f1xx_hal.h"

// OLED I2C地址(根据模块调整,0x78或0x7A)
#define OLED_I2C_ADDR    0x78
// I2C句柄(对应CubeMX配置的I2C1)
extern I2C_HandleTypeDef hi2c1;

// 函数声明
void OLED_Init(void);                  // OLED初始化
void OLED_Clear(void);                 // 清屏
void OLED_SetPos(uint8_t x, uint8_t y);// 设置光标位置
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr); // 显示单个字符
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *str); // 显示字符串
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len); // 显示数字

#endif
文件名:oled_i2c.c
c 复制代码
#include "oled_i2c.h"
#include "string.h"
#include "stdio.h"

// OLED写命令
void OLED_WriteCmd(uint8_t cmd)
{
    HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x00, I2C_MEMADD_SIZE_8BIT, &cmd, 1, 100);
}

// OLED写数据
void OLED_WriteData(uint8_t data)
{
    HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
}

// OLED初始化
void OLED_Init(void)
{
    HAL_Delay(100); // 上电延时
    
    OLED_WriteCmd(0xAE); // 关闭显示
    OLED_WriteCmd(0xD5); // 设置显示时钟分频比/振荡器频率
    OLED_WriteCmd(0x80);
    OLED_WriteCmd(0xA8); // 设置多路复用率
    OLED_WriteCmd(0x3F);
    OLED_WriteCmd(0xD3); // 设置显示偏移
    OLED_WriteCmd(0x00);
    OLED_WriteCmd(0x40); // 设置显示开始行
    OLED_WriteCmd(0x8D); // 电荷泵设置
    OLED_WriteCmd(0x14); // 开启电荷泵
    OLED_WriteCmd(0x20); // 设置内存地址模式
    OLED_WriteCmd(0x00); // 水平寻址模式
    OLED_WriteCmd(0xA1); // 设置段重映射
    OLED_WriteCmd(0xC8); // 设置COM输出扫描方向
    OLED_WriteCmd(0xDA); // 设置COM引脚硬件配置
    OLED_WriteCmd(0x12);
    OLED_WriteCmd(0x81); // 设置对比度控制
    OLED_WriteCmd(0xCF);
    OLED_WriteCmd(0xD9); // 设置预充电周期
    OLED_WriteCmd(0xF1);
    OLED_WriteCmd(0xDB); // 设置VCOMH取消选择级别
    OLED_WriteCmd(0x40);
    OLED_WriteCmd(0xA4); // 整个显示开启/关闭
    OLED_WriteCmd(0xA6); // 设置正常/反显
    OLED_WriteCmd(0xAF); // 开启显示
    
    OLED_Clear(); // 清屏
}

// 清屏
void OLED_Clear(void)
{
    uint8_t i, j;
    for(i=0; i<8; i++)
    {
        OLED_WriteCmd(0xB0 + i); // 设置页地址
        OLED_WriteCmd(0x00);     // 设置列低地址
        OLED_WriteCmd(0x10);     // 设置列高地址
        for(j=0; j<128; j++)
        {
            OLED_WriteData(0x00);
        }
    }
}

// 设置光标位置
// x: 列 0-127
// y: 页 0-7(0.96寸OLED分为8页,每页8行)
void OLED_SetPos(uint8_t x, uint8_t y)
{
    OLED_WriteCmd(0xB0 + y); // 设置页
    OLED_WriteCmd((x & 0x0F)); // 列低4位
    OLED_WriteCmd(((x >> 4) & 0x0F) | 0x10); // 列高4位
}

// 显示单个字符
// x: 列 0-127
// y: 页 0-7
// chr: 要显示的字符
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr)
{
    uint8_t c = 0, i = 0;
    c = chr - ' '; // 偏移量,对应字库起始位置
    if(x > 127) // 列越界,切换到下一页
    {
        x = 0;
        y += 2;
    }
    OLED_SetPos(x, y);
    for(i=0; i<8; i++) // 显示字符上半部分
    {
        OLED_WriteData(F8x16[c*16 + i]);
    }
    OLED_SetPos(x, y+1);
    for(i=0; i<8; i++) // 显示字符下半部分
    {
        OLED_WriteData(F8x16[c*16 + i + 8]);
    }
}

// 显示字符串
// x: 列 0-127
// y: 页 0-7
// str: 字符串指针
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *str)
{
    uint8_t i = 0;
    while(str[i] != '\0')
    {
        OLED_ShowChar(x + 8*i, y, str[i]);
        i++;
    }
}

// 显示数字
// x: 列 0-127
// y: 页 0-7
// num: 要显示的数字
// len: 数字长度
void OLED_ShowNum(uint8_t x, uint8_t y, uint32_t num, uint8_t len)
{
    uint8_t i, temp;
    uint8_t enshow = 0;
    for(i=0; i<len; i++)
    {
        temp = (num / (uint32_t)pow(10, len-i-1)) % 10;
        if(enshow == 0 && i < len-1)
        {
            if(temp == 0)
            {
                OLED_ShowChar(x + 8*i, y, ' ');
                continue;
            }
            else
            {
                enshow = 1;
            }
        }
        OLED_ShowChar(x + 8*i, y, temp + '0');
    }
}

// 8x16 ASCII字库(部分)
const unsigned char F8x16[] = {
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 空格
    0x00,0x00,0x7C,0x12,0x11,0x12,0x7C,0x00,0x00,0x00,0x7C,0x12,0x11,0x12,0x7C,0x00,// 0
    0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x00,0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x00,// 1
    // 其余ASCII字库可自行补充,或下载完整字库文件
};
文件名:rfid_rc522.h
c 复制代码
#ifndef __RFID_RC522_H
#define __RFID_RC522_H

#include "stm32f1xx_hal.h"

// RC522引脚定义(对应CubeMX配置)
#define RC522_CS_PIN    GPIO_PIN_4
#define RC522_CS_PORT   GPIOA
#define RC522_RST_PIN   GPIO_PIN_3
#define RC522_RST_PORT  GPIOA

// SPI句柄(对应CubeMX配置的SPI1)
extern SPI_HandleTypeDef hspi1;

// RC522命令字
#define PCD_IDLE              0x00
#define PCD_AUTHENT           0x0E
#define PCD_RECEIVE           0x08
#define PCD_TRANSMIT          0x04
#define PCD_TRANSCEIVE        0x0C
#define PCD_RESETPHASE        0x0F
#define PCD_CALCCRC           0x03

// MIFARE卡命令字
#define PICC_REQIDL           0x26
#define PICC_REQALL           0x52
#define PICC_ANTICOLL         0x93
#define PICC_SElECTTAG        0x93
#define PICC_AUTHENT1A        0x60
#define PICC_AUTHENT1B        0x61
#define PICC_READ             0x30
#define PICC_WRITE            0xA0
#define PICC_DECREMENT        0xC0
#define PICC_INCREMENT        0xC1
#define PICC_RESTORE          0xC2
#define PICC_TRANSFER         0xB0
#define PICC_HALT             0x50

// RC522寄存器地址
#define Reserved00            0x00
#define CommandReg            0x01
#define ComIEnReg             0x02
#define DivlEnReg             0x03
#define ComIrqReg             0x04
#define DivIrqReg             0x05
#define ErrorReg              0x06
#define Status1Reg            0x07
#define Status2Reg            0x08
#define FIFODataReg           0x09
#define FIFOLevelReg          0x0A
#define WaterLevelReg         0x0B
#define ControlReg            0x0C
#define BitFramingReg         0x0D
#define CollReg               0x0E
#define Reserved01            0x0F

#define Reserved10            0x10
#define ModeReg               0x11
#define TxModeReg             0x12
#define RxModeReg             0x13
#define TxControlReg          0x14
#define TxAutoReg             0x15
#define TxSelReg              0x16
#define RxSelReg              0x17
#define RxThresholdReg        0x18
#define DemodReg              0x19
#define Reserved11            0x1A
#define Reserved12            0x1B
#define MifareReg             0x1C
#define Reserved13            0x1D
#define Reserved14            0x1E
#define SerialSpeedReg        0x1F

#define Reserved20            0x20
#define CRCResultRegM         0x21
#define CRCResultRegL         0x22
#define Reserved21            0x23
#define ModWidthReg           0x24
#define Reserved22            0x25
#define RFCfgReg              0x26
#define GsNReg                0x27
#define CWGsPReg              0x28
#define ModGsPReg             0x29
#define TModeReg              0x2A
#define TPrescalerReg         0x2B
#define TReloadRegH           0x2C
#define TReloadRegL           0x2D
#define TCounterValueRegH     0x2E
#define TCounterValueRegL     0x2F

#define Reserved30            0x30
#define TestSel1Reg           0x31
#define TestSel2Reg           0x32
#define TestPinEnReg          0x33
#define TestPinValueReg       0x34
#define TestBusReg            0x35
#define AutoTestReg           0x36
#define VersionReg            0x37
#define AnalogTestReg         0x38
#define TestDAC1Reg           0x39
#define TestDAC2Reg           0x3A
#define TestADCReg            0x3B
#define Reserved31            0x3C
#define Reserved32            0x3D
#define Reserved33            0x3E
#define Reserved34            0x3F

// 函数声明
void RC522_Init(void);                          // RC522初始化
uint8_t RC522_ReadCard(uint8_t *cardID);        // 读取卡号
uint8_t RC522_CompareCard(uint8_t *cardID, uint8_t *authCardID); // 卡号对比

#endif
文件名:rfid_rc522.c
c 复制代码
#include "rfid_rc522.h"
#include "stm32f1xx_hal_spi.h"

// SPI读写一个字节
static uint8_t SPI_ReadWriteByte(uint8_t txData)
{
    uint8_t rxData;
    HAL_SPI_TransmitReceive(&hspi1, &txData, &rxData, 1, 100);
    return rxData;
}

// 写RC522寄存器
static void RC522_WriteReg(uint8_t addr, uint8_t data)
{
    HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_RESET); // 片选拉低
    SPI_ReadWriteByte((addr << 1) & 0x7E); // 写地址(最高位0为写)
    SPI_ReadWriteByte(data);               // 写数据
    HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_SET);   // 片选拉高
}

// 读RC522寄存器
static uint8_t RC522_ReadReg(uint8_t addr)
{
    uint8_t data;
    HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_RESET); // 片选拉低
    SPI_ReadWriteByte(((addr << 1) & 0x7E) | 0x80); // 读地址(最高位1为读)
    data = SPI_ReadWriteByte(0x00);                 // 读数据
    HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_SET);   // 片选拉高
    return data;
}

// 设置RC522寄存器位
static void RC522_SetBitMask(uint8_t reg, uint8_t mask)
{
    uint8_t tmp;
    tmp = RC522_ReadReg(reg);
    RC522_WriteReg(reg, tmp | mask);
}

// 清除RC522寄存器位
static void RC522_ClearBitMask(uint8_t reg, uint8_t mask)
{
    uint8_t tmp;
    tmp = RC522_ReadReg(reg);
    RC522_WriteReg(reg, tmp & (~mask));
}

// 复位RC522
static void RC522_Reset(void)
{
    RC522_WriteReg(CommandReg, PCD_RESETPHASE);
}

// 初始化RC522
void RC522_Init(void)
{
    // 配置CS和RST引脚为输出
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    GPIO_InitStruct.Pin = RC522_CS_PIN | RC522_RST_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(RC522_CS_PORT, &GPIO_InitStruct);
    
    // 复位RC522
    HAL_GPIO_WritePin(RC522_RST_PORT, RC522_RST_PIN, GPIO_PIN_RESET);
    HAL_Delay(10);
    HAL_GPIO_WritePin(RC522_RST_PORT, RC522_RST_PIN, GPIO_PIN_SET);
    HAL_Delay(10);
    
    RC522_Reset();
    
    // 配置RC522
    RC522_WriteReg(TModeReg, 0x8D);        // 定时器配置
    RC522_WriteReg(TPrescalerReg, 0x3E);   // 定时器预分频
    RC522_WriteReg(TReloadRegL, 30);       // 定时器重装值低字节
    RC522_WriteReg(TReloadRegH, 0);        // 定时器重装值高字节
    RC522_WriteReg(TxAutoReg, 0x40);       // 自动发射
    RC522_WriteReg(ModeReg, 0x3D);         // 通信模式
    RC522_SetBitMask(TxControlReg, 0x03);  // 打开发射器
    RC522_WriteReg(RFCfgReg, 0x70);        // RF配置,增益最大
    RC522_ClearBitMask(Status2Reg, 0x08);  // 清除MFCrypto1On位
    RC522_WriteReg(SerialSpeedReg, 0x00);  // 串口速度
    
    HAL_Delay(10);
}

// 寻卡
static uint8_t RC522_Request(uint8_t reqMode, uint8_t *tagType)
{
    uint8_t status;
    uint16_t unLen;
    
    RC522_WriteReg(BitFramingReg, 0x07); // 发送最后一个字节的所有位
    
    tagType[0] = reqMode;
    status = RC522_ToCard(PCD_TRANSCEIVE, tagType, 1, tagType, &unLen);
    
    if((status != MI_OK) || (unLen != 0x02))
    {
        status = MI_ERR;
    }
    
    return status;
}

// 防冲撞
static uint8_t RC522_Anticoll(uint8_t *serNum)
{
    uint8_t status;
    uint8_t i;
    uint8_t serNumCheck = 0;
    uint16_t unLen;
    
    RC522_WriteReg(BitFramingReg, 0x00); // 发送最后一个字节的所有位
    
    serNum[0] = PICC_ANTICOLL;
    serNum[1] = 0x20;
    
    status = RC522_ToCard(PCD_TRANSCEIVE, serNum, 2, serNum, &unLen);
    
    if(status == MI_OK)
    {
        // 校验卡号
        for(i=0; i<4; i++)
        {
            serNumCheck ^= serNum[i];
        }
        if(serNumCheck != serNum[4])
        {
            status = MI_ERR;
        }
    }
    
    return status;
}

// 与卡片通信
static uint8_t RC522_ToCard(uint8_t command, uint8_t *sendData, uint8_t sendLen, uint8_t *backData, uint16_t *backLen)
{
    uint8_t status = MI_ERR;
    uint8_t irqEn = 0x00;
    uint8_t waitIRq = 0x00;
    uint8_t lastBits;
    uint8_t n;
    uint16_t i;
    
    switch(command)
    {
        case PCD_AUTHENT:
            irqEn = 0x12;
            waitIRq = 0x10;
            break;
        case PCD_TRANSCEIVE:
            irqEn = 0x77;
            waitIRq = 0x30;
            break;
        default:
            break;
    }
    
    RC522_WriteReg(ComIEnReg, irqEn | 0x80); // 使能中断
    RC522_ClearBitMask(ComIrqReg, 0x80);     // 清除所有中断标志
    RC522_SetBitMask(FIFOLevelReg, 0x80);    // 清空FIFO缓冲区
    
    RC522_WriteReg(CommandReg, PCD_IDLE);    // 取消当前命令
    
    // 写数据到FIFO
    for(i=0; i<sendLen; i++)
    {
        RC522_WriteReg(FIFODataReg, sendData[i]);
    }
    
    // 执行命令
    RC522_WriteReg(CommandReg, command);
    if(command == PCD_TRANSCEIVE)
    {
        RC522_SetBitMask(BitFramingReg, 0x80); // 开始发送
    }
    
    // 等待接收数据完成
    i = 2000; // 超时时间
    do
    {
        n = RC522_ReadReg(ComIrqReg);
        i--;
    }
    while((i!=0) && !(n&0x01) && !(n&waitIRq));
    
    RC522_ClearBitMask(BitFramingReg, 0x80); // 停止发送
    
    if(i != 0)
    {
        if(!(RC522_ReadReg(ErrorReg) & 0x1B)) // 无错误
        {
            status = MI_OK;
            if(n & irqEn & 0x01)
            {
                status = MI_NOTAGERR; // 无卡
            }
            
            if(command == PCD_TRANSCEIVE)
            {
                n = RC522_ReadReg(FIFOLevelReg);
                lastBits = RC522_ReadReg(ControlReg) & 0x07;
                if(lastBits)
                {
                    *backLen = (n-1)*8 + lastBits;
                }
                else
                {
                    *backLen = n*8;
                }
                
                if(n == 0)
                {
                    n = 1;
                }
                if(n > 16)
                {
                    n = 16;
                }
                
                // 读取FIFO数据
                for(i=0; i<n; i++)
                {
                    backData[i] = RC522_ReadReg(FIFODataReg);
                }
            }
        }
        else
        {
            status = MI_ERR;
        }
    }
    
    return status;
}

// 读取卡号
uint8_t RC522_ReadCard(uint8_t *cardID)
{
    uint8_t status;
    uint8_t unLen;
    uint8_t ucArray_ID[5];
    
    // 寻卡
    status = RC522_Request(PICC_REQIDL, ucArray_ID);
    if(status != MI_OK)
    {
        return 0;
    }
    
    // 防冲撞
    status = RC522_Anticoll(ucArray_ID);
    if(status != MI_OK)
    {
        return 0;
    }
    
    // 复制卡号到输出缓冲区
    for(uint8_t i=0; i<4; i++)
    {
        cardID[i] = ucArray_ID[i];
    }
    
    return 1;
}

// 卡号对比
uint8_t RC522_CompareCard(uint8_t *cardID, uint8_t *authCardID)
{
    for(uint8_t i=0; i<4; i++)
    {
        if(cardID[i] != authCardID[i])
        {
            return 0; // 不匹配
        }
    }
    return 1; // 匹配
}

// 定义状态常量(补充)
#define MI_OK      0
#define MI_NOTAGERR 1
#define MI_ERR     2
文件名:as608_finger.h
c 复制代码
#ifndef __AS608_FINGER_H
#define __AS608_FINGER_H

#include "stm32f1xx_hal.h"

// 串口句柄(对应CubeMX配置的USART2)
extern UART_HandleTypeDef huart2;

// AS608指令码
#define CMD_VERIFY        0x01 // 验证指纹
#define CMD_IDENTIFY      0x02 // 识别指纹
#define CMD_REGISTER      0x04 // 录入指纹
#define CMD_DELETE        0x0C // 删除指纹
#define CMD_EMPTY         0x0D // 清空指纹库
#define CMD_READSYSPARA   0x0F // 读取系统参数
#define CMD_SETPARA       0x10 // 设置系统参数

// 函数声明
uint8_t Finger_Init(void);                          // 指纹模块初始化
uint8_t Finger_Collect(void);                       // 采集指纹
uint8_t Finger_Verify(uint16_t ID);                 // 验证指定ID指纹
uint8_t Finger_Identify(uint16_t *ID);              // 识别指纹(返回匹配ID)
uint8_t Finger_Register(uint16_t ID);               // 录入指纹到指定ID
uint8_t Finger_Delete(uint16_t ID);                 // 删除指定ID指纹
uint8_t Finger_Empty(void);                         // 清空指纹库

#endif
文件名:as608_finger.c
c 复制代码
#include "as608_finger.h"
#include "string.h"

// 数据包格式:包头(0xEF01) + 地址(0xFFFFFFFF) + 包类型 + 长度 + 指令 + 参数 + 校验和 + 包尾
#define PACKET_HEAD1    0xEF
#define PACKET_HEAD2    0x01
#define DEVICE_ADDR1    0xFF
#define DEVICE_ADDR2    0xFF
#define DEVICE_ADDR3    0xFF
#define DEVICE_ADDR4    0xFF

// 串口接收缓冲区
static uint8_t uart_rx_buf[32];
static uint16_t uart_rx_len = 0;

// 串口接收中断回调函数(需在stm32f1xx_it.c中调用)
void Finger_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART2)
    {
        uart_rx_buf[uart_rx_len++] = huart->Instance->DR;
        // 重新开启接收中断
        HAL_UART_Receive_IT(&huart2, &uart_rx_buf[uart_rx_len], 1);
    }
}

// 发送指令包
static uint8_t Finger_SendCmd(uint8_t cmd, uint8_t *param, uint8_t param_len, uint8_t *resp, uint16_t *resp_len)
{
    uint8_t tx_buf[32];
    uint16_t len = 6 + param_len; // 包类型(1) + 长度(2) + 指令(1) + 参数(n)
    uint8_t checksum = 0;
    uint16_t i;
    
    // 清空接收缓冲区
    memset(uart_rx_buf, 0, sizeof(uart_rx_buf));
    uart_rx_len = 0;
    
    // 组装数据包
    tx_buf[0] = PACKET_HEAD1;
    tx_buf[1] = PACKET_HEAD2;
    tx_buf[2] = DEVICE_ADDR1;
    tx_buf[3] = DEVICE_ADDR2;
    tx_buf[4] = DEVICE_ADDR3;
    tx_buf[5] = DEVICE_ADDR4;
    tx_buf[6] = 0x01; // 包类型:指令包
    tx_buf[7] = (len >> 8) & 0xFF; // 长度高字节
    tx_buf[8] = len & 0xFF;        // 长度低字节
    tx_buf[9] = cmd;               // 指令码
    
    // 复制参数
    for(i=0; i<param_len; i++)
    {
        tx_buf[10 + i] = param[i];
    }
    
    // 计算校验和
    for(i=6; i<10+param_len; i++)
    {
        checksum += tx_buf[i];
    }
    tx_buf[10 + param_len] = checksum;
    
    // 发送数据包
    HAL_UART_Transmit(&huart2, tx_buf, 11 + param_len, 1000);
    
    // 等待响应(超时500ms)
    HAL_Delay(500);
    
    // 解析响应包
    if(uart_rx_len < 12) // 最小响应包长度
    {
        return 0;
    }
    
    // 校验包头和地址
    if((uart_rx_buf[0] != PACKET_HEAD1) || (uart_rx_buf[1] != PACKET_HEAD2) ||
       (uart_rx_buf[2] != DEVICE_ADDR1) || (uart_rx_buf[3] != DEVICE_ADDR2) ||
       (uart_rx_buf[4] != DEVICE_ADDR3) || (uart_rx_buf[5] != DEVICE_ADDR4))
    {
        return 0;
    }
    
    // 校验校验和
    checksum = 0;
    len = (uart_rx_buf[7] << 8) | uart_rx_buf[8]; // 响应长度
    for(i=6; i<6+len; i++)
    {
        checksum += uart_rx_buf[i];
    }
    if(checksum != uart_rx_buf[6+len])
    {
        return 0;
    }
    
    // 复制响应数据
    *resp_len = len - 2; // 减去指令码和状态码长度
    memcpy(resp, &uart_rx_buf[10], *resp_len);
    
    // 检查状态码
    if(uart_rx_buf[10] != 0x00) // 状态码0x00为成功
    {
        return 0;
    }
    
    return 1;
}

// 指纹模块初始化
uint8_t Finger_Init(void)
{
    uint8_t resp[16];
    uint16_t resp_len;
    
    // 开启串口接收中断
    HAL_UART_Receive_IT(&huart2, &uart_rx_buf[0], 1);
    
    // 读取系统参数,验证模块通信
    if(Finger_SendCmd(CMD_READSYSPARA, NULL, 0, resp, &resp_len))
    {
        return 1; // 初始化成功
    }
    return 0; // 初始化失败
}

// 采集指纹
uint8_t Finger_Collect(void)
{
    uint8_t param[2] = {0x01, 0x00}; // 参数:采集次数=1,备用=0
    uint8_t resp[16];
    uint16_t resp_len;
    
    if(Finger_SendCmd(0x03, param, 2, resp, &resp_len)) // 0x03为采集指纹指令
    {
        return 1; // 采集成功
    }
    return 0; // 采集失败
}

// 验证指定ID指纹
uint8_t Finger_Verify(uint16_t ID)
{
    uint8_t param[2] = {ID >> 8, ID & 0xFF}; // 参数:指纹ID
    uint8_t resp[16];
    uint16_t resp_len;
    
    if(Finger_SendCmd(CMD_VERIFY, param, 2, resp, &resp_len))
    {
        return 1; // 验证成功
    }
    return 0; // 验证失败
}

// 识别指纹(返回匹配ID)
uint8_t Finger_Identify(uint16_t *ID)
{
    uint8_t resp[16];
    uint16_t resp_len;
    
    if(Finger_SendCmd(CMD_IDENTIFY, NULL, 0, resp, &resp_len))
    {
        *ID = (resp[1] << 8) | resp[2]; // 提取匹配的指纹ID
        return 1; // 识别成功
    }
    return 0; // 识别失败
}

// 录入指纹到指定ID
uint8_t Finger_Register(uint16_t ID)
{
    uint8_t param[2] = {ID >> 8, ID & 0xFF}; // 参数:指纹ID
    uint8_t resp[16];
    uint16_t resp_len;
    
    // 第一次采集
    if(!Finger_Collect())
    {
        return 0;
    }
    
    // 第二次采集
    if(!Finger_Collect())
    {
        return 0;
    }
    
    // 合成模板并保存
    if(Finger_SendCmd(CMD_REGISTER, param, 2, resp, &resp_len))
    {
        return 1; // 录入成功
    }
    return 0; // 录入失败
}

// 删除指定ID指纹
uint8_t Finger_Delete(uint16_t ID)
{
    uint8_t param[4] = {ID >> 8, ID & 0xFF, 0x00, 0x01}; // 参数:起始ID,删除数量=1
    uint8_t resp[16];
    uint16_t resp_len;
    
    if(Finger_SendCmd(CMD_DELETE, param, 4, resp, &resp_len))
    {
        return 1; // 删除成功
    }
    return 0; // 删除失败
}

// 清空指纹库
uint8_t Finger_Empty(void)
{
    uint8_t resp[16];
    uint16_t resp_len;
    
    if(Finger_SendCmd(CMD_EMPTY, NULL, 0, resp, &resp_len))
    {
        return 1; // 清空成功
    }
    return 0; // 清空失败
}
文件名:access_control.h
c 复制代码
#ifndef __ACCESS_CONTROL_H
#define __ACCESS_CONTROL_H

#include "stm32f1xx_hal.h"

// 授权卡号(示例,需替换为实际卡号)
#define AUTH_CARD_ID {0x12, 0x34, 0x56, 0x78}
// 继电器控制引脚
#define RELAY_PIN    GPIO_PIN_0
#define RELAY_PORT   GPIOB

// 函数声明
void AccessControl_Init(void);                  // 门禁系统初始化
void AccessControl_Run(void);                   // 门禁系统主逻辑
void AccessControl_OpenLock(void);              // 开门
void AccessControl_CloseLock(void);             // 关门
uint8_t AccessControl_CheckRFID(void);          // 检查RFID卡
uint8_t AccessControl_CheckFinger(void);        // 检查指纹

#endif
文件名:access_control.c
c 复制代码
#include "access_control.h"
#include "oled_i2c.h"
#include "rfid_rc522.h"
#include "as608_finger.h"
#include "stdio.h"

// 授权卡号
static uint8_t auth_card_id[4] = AUTH_CARD_ID;

// 门禁系统初始化
void AccessControl_Init(void)
{
    // 初始化OLED
    OLED_Init();
    OLED_Clear();
    OLED_ShowString(0, 0, (uint8_t*)"Smart Access Control");
    OLED_ShowString(0, 2, (uint8_t*)"Init System...");
    
    // 初始化RC522
    RC522_Init();
    OLED_ShowString(0, 3, (uint8_t*)"RFID Init OK");
    
    // 初始化指纹模块
    if(Finger_Init())
    {
        OLED_ShowString(0, 4, (uint8_t*)"Finger Init OK");
    }
    else
    {
        OLED_ShowString(0, 4, (uint8_t*)"Finger Init ERR");
    }
    
    // 初始化继电器引脚
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    GPIO_InitStruct.Pin = RELAY_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(RELAY_PORT, &GPIO_InitStruct);
    
    // 默认关闭继电器
    AccessControl_CloseLock();
    OLED_ShowString(0, 5, (uint8_t*)"Relay Init OK");
    
    // 初始化完成
    HAL_Delay(2000);
    OLED_Clear();
    OLED_ShowString(0, 0, (uint8_t*)"欢迎使用智能门禁");
    OLED_ShowString(0, 2, (uint8_t*)"请刷RFID卡或验证指纹");
}

// 开门
void AccessControl_OpenLock(void)
{
    HAL_GPIO_WritePin(RELAY_PORT, RELAY_PIN, GPIO_PIN_RESET); // 低电平触发继电器吸合
    OLED_Clear();
    OLED_ShowString(0, 0, (uint8_t*)"Verify Success!");
    OLED_ShowString(0, 2, (uint8_t*)"Door Open!");
}

// 关门
void AccessControl_CloseLock(void)
{
    HAL_GPIO_WritePin(RELAY_PORT, RELAY_PIN, GPIO_PIN_SET); // 高电平关闭继电器
    OLED_Clear();
    OLED_ShowString(0, 0, (uint8_t*)"Door Closed");
    OLED_ShowString(0, 2, (uint8_t*)"Waiting...");
}

// 检查RFID卡
uint8_t AccessControl_CheckRFID(void)
{
    uint8_t card_id[4];
    uint8_t card_str[20];
    
    // 读取卡号
    if(RC522_ReadCard(card_id))
    {
        // 显示卡号
        sprintf((char*)card_str, "Card ID: %02X%02X%02X%02X", card_id[0], card_id[1], card_id[2], card_id[3]);
        OLED_ShowString(0, 4, card_str);
        
        // 验证卡号
        if(RC522_CompareCard(card_id, auth_card_id))
        {
            return 1; // 验证通过
        }
        else
        {
            OLED_Clear();
            OLED_ShowString(0, 0, (uint8_t*)"卡未授权!");
            HAL_Delay(2000);
            OLED_Clear();
            OLED_ShowString(0, 0, (uint8_t*)"欢迎使用智能门禁");
            OLED_ShowString(0, 2, (uint8_t*)"请刷RFID卡或验证指纹");
            return 0; // 验证失败
        }
    }
    
    return 0; // 无卡
}

// 检查指纹
uint8_t AccessControl_CheckFinger(void)
{
    uint16_t finger_id;
    
    OLED_Clear();
    OLED_ShowString(0, 0, (uint8_t*)"请按压指纹...");
    
    // 采集并识别指纹
    if(Finger_Collect() && Finger_Identify(&finger_id))
    {
        OLED_ShowString(0, 2, (uint8_t*)"指纹匹配成功!");
        HAL_Delay(1000);
        return 1; // 验证通过
    }
    else
    {
        OLED_ShowString(0, 2, (uint8_t*)"指纹匹配失败!");
        HAL_Delay(2000);
        OLED_Clear();
        OLED_ShowString(0, 0, (uint8_t*)"欢迎使用智能门禁");
        OLED_ShowString(0, 2, (uint8_t*)"请刷RFID卡或验证指纹");
        return 0; // 验证失败
    }
}

// 门禁系统主逻辑
void AccessControl_Run(void)
{
    uint8_t rfid_ok = 0;
    uint8_t finger_ok = 0;
    
    // 检查RFID卡
    rfid_ok = AccessControl_CheckRFID();
    
    // 检查指纹(可通过按键触发,此处简化为轮询)
    // 实际应用中建议通过外部按键触发指纹采集
    finger_ok = AccessControl_CheckFinger();
    
    // 验证通过,开门
    if(rfid_ok || finger_ok)
    {
        AccessControl_OpenLock();
        HAL_Delay(5000); // 开门保持5秒
        AccessControl_CloseLock();
    }
    
    HAL_Delay(100); // 降低轮询频率
}
文件名:main.c
c 复制代码
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "access_control.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

// 串口接收中断回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    Finger_UART_RxCpltCallback(huart);
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_SPI1_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  
  // 初始化门禁系统
  AccessControl_Init();

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    // 运行门禁系统主逻辑
    AccessControl_Run();
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  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_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
    // 错误处理:闪烁LED(如果有)
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

七、程序下载与调试

7.1 编译代码

  1. 打开Keil MDK工程文件SmartAccessControl.uvprojx
  2. 点击BuildRebuild编译代码,确保无错误
  3. 编译成功后,生成.hex文件

7.2 下载程序

  1. 连接ST-Link到STM32F103C8T6最小系统板
  2. 打开Keil MDK的Debug配置,选择ST-Link调试器
  3. 点击Download下载程序到STM32

7.3 调试步骤

  1. 上电后,OLED显示初始化信息,检查各模块是否初始化成功
  2. 刷授权的RFID卡,观察OLED是否显示"验证通过 开门成功",继电器是否吸合
  3. 验证已录入的指纹,观察门禁是否正常开门
  4. 刷未授权的卡或验证错误指纹,观察OLED是否显示错误提示,继电器是否保持断开

八、常见问题解决

  1. RFID模块无法读卡

    • 检查RC522供电是否为3.3V(禁止接5V)
    • 检查SPI引脚连接是否正确
    • 确认RC522的CS、RST引脚配置正确
  2. 指纹模块通信失败

    • 检查串口引脚连接(TX/RX交叉连接)
    • 确认串口波特率为9600
    • 检查指纹模块供电是否稳定
  3. 继电器不吸合

    • 检查继电器控制引脚电平(低电平触发)
    • 确认继电器模块供电为5V
    • 检查电磁锁与继电器的连接
  4. OLED显示乱码

    • 检查I2C引脚连接
    • 确认OLED I2C地址是否正确(0x78或0x7A)
    • 检查字库是否完整

九、功能扩展建议

  1. 添加密码验证功能,通过按键输入密码
  2. 增加WiFi模块,实现远程开门和状态监控
  3. 添加摄像头,实现人脸识别功能
  4. 增加刷卡/指纹记录,通过串口或SD卡存储
  5. 添加报警功能,连续验证失败触发蜂鸣器报警

总结

  1. 本智能门禁系统基于STM32F103C8T6,整合RFID(RC522)和指纹(AS608)双验证方式,通过继电器控制电磁锁实现开门,OLED屏提供状态显示,逻辑完整且可落地。
  2. 代码层面分模块编写(OLED、RFID、指纹、门禁逻辑),结构清晰,包含详细注释,零基础小白可按步骤模仿实现,关键外设初始化和通信逻辑均提供完整代码。
  3. 硬件连接需严格遵循引脚定义,重点注意RC522的3.3V供电、指纹模块的串口交叉连接、继电器的低电平触发特性,调试时可按模块逐一验证功能。
相关推荐
可乐鸡翅好好吃3 小时前
NRF芯片下的ADC采集
单片机·嵌入式硬件
3壹3 小时前
LED模块控制与左移运算详解
单片机·嵌入式硬件
senijusene3 小时前
通信概念,51UART的使用,以及MODBUS的简单应用
c语言·开发语言·单片机·51单片机
Zevalin爱灰灰4 小时前
零基础入门学用物联网(ESP8266) 第一部分 基础知识篇(五)
单片机·物联网·嵌入式·esp8266
全栈游侠4 小时前
05-FreeRTOS的移植与适配
stm32
学嵌入式的小杨同学5 小时前
STM32 进阶封神之路(二十二):DMA 实战全攻略 ——ADC 采集 + 串口收发 + 内存复制(库函数 + 代码落地)
c++·stm32·单片机·嵌入式硬件·mcu·硬件架构·pcb
是翔仔呐5 小时前
C语言从黑框框到控硬件!51单片机零基础保姆式全系列教程 开篇前言+全书总览
c语言·开发语言·单片机·嵌入式硬件·gitee·51单片机
-Springer-5 小时前
STM32 学习 —— 个人学习笔记10-1(I2C 通信协议及 MPU6050 简介 & 软件 I2C 读写 MPU6050)
笔记·stm32·学习
流浪_彩虹5 小时前
MCU/DSP 与 ROS2 如何通信?XRCE-DDS 实现 rostopic 与嵌入式 Msg 无缝映射(附架构图)
单片机·嵌入式硬件