news 2026/5/16 11:03:04

STM32F103RCT6(HAL库)驱动RC522:从零构建RFID门禁系统核心模块

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103RCT6(HAL库)驱动RC522:从零构建RFID门禁系统核心模块

1. 项目背景与硬件准备

最近在做一个智能门禁系统的原型开发,选择了STM32F103RCT6作为主控芯片,搭配RC522 RFID读写模块。这种组合在门禁考勤系统中非常常见,成本低且性能稳定。先说说为什么选这两个器件:

STM32F103RCT6是ST的经典款,72MHz主频够用,自带SPI接口正好驱动RC522。RC522模块更是个好东西,13.56MHz工作频率,支持ISO14443A标准的M1卡,淘宝上十块钱就能买到,还带天线板。我用的这块开发板是正点原子的MiniSTM32,引脚排布规整,调试起来特别方便。

硬件连接其实很简单,七根杜邦线就能搞定:

  • PA4(SPI1_NSS) -> SDA
  • PA5(SPI1_SCK) -> SCK
  • PA7(SPI1_MOSI) -> MOSI
  • PA6(SPI1_MISO) <- MISO
  • PA3 -> RST
  • 3.3V -> 3.3V
  • GND -> GND

这里有个坑要注意:RC522的IQ引脚不用接,悬空就行。第一次做的时候我傻乎乎地把所有引脚都接了,结果模块死活不工作,查了半天手册才发现这个细节。

2. CubeMX配置要点

用STM32CubeMX配置SPI接口时,建议这样设置:

  1. SPI模式选全双工主模式
  2. 时钟分频设到PCLK2的8分频(9MHz)
  3. 时钟极性低电平,相位第一个边沿
  4. 数据宽度8bit,MSB先行
  5. NSS信号选软件控制

GPIO配置要注意:

  • NSS引脚(PA4)要手动设为GPIO输出
  • RST引脚(PA3)同样设为GPIO输出
  • SPI的SCK/MOSI/MISO记得配置复用功能

我遇到过SPI时钟速率过高导致通信失败的情况。RC522官方手册说最高支持10MHz,但实测发现当STM32超频到128MHz时,即使分频后SPI时钟在规格内也会出问题。后来把系统时钟调回72MHz就稳定了。

3. RC522驱动开发详解

3.1 寄存器操作基础

RC522的所有功能都是通过寄存器控制的,底层驱动要封装好读写函数。这里分享我的实现:

// 写寄存器 void MFRC_WriteReg(uint8_t addr, uint8_t data) { uint8_t AddrByte = (addr << 1) & 0x7E; // 地址格式转换 RS522_NSS(0); // 片选使能 HAL_SPI_TransmitReceive(&hspi1, &AddrByte, &ret, 1, 10); HAL_SPI_TransmitReceive(&hspi1, &data, &ret, 1, 10); RS522_NSS(1); // 片选禁用 } // 读寄存器 uint8_t MFRC_ReadReg(uint8_t addr) { uint8_t AddrByte = ((addr << 1) & 0x7E) | 0x80; // 读操作位 uint8_t data = 0; RS522_NSS(0); HAL_SPI_TransmitReceive(&hspi1, &AddrByte, &ret, 1, 10); HAL_SPI_TransmitReceive(&hspi1, &dummy, &data, 1, 10); RS522_NSS(1); return data; }

调试时发现个有趣现象:如果连续快速读写寄存器,必须保证NSS信号有足够的高低电平保持时间。我有次在for循环里连续写配置,结果只有第一个字节生效了,后来在每次操作后加了1us延时就正常了。

3.2 模块初始化流程

完整的初始化应该包括:

  1. 硬件复位(拉低RST引脚至少2us)
  2. 软复位(写CommandReg)
  3. 配置定时器参数
  4. 开启天线
