【嵌入式实战-06】从零搭建 STM32+MFRC522 RFID 门禁系统
前言
很多嵌入式初学者学完 RFID 理论后,往往卡在 "怎么落地成一个能用的项目" 这一步。本文将带你从零开始,搭建一个工业级可用的 RFID 门禁系统,涵盖硬件选型、电路连接、代码实现、调试排坑全流程。
本文亮点:
- 基于STM32F103C8T6+MFRC522,成本低、资料多、易移植
- 完整可运行的HAL 库源码,注释详细,直接编译烧录
- 支持按键加卡 / 删卡,授权信息断电不丢失
- 真实项目踩坑记录,解决 90% 新手会遇到的问题
- 提供 5 种以上功能扩展方案,可直接用于毕设或小项目
适合人群:嵌入式初学者、物联网从业者、正在做毕设的学生
一、系统整体设计
1.1 核心功能
- ✅ 刷卡开门:识别授权 IC 卡,自动控制电磁锁
- ✅ 声光提示:绿灯亮 + 短鸣 = 开门成功;红灯亮 + 长鸣 = 非法卡
- ✅ 授权管理:通过按键添加 / 删除授权卡,最多支持 20 张
- ✅ 自动关门:开门 3 秒后自动上锁
- ✅ 防冲突处理:多张卡同时刷卡时,能正确识别单张卡
1.2 系统架构
STM32F103C8T6主控 ├─ MFRC522 RFID读卡器 → 读取IC卡UID ├─ 5V继电器模块 → 控制12V电磁锁 ├─ 有源蜂鸣器 → 声音提示 ├─ 红绿LED指示灯 → 状态指示 └─ 2个独立按键 → 加卡/删卡功能二、硬件选型与电路连接
2.1 完整 BOM 表(总成本约 80 元)
| 模块名称 | 推荐型号 | 核心作用 | 选型原因 | 成本参考 |
|---|---|---|---|---|
| 主控板 | STM32F103C8T6 最小系统板 | 系统核心控制 | 入门经典款,性价比高,资料丰富 | ¥15 |
| RFID 读卡器 | MFRC522(带天线) | 识别 13.56MHz IC 卡 | 集成度高,SPI 通信,驱动简单 | ¥12 |
| 执行机构 | 5V 单路继电器模块 | 控制电磁锁通断 | 带光耦隔离,抗干扰能力强 | ¥8 |
| 门锁 | 12V 电磁锁(吸力 60kg) | 执行开门 / 关门动作 | 断电上锁,安全可靠 | ¥25 |
| 提示模块 | 有源蜂鸣器 + 红绿 LED | 声光状态提示 | 成本低,效果直观 | ¥5 |
| 输入模块 | 2 个独立按键 | 加卡 / 删卡操作 | 简单易用,无需额外驱动 | ¥2 |
| 电源模块 | 12V/2A 适配器 + 3.3V 降压模块 | 系统供电 | 电磁锁单独供电,避免干扰 | ¥13 |
2.2 详细引脚连接表
| STM32 引脚 | 连接模块 | 引脚功能 |
|---|---|---|
| PA4 | MFRC522 | SDA(SPI 片选) |
| PA5 | MFRC522 | SCK(SPI 时钟) |
| PA6 | MFRC522 | MISO(主入从出) |
| PA7 | MFRC522 | MOSI(主出从入) |
| PA8 | MFRC522 | RST(复位) |
| PB0 | 继电器模块 | IN(控制信号) |
| PB1 | 绿色 LED | 正极 |
| PB2 | 红色 LED | 正极 |
| PB3 | 有源蜂鸣器 | 正极 |
| PA0 | 加卡按键 | 输入(下拉) |
| PA1 | 删卡按键 | 输入(下拉) |
| 3.3V | MFRC522/LED/ 蜂鸣器 / 按键 | 电源 |
| GND | 所有模块 | 地 |
⚠️ 关键硬件注意事项
- MFRC522 只能用 3.3V 供电,接 5V 会直接烧毁芯片
- 电磁锁必须单独供电,并在两端并联FR107 续流二极管(阴极接 12V 正极,阳极接负极),防止断电瞬间的反电动势击穿继电器和主控
- MFRC522 天线要远离金属物体,否则读取距离会大幅缩短(正常 3-5cm)
- 继电器触点容量要大于电磁锁工作电流(通常 1-2A)
三、软件实现(完整 HAL 库源码)
3.1 开发环境
- Keil MDK5.38
- STM32CubeMX 6.10.0
- ST-Link/V2 仿真器
3.2 STM32CubeMX 配置
- 时钟配置:外部 8MHz 晶振,系统时钟 72MHz
- SPI1 配置:Mode 0(CPOL=0, CPHA=0),数据大小 8 位,波特率 5MHz(实测最稳定)
- GPIO 配置:
- PA4/PA5/PA6/PA7/PA8:推挽输出(MFRC522)
- PB0/PB1/PB2/PB3:推挽输出(继电器 / LED / 蜂鸣器)
- PA0/PA1:下拉输入(按键)
- USART1 配置:波特率 115200,用于调试输出
3.3 核心代码实现
3.3.1 MFRC522 驱动核心函数
// mfrc522.h #ifndef __MFRC522_H #define __MFRC522_H #include "stm32f1xx_hal.h" #define MI_OK 0 #define MI_ERR 1 // MFRC522命令定义 #define PICC_REQIDL 0x26 #define PICC_ANTICOLL1 0x93 #define PICC_SELECTTAG 0x93 #define PICC_HALT 0x50 // 函数声明 void MFRC522_Init(void); uint8_t MFRC522_Request(uint8_t reqMode, uint8_t *TagType); uint8_t MFRC522_Anticoll(uint8_t *cardID); uint8_t MFRC522_Select(uint8_t *cardID); void MFRC522_Halt(void); #endif// mfrc522.c(核心函数节选) #include "mfrc522.h" #include "spi.h" #include "gpio.h" // 写寄存器 void MFRC522_WriteReg(uint8_t reg, uint8_t value) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, ®, 1, 100); HAL_SPI_Transmit(&hspi1, &value, 1, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); } // 读寄存器 uint8_t MFRC522_ReadReg(uint8_t reg) { uint8_t value; reg = 0x80 | reg; HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi1, ®, 1, 100); HAL_SPI_Receive(&hspi1, &value, 1, 100); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); return value; } // 防冲突函数:读取卡片唯一ID uint8_t MFRC522_Anticoll(uint8_t *cardID) { uint8_t status; uint8_t i, snr_check = 0; uint16_t recvLen; uint8_t sendData[2] = {PICC_ANTICOLL1, 0x20}; status = MFRC522_Communicate(PCD_TRANSCEIVE, sendData, 2, sendData, &recvLen); if(status == MI_OK && recvLen == 40) { for(i=0; i<4; i++) { cardID[i] = sendData[i]; snr_check ^= sendData[i]; } if(snr_check != sendData[4]) status = MI_ERR; } else { status = MI_ERR; } return status; }3.3.2 授权卡管理(Flash 存储)
// card_manager.h #ifndef __CARD_MANAGER_H #define __CARD_MANAGER_H #include "stm32f1xx_hal.h" #define MAX_CARD_NUM 20 // 最多支持20张授权卡 #define FLASH_ADDR 0x0800F800 // Flash存储地址(最后一页) // 函数声明 void CardManager_Init(void); uint8_t CardManager_Check(uint8_t *cardID); uint8_t CardManager_Add(uint8_t *cardID); uint8_t CardManager_Delete(uint8_t *cardID); void CardManager_Save(void); #endif3.3.3 主逻辑代码
// main.c #include "main.h" #include "spi.h" #include "gpio.h" #include "usart.h" #include "mfrc522.h" #include "card_manager.h" #include "relay.h" #include "beep.h" #include "led.h" #include "key.h" int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_SPI1_Init(); MX_USART1_UART_Init(); MFRC522_Init(); CardManager_Init(); printf("RFID门禁系统启动成功\r\n"); uint8_t cardID[4]; uint8_t status; uint32_t door_timer = 0; uint8_t door_state = 0; while(1) { // 按键处理:加卡/删卡 Key_Process(); // 寻卡 if(MFRC522_Request(PICC_REQIDL, cardID) == MI_OK) { // 防冲突,读取UID if(MFRC522_Anticoll(cardID) == MI_OK) { printf("检测到卡片,UID: %02X%02X%02X%02X\r\n", cardID[0], cardID[1], cardID[2], cardID[3]); // 检查是否是授权卡 if(CardManager_Check(cardID) == MI_OK) { printf("授权卡,开门\r\n"); Relay_Open(); LED_Green_On(); Beep_Short(); door_timer = HAL_GetTick(); door_state = 1; } else { printf("非法卡,报警\r\n"); LED_Red_On(); Beep_Long(); HAL_Delay(1000); LED_Red_Off(); } MFRC522_Halt(); // 休眠卡片 } } // 自动关门:3秒后 if(door_state && (HAL_GetTick() - door_timer > 3000)) { Relay_Close(); LED_Green_Off(); door_state = 0; printf("自动关门\r\n"); } HAL_Delay(10); } }四、测试与验证
4.1 测试步骤
- 上电测试:连接所有硬件,上电后绿色 LED 常亮,表示系统正常
- 刷卡测试:将一张空白 IC 卡靠近读卡器,串口会输出卡片 UID
- 加卡测试:按下加卡按键(PA0),蜂鸣器响一声,然后刷卡,提示 "加卡成功"
- 开门测试:刷刚添加的授权卡,继电器吸合,电磁锁打开,3 秒后自动关闭
- 非法卡测试:刷未授权的卡,蜂鸣器长鸣,红灯亮,门锁不打开
- 删卡测试:按下删卡按键(PA1),蜂鸣器响一声,然后刷卡,提示 "删卡成功"
- 断电测试:断电后重新上电,授权卡信息仍然保留
4.2 预期结果
- 授权卡刷卡成功率 100%,响应时间 < 0.5 秒
- 非法卡无法开门,报警提示正常
- 加卡 / 删卡功能正常,断电不丢失数据
- 自动关门时间准确,无卡顿
五、新手必看:踩坑与解决方案
这是我在实际项目中踩过的坑,90% 的新手都会遇到:
5.1 MFRC522 读取不到卡
问题现象:上电后读卡器无反应,串口无输出可能原因及解决:
- SPI 引脚接反:MISO 和 MOSI 是最容易接反的,一定要仔细核对
- 电源错误:MFRC522 必须用 3.3V,接 5V 会直接烧毁
- SPI 配置错误:必须设置为Mode 0(CPOL=0, CPHA=0),速率不要超过 5MHz
- 复位引脚未接:RST 引脚必须接 STM32 的 GPIO,不能悬空
- 天线问题:天线焊接不良或靠近金属,会导致读取距离为 0
5.2 电磁锁干扰导致系统死机
问题现象:开门瞬间系统死机,需要重新上电根本原因:电磁锁是感性负载,断电瞬间会产生数百伏的反电动势,通过电源线路干扰主控解决方案:
- 电磁锁必须单独供电,不要和主控共用电源
- 在电磁锁两端并联FR107 续流二极管(阴极接 12V 正极,阳极接负极)
- 继电器模块必须带光耦隔离,切断干扰路径
- 在主控电源输入端加 100uF 电解电容和 0.1uF 陶瓷电容滤波
5.3 多张卡同时刷卡出错
问题现象:两张卡同时靠近读卡器时,系统无反应或识别错误解决方案:正确实现 MFRC522 的防碰撞算法,本文提供的代码已经包含完整的防碰撞处理,能正确识别多张卡中的任意一张
5.4 授权卡信息断电丢失
问题现象:断电后重新上电,之前添加的授权卡全部失效解决方案:不要将授权卡信息存储在 RAM 中,要存储到 STM32 的Flash中。本文提供的 CardManager 模块已经实现了 Flash 读写功能,断电后数据不会丢失。
六、功能扩展方案
这个基础门禁系统可以很方便地扩展更多功能:
- 添加 LCD 显示屏:使用 0.96 寸 OLED 显示屏,显示卡号、系统状态、操作提示
- WiFi 远程控制:添加 ESP8266 模块,实现手机 APP 远程开门、刷卡记录查询
- 指纹识别:集成 AS608 指纹模块,实现 RFID + 指纹双重认证
- 密码开锁:添加 4x4 矩阵键盘,支持密码开锁
- 刷卡记录上传:将刷卡记录上传到云端服务器,实现远程监控
- 低功耗设计:使用 STM32 的低功耗模式,适合电池供电的场景
七、总结
本文详细介绍了如何从零搭建一个完整的 RFID 门禁系统,从硬件选型到软件实现,再到调试排坑,每一步都给出了详细的说明和完整的代码。这个系统不仅可以作为学习项目,还可以直接用于实际的门禁场景。
通过这个项目,你可以掌握:
- STM32 HAL 库的使用
- SPI 通信协议
- MFRC522 RFID 模块的驱动开发
- Flash 数据存储
- 硬件抗干扰设计
- 嵌入式系统的整体设计思路
创作不易,如果本文对你有帮助,欢迎点赞、收藏、转发,你的支持是我持续输出的动力!
