文章目录
-
- 一、项目概述
- 二、硬件清单
- 三、系统整体逻辑流程
- 四、硬件电路连接
-
- [4.1 STM32F103与RC522 RFID模块连接](#4.1 STM32F103与RC522 RFID模块连接)
- [4.2 STM32F103与AS608指纹模块连接](#4.2 STM32F103与AS608指纹模块连接)
- [4.3 STM32F103与OLED模块(I2C)连接](#4.3 STM32F103与OLED模块(I2C)连接)
- [4.4 STM32F103与继电器模块连接](#4.4 STM32F103与继电器模块连接)
- 五、开发环境搭建
-
- [5.1 软件工具](#5.1 软件工具)
- [5.2 STM32CubeMX配置步骤](#5.2 STM32CubeMX配置步骤)
- 六、代码编写
-
- [6.1 代码文件结构](#6.1 代码文件结构)
- [6.2 各模块代码编写](#6.2 各模块代码编写)
- 七、程序下载与调试
-
- [7.1 编译代码](#7.1 编译代码)
- [7.2 下载程序](#7.2 下载程序)
- [7.3 调试步骤](#7.3 调试步骤)
- 八、常见问题解决
- 九、功能扩展建议
一、项目概述
本项目基于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 软件工具
- STM32CubeMX(版本6.9.0):用于生成初始化代码
- Keil MDK-ARM(版本5.38):代码编译与下载
- 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:配置外设
- USART2(指纹模块)
- 模式:Asynchronous(异步)
- 波特率:9600
- 数据位:8
- 停止位:1
- 校验位:None
- SPI1(RFID模块)
- 模式:Full-Duplex Master(全双工主机)
- 时钟极性(CPOL):Low
- 时钟相位(CPHA):1 Edge
- 波特率:256kbps
- I2C1(OLED模块)
- 模式:I2C
- 速度:Fast Mode(400kHz)
- GPIO(继电器、RFID复位)
- PA3:Output(RFID复位),默认高电平
- PB0:Output(继电器控制),默认高电平(继电器不吸合)
- 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 编译代码
- 打开Keil MDK工程文件
SmartAccessControl.uvprojx - 点击
Build或Rebuild编译代码,确保无错误 - 编译成功后,生成
.hex文件
7.2 下载程序
- 连接ST-Link到STM32F103C8T6最小系统板
- 打开Keil MDK的
Debug配置,选择ST-Link调试器 - 点击
Download下载程序到STM32
7.3 调试步骤
- 上电后,OLED显示初始化信息,检查各模块是否初始化成功
- 刷授权的RFID卡,观察OLED是否显示"验证通过 开门成功",继电器是否吸合
- 验证已录入的指纹,观察门禁是否正常开门
- 刷未授权的卡或验证错误指纹,观察OLED是否显示错误提示,继电器是否保持断开
八、常见问题解决
-
RFID模块无法读卡
- 检查RC522供电是否为3.3V(禁止接5V)
- 检查SPI引脚连接是否正确
- 确认RC522的CS、RST引脚配置正确
-
指纹模块通信失败
- 检查串口引脚连接(TX/RX交叉连接)
- 确认串口波特率为9600
- 检查指纹模块供电是否稳定
-
继电器不吸合
- 检查继电器控制引脚电平(低电平触发)
- 确认继电器模块供电为5V
- 检查电磁锁与继电器的连接
-
OLED显示乱码
- 检查I2C引脚连接
- 确认OLED I2C地址是否正确(0x78或0x7A)
- 检查字库是否完整
九、功能扩展建议
- 添加密码验证功能,通过按键输入密码
- 增加WiFi模块,实现远程开门和状态监控
- 添加摄像头,实现人脸识别功能
- 增加刷卡/指纹记录,通过串口或SD卡存储
- 添加报警功能,连续验证失败触发蜂鸣器报警
总结
- 本智能门禁系统基于STM32F103C8T6,整合RFID(RC522)和指纹(AS608)双验证方式,通过继电器控制电磁锁实现开门,OLED屏提供状态显示,逻辑完整且可落地。
- 代码层面分模块编写(OLED、RFID、指纹、门禁逻辑),结构清晰,包含详细注释,零基础小白可按步骤模仿实现,关键外设初始化和通信逻辑均提供完整代码。
- 硬件连接需严格遵循引脚定义,重点注意RC522的3.3V供电、指纹模块的串口交叉连接、继电器的低电平触发特性,调试时可按模块逐一验证功能。