
简介STM32RC522刷卡模块是一套面向嵌入式入门者和物联网开发者的RFID读卡参考工程覆盖从STM32最小系统到RC522射频前端的完整软件链路核心目标是读取MIFARE系列IC卡的唯一ID并实时显示。压缩包共148个文件整体约2.78MB以C源码和头文件为主包含STM32F10x标准外设库、MFRC522驱动和Keil MDK工程配置也保留编译生成的.o/axf/hex等文件可直接打开烧录验证。代码中集成了OLED液晶显示与USART串口打印便于观察卡号主流程覆盖RC522初始化、SPI读写、寻卡、防冲突、选卡、读卡以及数据帧解析和CRC校验并附接线配置与寄存器操作说明。工程目录保留了项目文件、链接脚本和中间产物适合对照学习从底层寄存器配置到上层驱动调用的完整开发思路也可作为门禁、考勤或校园一卡通项目的原型参考。该项目已有5339人浏览学习尤其适合希望快速上手RFID应用或复刻刷卡读卡功能的嵌入式开发者。 手头这个门禁项目刚收尾趁着印象还深把STM32RC522这张刷卡组合的完整玩法整理出来。RC522方案便宜、资料多校园卡、门禁卡、考勤机里很多都是13.56MHz的M1卡所以STM32随便连个RC522模块就是一套能跑的刷卡原型。但真正动手的人会发现照着网上的例程接线容易想让刷卡稳定不翻车后面全是细节。这篇围绕RC522的硬件接线、驱动移植、一次完整刷卡流程和多卡场景里的坑把可能踩的雷提前排一排。1. 为什么门禁小项目绕不开RC5221.1 一片13.56MHz的读卡芯片能做什么RC522是NXP的ISO/IEC 14443A读卡芯片工作频率13.56MHz最常见的配套卡是M1卡也就是MIFARE Classic 1K。它的工作过程说白了就是芯片通过线圈产生射频场给非接触IC卡无线供电然后按14443A协议完成寻卡、防冲撞、选卡、密钥认证、数据读写。这个组合能落地的场景不少门禁、智能锁读UID白名单命中后控制继电器开锁考勤、会议签到记录UID和刷卡时间可离线缓存实验室器材借还、售货机鉴权后触发输出毕设、课设演示这是最常见的用途也是最容易从零跑通的题目之一如果你只是想做读卡号控制输出级别的原型M1卡完全够用。如果目标是NFC手机交互、卡模拟这类功能RC522就吃力了那是另一个选型方向。1.2 为什么不是PN532或其他很多人选型时会在RC522和PN532之间纠结。我的建议很直接STM32学习项目、小门禁、毕设选RC522理由是便宜、协议逻辑简单、资料极多踩坑了都能搜到答案。PN532支持14443A/B、Felica、NFC双模式能模拟卡也能读卡但价格贵、体积大驱动复杂度也高一个量级。方案支持类型接口价格上手难度适合场景RC522ISO14443AM1/UltralightSPI/I2C/UART极低低门禁、考勤、毕设原型PN53214443A/B、Felica、NFCSPI/I2C/UART中高中高NFC产品原型、卡模拟串口一体读卡器一般只读UIDUART中极低不折腾底层直接拿卡号这里要强调一个容易被忽略的判断标准可排查性。RC522的寄存器数量不多通信时序简单出了问题用万用表、逻辑分析仪都能快速定位。相比之下一体式读卡器虽然零门槛但内部黑盒出了问题你只能换板子。对于想学STM32的人来说RC522的透明感反而是最大的价值。2. 硬件接线与电气层面的几个坑2.1 引脚接线照抄不翻车RC522模块和STM32的接线标准接法如下我用的是SPI1RC522引脚STM32引脚说明VCC3.3V必须3.3V别接5VGNDGND共地RSTPB0复位引脚GPIO控制NSS/SDAPA4SPI片选GPIO或硬件NSS均可SCKPA5SPI时钟MOSIPA7主机输出MISOPA6主机输入IRQ不接查询方式不需要两个容易看晕的地方一是模块上丝印可能把NSS标成SDA别把它跟I2C的SDA混了这个SDA在RC522语境里是SPI片选二是IRQ引脚官方库用查询方式读寄存器就够了不需要真接外部中断。新手阶段把IRQ空着少一根线少一个出错点。2.2 RC522必须3.3V供电和连接线别省这个坑我在新手期踩过拿5V往VCC一插以为模块上有稳压电路。实际上很多廉价RC522模块根本没有板载稳压5V轻则发热、重则烧片就算侥幸没烧MISO引脚的输出电平也可能超过STM32的容忍范围长期用有隐患。建议直接从STM32板的3.3V引脚取电如果只有5V电源用AMS1117-3.3稳压后再供电。另一个问题是杜邦线长度。供电线、SPI信号线尽量短超过20cm时射频场稳定性和SPI信号都会恶化。射频部分对电源纹波尤其敏感我习惯在模块供电脚就近并一个10μF电解电容和一个100nF陶瓷电容花两毛钱能省掉大半的偶发刷卡失败。顺带说一句遇到过好多次刷不了卡最后发现根本不是RC522的问题是ST-Link连不上芯片、程序压根没下载进去。STM32开发板上最常见的报错就是no target found排查时要先确认调试器连接和程序下载正常再回头查模块。别一上来就怀疑RC522白折腾半天。2.3 为什么感应距离只有几厘米还读不到RC522模块的天线是板载的正常情况下白卡能稳定在3~5cm距离刷卡。如果你要贴上去才能读优先检查三件事天线周围有没有金属模块不要贴在金属外壳或铜柱附近13.56MHz的磁场会被金属涡流吃掉供电是不是被拉垮用万用表量模块VCC刷卡瞬间电压如果有明显跌落就是供电不足天线匹配电容有些模块批次匹配电容参数有差异感应距离会明显不同。这个问题在原型阶段不用深究产品化以后才需要网分仪去调谐振点如果你发现单个模块怎么调都只有1cm距离大概率不是程序问题是硬件本身。换一个模块对比测试是最快的排查方法RC522模块便宜备两个不心疼。3. 驱动移植SPI通信与MFRC522寄存器操作3.1 数据手册几十个寄存器实际只盯这几个RC522内部寄存器很多但移植驱动的时候实际频繁操作的也就七八个寄存器地址作用CommandReg0x01下发命令如发送、软复位ComIrqReg0x04中断标志判断发送/接收完成FIFODataReg0x09读写发送/接收数据FIFOLevelReg0x0AFIFO中剩余字节数BitFramingReg0x0D控制最后一字节发送位数、接收起始TxControlReg0x14天线开关控制Status2Reg0x08查询MFCrypto1On、忙碌状态VersionReg0x37版本号通信自检用把这几个寄存器搞清楚网上任何一个版本的RC522驱动你都能读懂而不是只会复制粘贴。CommandReg下发命令、FIFODataReg塞数据、ComIrqReg等结果这三位一体就是RC522驱动的基本套路。3.2 SPI读写帧的一个关键细节很多人移植时卡在SPI读不到数据问题往往出在地址格式上。RC522的SPI帧里寄存器地址不是直接写寄存器号MFRC522库会把寄存器地址左移一位写操作bit00读操作bit01。举个例子CommandReg寄存器地址是0x01写的时候发0x02读的时候发0x82。如果你自己用CubeMX的HAL库写底层地址拼接不对数据写不到目标寄存器读回来全是0xFF或0x00。SPI模式一般用Mode0也就是CPOL0、CPHA0串在SCLK上的数据要在正确相位采样。时钟频率不用拉高RC522模块10MHz以内都稳STM32的SPI分频到1~4MHz完全够了瓶颈不在SPI速率在卡的响应时间。3.3 移植驱动按这个顺序来我的习惯是严格分步走每步都留验证点用CubeMX初始化SPI1配置PA5/PA6/PA7/PA4生成工程实现SPI写字节、读字节两个底层函数实现MFRC522_WriteReg和MFRC522_ReadReg处理地址偏移实现PCD_Init软复位、配置定时器、开天线读VersionReg验证通信是否正常实现寻卡/防冲撞/选卡/认证/读写函数第5步千万别跳。VersionReg读不到后面调什么都是瞎猜。正常返回值一般是0x92或0x91读0xBB也是某些国产兼容芯片的正常版本号。读到0xFF是SPI通信失败读到0x00基本是RST没拉对、电源没到位、片选没工作。我见过不少同学用标准库例程先跑通再往HAL工程里挪。这个顺序没问题但移植时优先关注SPI底层收发函数是否和原来的例程匹配而不是一上来就调读卡流程。底层通信稳定了上层逻辑才有意义。3.4 等待刷卡结果尽量不用固定延时这是delay卡死最常见的源头。有人写的等待逻辑是HAL_Delay(10); if (ComIrqReg 0x01) { // 处理数据 }这种写法在单卡正常时偶尔能过但卡响应慢一点、天线匹配差一点数据就丢了。更坑的是把等待写成无条件死等while ((ComIrqReg 0x01) 0);一旦卡没有响应程序永远停在这里整个系统看起来就是卡死了。正确做法是每个等待循环都带超时退出超时返回错误码而不是原地死等。官方驱动的PCD_WaitForIrq就是这种思路用计数器判断超时调用方能明确知道是成功还是超时便于上层做重试。固定延时还有一个连带问题每次读卡前做全芯片软件复位的人不在少数想着复位一下更干净。RC522刚复位时天线场还没稳定第一次请求经常失败反而增加了无效等待。正常情况下初始化一次就够了后面直接走读卡流程。4. 一次完整刷卡寻卡、防冲撞、选卡、认证和读写扇区4.1 一张M1卡的访问流程不是读一下卡号那么简单很多精简例程把读卡号封装成一个函数看起来方便但遇到异常情况就不好排查。完整流程要按协议顺序走Request寻卡发0x26(REQA)或0x52(WUPA)卡返回ATQA比如0x4400表示MIFARE Classic 1KAnticoll防冲撞发0x93 0x20卡返回4字节UID和1字节校验BCCSelect选卡发0x93 0x70 UID BCC卡返回SAKAuth密钥认证对目标扇区做KeyA或KeyB认证Read/Write按块读写16字节数据每一步返回的状态都要检查。我调试时会把每一步打印到串口REQ OK、ANTI OK、SEL OK、AUTH OK这样刷不上卡时能看到卡在哪一步而不是一脸懵。4.2 关键指令到底发了什么指令实际发送内容返回REQA0x262字节ATQAWUPA0x522字节ATQAAnticoll0x93 0x204字节UID 1字节BCCSelect0x93 0x70 UID BCC1字节SAKAuth KeyA0x60 块号 密钥 UID成功则MFCrypto1On置位Auth KeyB0x61 块号 密钥 UID成功则MFCrypto1On置位Read0x30 块号16字节数据Write0xA0 块号 16字节数据4位应答Halt0x50卡进入HALT状态这里有个细节REQA和WUPA都用于寻卡但REQA只唤醒静止状态的卡WUPA连HALT状态的卡也能唤醒。多卡场景下WUPA的容错性更好后面讲排查时会提到。4.3 扇区、块和默认密钥M1卡1K容量是1KB分成16个扇区每个扇区4个块每块16字节。第0扇区的第0块由厂商写入UID和厂商数据出厂后一般不可改。每个扇区的第3块是尾块存放KeyA6字节、访问位4字节、KeyB6字节。所以实际可存用户数据的是每扇区的块0到块2一共16×3×16768字节。读写时用的是全局块号不是扇区号。扇区2的块0是全局第8块2×40扇区3的块1是全局第13块。这个换算搞错就会把数据写到意想不到的地方去。出厂默认密钥是FF FF FF FF FF FF。很多学校、公司的读卡器如果没有改过密钥拿一个RC522就能读这是M1卡广为人知的安全弱点。如果你的门禁系统只比对UID、不做密钥认证和卡内数据校验那和裸奔没有区别。产品化至少要改密钥不要用默认密钥更不要明文硬编码在生产固件里。4.4 一个可以直接抄的完整调用流程下面这段代码不是完整驱动但逻辑顺序就是实际协议顺序底层函数用任何一版驱动替换即可uint8_t sn[4], sak; uint8_t atqa[2]; uint8_t buf[16]; uint8_t keyA[6] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; uint8_t block 0x01; // 扇区0第1块 if (RC522_Request(0x52, atqa) OK) { // 寻卡WUPA方式 if (RC522_Anticoll(sn) OK) { // 防冲撞拿到4字节UID if (RC522_Select(sn, sak) OK) { // 选卡确认SAK if (RC522_Auth(0x60, block, keyA, sn) OK) { RC522_Read(block, buf); // 读16字节 for (uint8_t i 0; i 16; i) { printf(%02X , buf[i]); } } } } }要特别提醒UID字节序。不同例程打印UID的格式可能不一样同一个卡在这块板子上显示ABCD换一块板子变成DCBA多半是读取顺序的问题。一旦定了格式后面存白名单、上位机对比都按这个格式来别混用。5. 多卡同场与刷不上卡的排查实录5.1 先泼冷水RC522不是为同时刷多张卡设计的虽然14443A协议自带防冲撞机制但RC522模块天线范围小、射频场弱两张卡同时贴上去很容易撞包要么只识别出一张要么这次识别A下次识别B。如果做的是盘点一批卡这种需求RC522不合适趁早换大天线读卡器或者PN532。但实际项目里说的多卡识别更多是指三种情况多张卡同时在场时能稳定识别出目标卡而不是随机挑一张防冲撞乱码明明只有一张卡也读出错UID一台设备管理多张授权卡拿其中任意一张都能通过这三种情况RC522配合软件重试和超时控制是可以规避大部分问题的。5.2 一张授权卡都读不顺的排查实录说一个我实际遇到的案例。现象是两张授权卡叠在一起刷偶尔能开锁更多时候灯亮一下没反应单独刷任何一张都正常。如果只看现象容易怀疑硬件坏了但排查下来发现不是。第一步拿单张卡反复测排除天线和供电问题。第二步把读卡流程每一步打串口日志发现是Anticoll返回乱码。第三步看驱动代码Request用的是REQA两张卡都是静止状态都回了ATQA然后防冲撞时两卡的响应撞在一起Anticoll返回的数据校验不通过软件直接丢弃。问题根源是两卡同时进场的时序撞包。解决方式分两层软件层把REQA换成WUPA请求函数加重试机制第一次防冲撞失败不立即报错重新Anticoll几次成功概率大幅提升硬件层把模块天线位置调整让两张卡不要完全叠在一起人为错开进场时序关键点在于很多驱动把Anticoll失败当成硬错误直接返回。只要加上失败→短延时→重试的循环多卡场景的体验会明显改善。这个重试不是瞎等重试间隔取20~50ms既不会卡顿也能有效错开卡的随机响应时间。5.3 读卡慢半拍背后的超时问题另一个常见现象是卡放上去要等1~2秒才响应。多数原因是等待ComIrqReg的超时时间设太长。RC522的应答时间在毫秒级超时上限设几十ms足够没有必要设几百ms。如果每次读卡都要等很久挨个排查SPI时钟是不是太慢如果SPI分频系数设得特别大传输一条命令的时间会被拉长是否每次刷卡前都重新初始化如前面说的初始化后天线场没稳定第一下容易失败上次刷卡后有没有收尾读卡失败后要发Halt指令或者让卡回到IDLE状态否则下一轮Request可能收不到应答。这就是第一次刷卡成功、第二次要等更久的直接原因这种问题难定位是因为现象像卡变慢了实际是软件状态的遗留问题。建议读卡主流程用状态机而不是顺序调用每次刷卡结束都把状态归位。6. 从刷卡到产品白名单、继电器联动和几个教训6.1 让卡号驱动继电器开锁产品化的最小模型是读卡 → 查UID白名单 → 命中拉高GPIO → 驱动三极管 → 继电器动作 → 电磁锁开锁。继电器驱动有几个必须注意的点继电器线圈要并联续流二极管方向接反会打坏三极管。这个二极管的作用是吸收线圈断电时的反向电动势别省GPIO不要直接驱动继电器中间加S8050之类的NPN三极管或者用ULN2003电磁锁瞬间电流很大单独供电不要和STM32共用3.3V我在原型阶段被继电器打过一次续流二极管漏接了程序跑着跑着就复位。用示波器看电源继电器动作瞬间有大幅电压跌落。加了续流二极管、把电磁锁电源分开之后问题消失。这类电源干扰问题在带射频模块的项目里尤其要小心RC522对电源纹波本来就敏感继电器再一干扰读卡失败率直接飙升。6.2 白名单的存储与安全问题卡号少可以写死在代码里但要管理几十上百个卡号就得存Flash或外部EEPROM。STM32内部Flash的擦写寿命有限频繁增删卡号建议用外部I2C EEPROM比如AT24C02几毛钱一片256字节到2K容量可选。存卡号时建议存成ASCII十六进制字符串而不是裸二进制。好处是导出bin文件后直接能对照排查。用J-Flash或者STM32CubeProgrammer读出Flash内容一眼就能看到卡号列表不用写脚本解析。安全方面再强调一次不要只校验UID。M1卡的UID是明文的市面上不少工具能改部分卡的UID。只比对UID等于门禁没有锁。就算是毕设原型也建议至少改掉默认密钥并对卡内数据做简单校验。更严格的做法是对卡内数据做HMAC或AES加密配合发卡器写入动态数据但这些属于安防产品的范畴超出原型阶段的讨论。6.3 从驱动到工程的其他经验最后整理几条散落的经验标准库例程先跑通再移植到HAL/CubeMX工程。一上来就在HAL工程里手搓寄存器出问题你分不清是SPI配置错还是RC522时序错国产兼容RC522芯片越来越多VersionReg可能不是0x92。初始化代码不要写死必须等于0x92把0x91、0xBB这类值都当正常用读不到0xFF/0x00来判错Keil、VSCodeCMake都能正常开发STM32工程框架不影响RC522工作。移植驱动时重点看SPI底层收发函数对应关系这点比IDE选择重要得多收一个10μF和100nF电容进物料盒凡是带射频的模块都并上去能解决大量偶发问题调试串口多打日志把读卡状态机的每一步输出出来排错的效率比闷头试高很多最后再分享一个个人习惯RC522这类读卡模块的偶发问题十次里有七八次跟供电和天线布局有关而不是寄存器配置。遇到刷卡时好时坏先量电压、看天线环境再回头翻驱动代码。我自己的小门禁项目里给模块供电并了电容、把天线挪离金属支架之后读卡成功率从勉强能用提升到几乎百分百。这些硬件层面的细节往往比纠结代码更值得花时间。本文还有配套的精品资源点击获取