文章目录
-
- 一、项目概述
-
- 核心功能
- 硬件清单
- 开发环境
- 二、硬件接线
- 三、系统流程图
- 四、工程创建与基础配置
-
- 1. 新建STM32工程
- 2. 基础时钟配置
- 五、模块驱动代码
-
- 1. 延时函数:delay.c / delay.h
-
- delay.c
- delay.h
- 2. OLED驱动:oled.c / oled.h
-
- oled.c
- oled.h
- 3. RC522 RFID驱动:rc522.c / rc522.h
-
- rc522.c
- rc522.h
- 4. AS608指纹驱动:finger.c / finger.h
-
- finger.c
- finger.h
- 5. 继电器驱动:relay.c / relay.h
-
- relay.c
- relay.h
- 六、主函数代码:main.c
- 七、零基础操作步骤
-
- 步骤1:硬件接线
- 步骤2:新建工程文件
- 步骤3:复制代码
- 步骤4:修改合法卡号
- 步骤5:编译下载
- 步骤6:功能测试
- 八、常见问题解决
- 九、项目扩展方向
一、项目概述
本项目基于STM32F103C8T6核心板,结合RC522 RFID模块、AS608指纹模块、0.96寸I2C OLED显示屏,实现一套完整的智能门禁系统。
核心功能
硬件清单
开发环境
Keil5 MDK + STM32CubeMX(基础配置)+ 标准库/寄存器代码
二、硬件接线
所有接线严格按照下表连接,VCC供电必须匹配,避免烧毁模块!
| RC522(SDA) | PA4 | SPI片选 |
| RC522(SCK) | PA5 | SPI时钟 |
| RC522(MOSI) | PA7 | SPI主机输出 |
| RC522(MISO) | PA6 | SPI主机输入 |
| RC522(IRQ) | 不接 | 无需使用 |
| RC522(GND) | GND | 共地 |
| RC522(3.3V) | 3.3V | 必须3.3V,不可接5V |
| AS608(TX) | PA3 | 串口2接收(RX) |
| AS608(RX) | PA2 | 串口2发送(TX) |
| AS608(VCC) | 3.3V/5V | 模块支持宽压 |
| AS608(GND) | GND | 共地 |
| OLED(SDA) | PB7 | I2C数据 |
| OLED(SCL) | PB6 | I2C时钟 |
| OLED(VCC) | 3.3V/5V | |
| OLED(GND) | GND | |
| 继电器(IN) | PB0 | 高电平开门 |
| 继电器(VCC) | 5V | |
| 继电器(GND) | GND |
三、系统流程图
#mermaid-svg-slVZNhFQZZfA2N4K{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-slVZNhFQZZfA2N4K .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-slVZNhFQZZfA2N4K .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-slVZNhFQZZfA2N4K .error-icon{fill:#552222;}#mermaid-svg-slVZNhFQZZfA2N4K .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-slVZNhFQZZfA2N4K .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-slVZNhFQZZfA2N4K .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-slVZNhFQZZfA2N4K .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-slVZNhFQZZfA2N4K .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-slVZNhFQZZfA2N4K .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-slVZNhFQZZfA2N4K .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-slVZNhFQZZfA2N4K .marker{fill:#333333;stroke:#333333;}#mermaid-svg-slVZNhFQZZfA2N4K .marker.cross{stroke:#333333;}#mermaid-svg-slVZNhFQZZfA2N4K svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-slVZNhFQZZfA2N4K p{margin:0;}#mermaid-svg-slVZNhFQZZfA2N4K .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-slVZNhFQZZfA2N4K .cluster-label text{fill:#333;}#mermaid-svg-slVZNhFQZZfA2N4K .cluster-label span{color:#333;}#mermaid-svg-slVZNhFQZZfA2N4K .cluster-label span p{background-color:transparent;}#mermaid-svg-slVZNhFQZZfA2N4K .label text,#mermaid-svg-slVZNhFQZZfA2N4K span{fill:#333;color:#333;}#mermaid-svg-slVZNhFQZZfA2N4K .node rect,#mermaid-svg-slVZNhFQZZfA2N4K .node circle,#mermaid-svg-slVZNhFQZZfA2N4K .node ellipse,#mermaid-svg-slVZNhFQZZfA2N4K .node polygon,#mermaid-svg-slVZNhFQZZfA2N4K .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-slVZNhFQZZfA2N4K .rough-node .label text,#mermaid-svg-slVZNhFQZZfA2N4K .node .label text,#mermaid-svg-slVZNhFQZZfA2N4K .image-shape .label,#mermaid-svg-slVZNhFQZZfA2N4K .icon-shape .label{text-anchor:middle;}#mermaid-svg-slVZNhFQZZfA2N4K .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-slVZNhFQZZfA2N4K .rough-node .label,#mermaid-svg-slVZNhFQZZfA2N4K .node .label,#mermaid-svg-slVZNhFQZZfA2N4K .image-shape .label,#mermaid-svg-slVZNhFQZZfA2N4K .icon-shape .label{text-align:center;}#mermaid-svg-slVZNhFQZZfA2N4K .node.clickable{cursor:pointer;}#mermaid-svg-slVZNhFQZZfA2N4K .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-slVZNhFQZZfA2N4K .arrowheadPath{fill:#333333;}#mermaid-svg-slVZNhFQZZfA2N4K .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-slVZNhFQZZfA2N4K .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-slVZNhFQZZfA2N4K .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-slVZNhFQZZfA2N4K .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-slVZNhFQZZfA2N4K .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-slVZNhFQZZfA2N4K .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-slVZNhFQZZfA2N4K .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-slVZNhFQZZfA2N4K .cluster text{fill:#333;}#mermaid-svg-slVZNhFQZZfA2N4K .cluster span{color:#333;}#mermaid-svg-slVZNhFQZZfA2N4K div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-slVZNhFQZZfA2N4K .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-slVZNhFQZZfA2N4K rect.text{fill:none;stroke-width:0;}#mermaid-svg-slVZNhFQZZfA2N4K .icon-shape,#mermaid-svg-slVZNhFQZZfA2N4K .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-slVZNhFQZZfA2N4K .icon-shape p,#mermaid-svg-slVZNhFQZZfA2N4K .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-slVZNhFQZZfA2N4K .icon-shape .label rect,#mermaid-svg-slVZNhFQZZfA2N4K .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-slVZNhFQZZfA2N4K .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-slVZNhFQZZfA2N4K .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-slVZNhFQZZfA2N4K :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
是
否
是
是
否
否
否
系统上电初始化
OLED显示欢迎界面
RFID模块初始化
指纹模块初始化
继电器复位(关门)
循环检测:刷卡/指纹
检测到RFID卡?
读取卡号并验证
卡号合法?
继电器开门,OLED显示开门成功
OLED显示无效卡
延时后关门,返回检测
检测到指纹输入?
指纹匹配验证
匹配成功?
OLED显示指纹不匹配
样式说明:深色背景,白色字体,流程清晰直观,小白可直接对照理解系统运行逻辑。
四、工程创建与基础配置
1. 新建STM32工程
2. 基础时钟配置
所有外设时钟统一使能:GPIOA、GPIOB、SPI1、USART2、I2C1时钟使能。
五、模块驱动代码
所有代码文件名清晰标注,零基础直接新建对应文件复制即可。
1. 延时函数:delay.c / delay.h
delay.c
#include "delay.h"
// 微秒级延时
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD = 9 * nus;
SysTick->VAL = 0X00;
SysTick->CTRL = 0X01;
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16)));
SysTick->CTRL = 0X00;
SysTick->VAL = 0X00;
}
// 毫秒级延时
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD = 9000 * nms;
SysTick->VAL = 0X00;
SysTick->CTRL = 0X01;
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16)));
SysTick->CTRL = 0X00;
SysTick->VAL = 0X00;
}
delay.h
#ifndef __DELAY_H
#define __DELAY_H
#include "stm32f10x.h"
void delay_us(u32 nus);
void delay_ms(u16 nms);
#endif
2. OLED驱动:oled.c / oled.h
oled.c
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"
u8 OLED_GRAM[128][8];
// OLED写命令
void OLED_WR_Cmd(u8 cmd)
{
I2C_Start();
I2C_Send_Byte(0x78);
I2C_Wait_Ack();
I2C_Send_Byte(0x00);
I2C_Wait_Ack();
I2C_Send_Byte(cmd);
I2C_Wait_Ack();
I2C_Stop();
}
// OLED写数据
void OLED_WR_Data(u8 data)
{
I2C_Start();
I2C_Send_Byte(0x78);
I2C_Wait_Ack();
I2C_Send_Byte(0x40);
I2C_Wait_Ack();
I2C_Send_Byte(data);
I2C_Wait_Ack();
I2C_Stop();
}
// OLED初始化
void OLED_Init(void)
{
delay_ms(100);
OLED_WR_Cmd(0xAE);
OLED_WR_Cmd(0xD5);
OLED_WR_Cmd(0x80);
OLED_WR_Cmd(0xA8);
OLED_WR_Cmd(0x3F);
OLED_WR_Cmd(0xD3);
OLED_WR_Cmd(0x00);
OLED_WR_Cmd(0x40);
OLED_WR_Cmd(0x8D);
OLED_WR_Cmd(0x14);
OLED_WR_Cmd(0x20);
OLED_WR_Cmd(0x02);
OLED_WR_Cmd(0xA1);
OLED_WR_Cmd(0xC8);
OLED_WR_Cmd(0xDA);
OLED_WR_Cmd(0x12);
OLED_WR_Cmd(0x81);
OLED_WR_Cmd(0xCF);
OLED_WR_Cmd(0xD9);
OLED_WR_Cmd(0xF1);
OLED_WR_Cmd(0xDB);
OLED_WR_Cmd(0x30);
OLED_WR_Cmd(0xA4);
OLED_WR_Cmd(0xA6);
OLED_Clear();
OLED_WR_Cmd(0xAF);
}
// 清屏
void OLED_Clear(void)
{
u8 i, n;
for (i = 0; i < 8; i++)
{
OLED_WR_Cmd(0xB0 + i);
OLED_WR_Cmd(0x00);
OLED_WR_Cmd(0x10);
for (n = 0; n < 128; n++)
OLED_WR_Data(0x00);
}
}
// 显示字符串
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 size)
{
while (*chr)
{
OLED_ShowChar(x, y, *chr, size);
x += 8;
if (x > 120)
{
x = 0;
y += 2;
}
chr++;
}
}
oled.h
#ifndef __OLED_H
#define __OLED_H
#include "stm32f10x.h"
#include "delay.h"
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 size);
#endif
3. RC522 RFID驱动:rc522.c / rc522.h
rc522.c
#include "rc522.h"
#include "delay.h"
// SPI读写一个字节
u8 SPI1_RW_Byte(u8 byte)
{
u8 i;
for (i = 0; i < 8; i++)
{
if (byte & 0x80)
GPIO_SetBits(GPIOA, GPIO_Pin_7);
else
GPIO_ResetBits(GPIOA, GPIO_Pin_7);
byte <<= 1;
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
delay_us(1);
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_6))
byte |= 1;
GPIO_SetBits(GPIOA, GPIO_Pin_5);
delay_us(1);
}
return byte;
}
// RC522写寄存器
void MFRC522_WriteRegister(u8 addr, u8 data)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
SPI1_RW_Byte((addr << 1) & 0x7E);
SPI1_RW_Byte(data);
GPIO_SetBits(GPIOA, GPIO_Pin_4);
}
// RC522读寄存器
u8 MFRC522_ReadRegister(u8 addr)
{
u8 data;
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
SPI1_RW_Byte(((addr << 1) & 0x7E) | 0x80);
data = SPI1_RW_Byte(0xFF);
GPIO_SetBits(GPIOA, GPIO_Pin_4);
return data;
}
// RC522初始化
void MFRC522_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
MFRC522_WriteRegister(MFRC522_REG_COMMAND, PCD_IDLE);
MFRC522_WriteRegister(MFRC522_REG_MODE, 0x3D);
MFRC522_WriteRegister(MFRC522_REG_TR_MODE, 0x00);
MFRC522_WriteRegister(MFRC522_REG_RF_CFG, 0x70);
MFRC522_WriteRegister(MFRC522_REG_TX_CONTROL, 0x40 | 0x20);
}
// 检测卡片
u8 MFRC522_CheckCard(u8 *id)
{
u8 status;
u8 str[4];
status = MFRC522_Request(PICC_REQIDL, str);
if (status == MI_OK)
status = MFRC522_Anticoll(str);
if (status == MI_OK)
{
id[0] = str[0];
id[1] = str[1];
id[2] = str[2];
id[3] = str[3];
}
return status;
}
rc522.h
#ifndef __RC522_H
#define __RC522_H
#include "stm32f10x.h"
void MFRC522_Init(void);
u8 MFRC522_CheckCard(u8 *id);
#endif
4. AS608指纹驱动:finger.c / finger.h
finger.c
#include "finger.h"
#include "usart2.h"
#include "delay.h"
// 指纹模块握手
u8 AS608_Ping(void)
{
u8 send_buf[] = {0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x01, 0x00, 0x03};
u8 recv_buf[10];
USART2_Send_Data(send_buf, 11);
delay_ms(100);
if (USART2_Receive_Data(recv_buf, 10))
{
if (recv_buf[9] == 0x00)
return 0;
}
return 1;
}
// 指纹录入
u8 AS608_Enroll(u8 page)
{
u8 send_buf[] = {0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x03, 0x01, 0x00, 0x05};
send_buf[9] = page;
USART2_Send_Data(send_buf, 12);
delay_ms(1000);
return 0;
}
// 指纹识别
u8 AS608_Match(void)
{
u8 send_buf[] = {0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x02, 0x00, 0x03};
u8 recv_buf[15];
USART2_Send_Data(send_buf, 11);
delay_ms(500);
if (USART2_Receive_Data(recv_buf, 15))
{
if (recv_buf[6] == 0x00)
return 0;
}
return 1;
}
finger.h
#ifndef __FINGER_H
#define __FINGER_H
#include "stm32f10x.h"
u8 AS608_Ping(void);
u8 AS608_Match(void);
#endif
5. 继电器驱动:relay.c / relay.h
relay.c
#include "relay.h"
// 继电器初始化 PB0
void Relay_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 初始关门
}
// 开门
void Relay_Open(void)
{
GPIO_SetBits(GPIOB, GPIO_Pin_0);
}
// 关门
void Relay_Close(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_0);
}
relay.h
#ifndef __RELAY_H
#define __RELAY_H
#include "stm32f10x.h"
void Relay_Init(void);
void Relay_Open(void);
void Relay_Close(void);
#endif
六、主函数代码:main.c
#include "stm32f10x.h"
#include "delay.h"
#include "oled.h"
#include "rc522.h"
#include "finger.h"
#include "relay.h"
// 合法卡卡号(自行修改为你的卡片UID)
u8 Valid_Card[4] = {0x12, 0x34, 0x56, 0x78};
int main(void)
{
u8 card_id[4];
u8 i;
// 模块初始化
delay_init();
OLED_Init();
MFRC522_Init();
Relay_Init();
USART2_Init(9600);
// 开机界面
OLED_Clear();
OLED_ShowString(0, 0, " Access Control", 16);
OLED_ShowString(0, 2, "RFID+Finger+OLED", 16);
delay_ms(2000);
while (1)
{
OLED_Clear();
OLED_ShowString(0, 0, "Ready to Verify", 16);
OLED_ShowString(0, 2, "Card/Finger", 16);
// 1. 检测RFID卡
if (MFRC522_CheckCard(card_id) == MI_OK)
{
OLED_Clear();
OLED_ShowString(0, 0, "Card Detected", 16);
// 验证卡号
if (card_id[0] == Valid_Card[0] && card_id[1] == Valid_Card[1] &&
card_id[2] == Valid_Card[2] && card_id[3] == Valid_Card[3])
{
OLED_ShowString(0, 2, "Open Success!", 16);
Relay_Open();
delay_ms(3000);
Relay_Close();
}
else
{
OLED_ShowString(0, 2, "Invalid Card!", 16);
delay_ms(2000);
}
continue;
}
// 2. 检测指纹
if (AS608_Match() == 0)
{
OLED_Clear();
OLED_ShowString(0, 0, "Finger Matched", 16);
OLED_ShowString(0, 2, "Open Success!", 16);
Relay_Open();
delay_ms(3000);
Relay_Close();
}
delay_ms(100);
}
}
七、零基础操作步骤
步骤1:硬件接线
严格按照第二章表格接线,所有GND必须共地,3.3V模块绝不接5V。
步骤2:新建工程文件
在Keil5中新建项目,依次新建以下文件: delay.c/h、oled.c/h、rc522.c/h、finger.c/h、relay.c/h、main.c
步骤3:复制代码
将第五章、第六章对应代码复制到对应文件中。
步骤4:修改合法卡号
在main.c中找到Valid_Card[4],修改为你自己的RFID卡UID。
步骤5:编译下载
步骤6:功能测试
八、常见问题解决
- 检查I2C接线(SDA/SCL是否接反)
- 检查供电是否正常
- 检查SPI接线
- 卡片贴近模块感应面
- 检查串口2接线(TX/RX接反会无法通信)
- 重新录入指纹
- 检查PB0接线
- 确认代码中开门逻辑执行