void PCD_Reset(void) { // 硬复位 RS522_RST(0); HAL_Delay(2); RS522_RST(1); HAL_Delay(2); // 软复位 MFRC_WriteReg(MFRC_CommandReg, MFRC_RESETPHASE); // 定时器配置 MFRC_WriteReg(MFRC_ModeReg, 0x3D); MFRC_WriteReg(MFRC_TReloadRegL, 30); MFRC_WriteReg(MFRC_TModeReg, 0x8D); MFRC_WriteReg(MFRC_TPrescalerReg, 0x3E); }

天线控制也有讲究,开关间隔至少要1ms:

void PCD_AntennaOn(void) { uint8_t temp = MFRC_ReadReg(MFRC_TxControlReg); if (!(temp & 0x03)) { MFRC_SetBitMask(MFRC_TxControlReg, 0x03); } }

4. RFID卡片操作实战

4.1 寻卡与防冲突

寻卡使用PCD_Request命令,有两种模式:

  • PICC_REQIDL:只寻找未进入休眠的卡
  • PICC_REQALL:寻找所有卡
char PCD_Request(uint8_t RequestMode, uint8_t *pCardType) { uint8_t CmdFrameBuf[MFRC_MAXRLEN]; CmdFrameBuf[0] = RequestMode; // 发送寻卡命令 char status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 1, CmdFrameBuf, &unLen); if(status == PCD_OK && unLen == 0x10) { pCardType[0] = CmdFrameBuf[0]; pCardType[1] = CmdFrameBuf[1]; } return status; }

防冲突算法是重点,RC522内置了防冲突机制,我们只需要调用PCD_Anticoll函数就能获得卡片UID:

char PCD_Anticoll(uint8_t *pSnr) { uint8_t CmdFrameBuf[MFRC_MAXRLEN] = {PICC_ANTICOLL1, 0x20}; char status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 2, CmdFrameBuf, &unLen); if(status == PCD_OK) { for(int i=0; i<4; i++) { pSnr[i] = CmdFrameBuf[i]; } } return status; }

4.2 密钥认证流程

M1卡的每个扇区都有独立的密钥,进行读写前必须先认证。典型流程如下:

char PCD_AuthState(uint8_t AuthMode, uint8_t BlockAddr, uint8_t *pKey, uint8_t *pSnr) { uint8_t CmdFrameBuf[12]; CmdFrameBuf[0] = AuthMode; CmdFrameBuf[1] = BlockAddr; memcpy(&CmdFrameBuf[2], pKey, 6); // 密钥 memcpy(&CmdFrameBuf[8], pSnr, 4); // UID char status = MFRC_CmdFrame(MFRC_AUTHENT, CmdFrameBuf, 12, CmdFrameBuf, &unLen); return status; }

注意BlockAddr参数:虽然认证是以扇区为单位,但参数可以是扇区内的任意块地址。比如要操作第2扇区(块8-11),传入8、9、10或11都可以。

5. 数据读写实现

5.1 读块数据

读操作使用PICC_READ命令,每次读取16字节:

char PCD_ReadBlock(uint8_t BlockAddr, uint8_t *pData) { uint8_t CmdFrameBuf[4] = {PICC_READ, BlockAddr}; MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]); char status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen); if(status == PCD_OK && unLen == 0x90) { memcpy(pData, CmdFrameBuf, 16); } return status; }

有个易错点:块地址范围是0-63,但:

  • 每个扇区的最后一块(如块3、7、11...)是控制块,存储密钥和访问权限
  • 块地址不能越界,否则会返回错误

5.2 写块数据

写操作相对复杂,需要先发送写命令,再发送数据:

char PCD_WriteBlock(uint8_t BlockAddr, uint8_t *pData) { uint8_t CmdFrameBuf[18]; CmdFrameBuf[0] = PICC_WRITE; CmdFrameBuf[1] = BlockAddr; MFRC_CalulateCRC(CmdFrameBuf, 2, &CmdFrameBuf[2]); // 发送写命令 char status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 4, CmdFrameBuf, &unLen); if(status == PCD_OK) { memcpy(CmdFrameBuf, pData, 16); MFRC_CalulateCRC(CmdFrameBuf, 16, &CmdFrameBuf[16]); // 发送数据 status = MFRC_CmdFrame(MFRC_TRANSCEIVE, CmdFrameBuf, 18, CmdFrameBuf, &unLen); } return status; }

重要安全提示:写操作前一定要先验证密钥,否则会触发芯片的保护机制。我有次忘记验证直接写卡,导致一张M1卡被锁死,只能报废处理。

6. 门禁系统逻辑整合

6.1 串口指令控制

通过串口可以动态修改写入卡的数据,我设计了简单协议:

  • 指令格式:"Set_CardData:数据内容;"
  • 最大支持16字节数据
void USART_Handl(void) { if(memcmp(UartData, "Set_CardData:", 13) == 0) { if(UartRxLen-14 <= 16) { RFID_WritFlag = 1; memcpy(RFID_WriteData, &UartData[13], UartRxLen-14); printf("准备写入数据:%s\n", RFID_WriteData); } } }

6.2 LED状态指示

增加两个LED作为状态指示:

  • LED0:系统运行指示灯(1Hz闪烁)
  • LED1:刷卡状态指示灯(刷卡时亮200ms)
// 在main循环中添加 HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin); HAL_Delay(500); if(cardDetected) { HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); HAL_Delay(200); HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); cardDetected = 0; }

7. 常见问题排查

  1. 寻卡失败

    • 检查天线连接是否正常
    • 测量3.3V电源是否稳定
    • 用逻辑分析仪抓SPI波形,看时钟和数据是否正常
  2. 认证总是失败

    • 确认使用的密钥与卡片匹配
    • 检查UID读取是否正确
    • 尝试用默认密钥0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
  3. 写操作不生效

    • 确保认证通过
    • 检查块地址是否有效(不能是控制块)
    • 验证写保护位是否被设置
  4. 通信不稳定

    • 缩短SPI线缆长度
    • 在3.3V电源加100uF电容
    • 降低SPI时钟速率

有个特别隐蔽的bug我花了三天才解决:当开发板通过USB供电时,如果插拔USB线,RC522会进入奇怪的状态。后来发现是电源扰动导致的,在VCC和GND之间加了10uF钽电容后问题消失。

8. 项目优化方向

  1. 增加多卡管理

    • 在Flash中存储授权卡UID列表
    • 实现添加/删除卡功能
  2. 引入EEPROM存储

    • 记录刷卡日志
    • 保存系统配置参数
  3. 添加无线功能

    • 通过ESP8266联网上报数据
    • 支持远程授权管理
  4. 低功耗优化

    • 使用STM32的停机模式
    • 间歇性唤醒RC522检测卡片

这个项目最让我惊喜的是RC522的稳定性。连续运行一个月,处理了上千次刷卡操作,没有出现一次误读或漏读。现在这套系统已经用在我工作室的门禁上,配合电磁锁工作得非常可靠。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/16 11:02:12

一键自动投递AI软件

一键自动投递AI软件版本&#xff1a;v1.0 更新日期&#xff1a;2026-05-15项目概述基于Spring AI开发的求职辅助工具&#xff0c;用户一次录入个人信息&#xff0c;AI即可智能识别任意公司投递页面的表单字段并自动填写&#xff0c;用户手动确认后提交。快速开始环境准备JDK 21…

作者头像 李华
网站建设 2026/5/16 11:02:11

CircuitPython驱动OV2640摄像头:从硬件连接到二维码识别的嵌入式视觉实践

1. 项目概述&#xff1a;为微控制器赋予“眼睛”在嵌入式开发的世界里&#xff0c;给一块小小的电路板加上“视觉”能力&#xff0c;曾经是件门槛颇高的事情。你需要处理复杂的时序信号、编写底层的寄存器配置代码&#xff0c;还得为有限的内存和算力头疼。但现在&#xff0c;情…

作者头像 李华
网站建设 2026/5/16 11:02:03

蜡笔变蜡烛:DIY分层香薰蜡烛的材料原理与制作实践

1. 项目概述&#xff1a;当蜡笔遇见蜡烛&#xff0c;一次关于气味与色彩的记忆重塑不知道你有没有过这样的体验&#xff1a;打开一盒崭新的蜡笔&#xff0c;那股混合着油脂、黏土与淡淡皂感的独特气味扑面而来&#xff0c;瞬间就能将你拉回铺满画纸的童年午后。Crayola蜡笔的官…

作者头像 李华
网站建设 2026/5/16 11:01:13

Harness Engineering:连接模型能力与业务价值的桥梁

Harness Engineering:连接模型能力与业务价值的桥梁 本文首发于「AI工程化实践」公众号,作者@资深AI架构师张小明,转载请注明来源 引言 痛点引入 2023年被称为大模型落地元年,截止2024年Q3,国内已经有超过200款通用大模型、3000多款行业大模型发布,超过70%的中大型企业…

作者头像 李华
网站建设 2026/5/16 11:01:11

告别sudo!在Ubuntu 20.04桌面版配置纯root环境,适合特定开发/测试场景

深度解锁Ubuntu 20.04纯root环境&#xff1a;开发者专属的高效沙盒方案 在特定开发场景中&#xff0c;频繁的权限验证往往成为效率瓶颈。想象一下&#xff1a;当你正在进行嵌入式系统交叉编译、调试内核模块或测试网络服务时&#xff0c;每次操作都需要输入密码确认&#xff0c…

作者头像 李华