51单片机驱动RC522读写M1卡:从SPI模拟到防碰撞全解析 📅 发布时间:2026/9/10 1:12:25 👁 浏览次数: 简介面向51单片机开发者的RC522 RFID读写方案资料包聚焦13.56MHz非接触式通信适用于门禁系统、智能卡读写器及物联网设备等场景也可作为电子设计竞赛与课程设计参考。压缩包为zip格式共0个文件上游未同步文件清单大小约91.47MB。内容覆盖SPI硬件接口连接与模式配置、RC522初始化命令框架、ISO/IEC 14443A协议流程、防冲突机制及数据加解密并围绕读写程序设计、错误处理与调试技巧展开配有C语言示例代码与工程注释同时涉及天线设计与调谐、电源管理优化等要点。已有1212人学习下载适合需要从零搭建RC522读卡器、理解RFID通信全流程并完成实际项目部署的嵌入式开发者。1. 先聊聊RC522和51单片机的适配性——为什么这个组合经久不衰RC522是NXP出的一块13.56MHz非接触式读写芯片支持ISO/IEC 14443A协议常见的M1卡S50、NTAG系列都能读。它本身不复杂但牵扯到模拟前端、射频发射、协议帧处理对很多刚接触单片机的人来说属于那种“照着例程能跑通但一改需求就抓瞎”的模块。至于51单片机说实话它在性能上没有任何优势——8位内核、12MHz晶振都算高的、没有硬件SPI的型号一抓一大把。但RC522和51这个组合在课程设计、电子竞赛、毕设里依然是常青树热度一直没下来。原因很现实51的资料多到泛滥RC522的例程在各大论坛、CSDN、B站上一搜一大把尤其江科大某知名教学UP主的51教程把这类外设讲得极其细致很多人的入门路径就是“51 RC522 LCD1602显示卡号”做完这个项目基本就把单片机外设通信的基本功练全了。所以这篇帖子我想从实际做的角度把RC522在51平台上的完整链路拆开讲清楚包括硬件连接的坑、SPI模拟时序怎么写、协议栈怎么搭、读卡流程每一步在做什么、多卡识别到底怎么回事最后附上我自己踩过的几个经典问题。适合正在做相关课设、或者刚学完51想搞个像样外设项目的人参考。先说一个标准配置MCU用STC89C52RC晶振11.0592MHzRC522模块用SPI接口接LCD1602用来显示卡号。这套配置网上例程最多出问题了也好找人问。2. 硬件接线不能只照着抄——三处最容易翻车的电路细节RC522模块市面上有两种常见形态一种是蓝色PCB的模块天线和芯片做在一块板子上引脚8个直接排针引出另一种是红板同样集成天线。绝大多数情况我们用SPI模式因为51读RC522内部寄存器最方便的就是SPI速度也够。2.1 SPI引脚映射与接线参考RC522模块上丝印一般标了SDA、SCK、MOSI、MISO、IRQ、GND、RST、VCC这8个脚其中IRQ是中断请求输出51裸机做轮询的话可以不接。下面是我常用的接线RC522引脚51单片机引脚说明SDAP1.2注意这里是SPI的片选不是I2C的数据脚SCKP1.3SPI时钟MOSIP1.1主机输出、从机输入MISOP1.0主机输入、从机输出IRQ悬空轮询模式下用不到RSTP1.4复位脚低电平有效VCC3.3V必须3.3V不能接5VGNDGND共地很多人第一次接就死在这几个问题上第一VCC接了5V芯片当场发烫或者直接烧掉。RC522的工作电压是2.5V到3.3V虽然有说法称模块板载了稳压芯片可以接5V但不同批次板子不一样最稳妥的做法是用3.3V供电。如果你手里的51开发板是5V系统需要把板子的3.3V引脚引出来给RC522单独供电。注意如果开发板上有AMS1117-3.3这种稳压芯片直接用它的输出引脚给RC522供电不要为了省事把VCC接到5V排针上。2.2 电平匹配问题3.3V模块和5V单片机怎么共存RC522的SPI引脚逻辑电平上限约等于VCC也就是3.3V左右。51单片机IO口输出高电平是5V直接用5V电平去驱动3.3V器件长期使用会有烧引脚的风险。最省事的办法SPI的MOSI、SCK、SDA、RST这几个51输出的信号线上各串一个1kΩ电阻分压兼限流。MISO方向是RC522输出到51RC522的3.3V高电平对51来说足够识别51的高电平门槛是2.0V左右可以直连。还有一种做法是加电平转换芯片比如TXS0108E、PCA9306但RC522的SPI速率本身不高串电阻完全够用没必要多花钱增加复杂度。2.3 天线匹配和读卡距离的关系RC522模块上天线线圈周围通常有配套的匹配电容这是出厂调好的。若读卡距离太近小于1cm甚至贴上去才能读不要一上来就怀疑程序先看看天线下方的PCB覆铜区域是否被螺丝柱、杜邦线挡住金属物体靠近天线会严重涡流损耗导致读卡失效。我遇到过一种情况模块用双面胶贴在金属外壳支架上结果读卡距离从标称的3-5cm缩水到几乎只能贴着读。把模块垫高或者挪开金属之后恢复正常。如果模块附近非要有金属至少保证天线下方3cm内没有大块金属平面。3. 软件层面没有硬件SPI就手搓一个顺便把协议栈的骨架搭起来51单片机尤其是STC89C52没有硬件SPI所以全部通信要靠GPIO模拟。其实模拟SPI很简单核心就是按照RC522的时序要求把电平拉高拉低。3.1 模拟SPI的读写字函数RC522工作在SPI从机模式通信格式很固定每次传输8位地址 8位数据。地址字节的最高位表示方向——高电平是读命令、低电平是写命令。注意地址字节是MSB先行也就是先发最高位。// 模拟SPI收发一个字节MSB先行 unsigned char RC522_Byte(unsigned char byte) { unsigned char i, temp 0; for (i 0; i 8; i) { // 先送最高位 if (byte 0x80) { MOSI 1; } else { MOSI 0; } byte 1; SCK 1; _nop_(); // 在时钟上升沿后采样这里读MISO temp 1; if (MISO) { temp | 0x01; } SCK 0; _nop_(); } return temp; } // 写寄存器地址和数据 void RC522_WriteReg(unsigned char addr, unsigned char value) { SDA 0; // 片选拉低 RC522_Byte((addr 1) 0x7E); // 写命令最高位为0 RC522_Byte(value); SDA 1; } // 读寄存器返回寄存器值 unsigned char RC522_ReadReg(unsigned char addr) { unsigned char value; SDA 0; RC522_Byte(((addr 1) 0x7E) | 0x80); // 读命令最高位为1 value RC522_Byte(0x00); SDA 1; return value; }这里有个细节很多初学的人不理解为什么地址要(addr 1) 0x7E再或上0x80因为RC522的地址只有7位有效寄存器地址本身是7位0x00-0x7FSPI传输时地址字节需要左移一位最低位留作方向标志。 0x7E是为了清掉本来就无效的第0位和最高位防止地址越界。3.2 初始化序列软复位、自检、打开天线RC522上电后必须先做初始化才能工作流程分四步硬复位RST引脚拉低至少10ms再拉高。软复位向CommandReg写入0x0F命令字Command为SoftReset。等待复位完成轮询CommandReg读到Command为0x00时说明复位完成。写配置寄存器、开天线关键寄存器包括TxControlReg打开TX1/TX2天线、TModeReg和TPrescalerReg设置定时器、RFCfgReg设置接收增益。执行自检可选项向CommandReg写入0x00进入自检模式读取BufferReg的值判断芯片是否正常。标准初始化代码大致如下void RC522_Init(void) { RST 0; delay_ms(10); RST 1; delay_ms(10); RC522_WriteReg(CommandReg, 0x0F); // SoftReset delay_ms(10); while (RC522_ReadReg(CommandReg) 0x0F); // 等待复位完成 // 定时器配置设置TModeReg为0x8DTPrescalerReg为0x3ETReloadReg为0x1F/0x30 RC522_WriteReg(TModeReg, 0x8D); RC522_WriteReg(TPrescalerReg, 0x3E); RC522_WriteReg(TReloadReg, 0x1F); RC522_WriteReg(TReloadRegH, 0x30); // 天线配置 RC522_WriteReg(TxAskReg, 0x40); // 强制100%ASK调制 RC522_WriteReg(TxControlReg, 0x83); // 开天线 RC522_WriteReg(RFCfgReg, 0x7F); // 接收增益48dB // 清除中断标志 RC522_WriteReg(Status2Reg, 0x00); RC522_WriteReg(ComIEnReg, 0x00); RC522_WriteReg(DivIEnReg, 0x00); RC522_WriteReg(ComIrqReg, 0x7F); RC522_WriteReg(ModeReg, 0x3D); }注意TModeReg、TPrescalerReg、TReloadReg这三组寄存器共同决定了RC522内部定标器的溢出时间。这个时间很关键它相当于“一次命令的最长等待时间”如果设得太短读卡过程中容易出现超时误报设得太长卡不在场时MCU要白等很久。一般例程里的0x8D/0x3E/0x1F/0x30这套组合实测溢出时间在25ms左右够用。4. 重点拆解读卡流程从寻卡到读扇区每一步在做什么数据又是怎么传的RC522的所有操作本质上是MCU往FIFO缓冲区里填写命令字节然后启动命令RC522会把响应数据写回FIFOMCU再读出来。理解这条链路比背代码重要得多。4.1 标准读卡五步协议链以读取M1卡数据为例完整流程是寻卡 → 防冲突 → 选卡 → 三次认证 → 读块。第一步寻卡Request/PICC。MCU向FIFO写入命令字节0x26Request mode7字节帧或0x52Wake-up mode7字节帧然后执行PCD_TRANSCEIVE命令命令码0x0ERC522会发送RF场信号等待卡片应答。卡片返回的ATQAAnswer To Request是两个字节比如M1 S50返回0x04 0x00。第二步防冲突Anticollision。当一张以上卡片同时进入射频场时M1卡的防碰撞机制是以UID的每一位为单位进行逐位仲裁。MCU发送0x93级联级1、0x20防碰撞命令卡片返回4字节UID实际是完整UID的前4字节加上BCC校验字节。第三歩选卡Select。把上一步拿到的完整4字节UID连同BCC发回去命令是0x93、0x70卡片会返回1字节SAKSelect Acknowledge用于告知卡的类型。这里如果返回0x08说明这是完整UID不需要级联第二级。第四步三次认证Authentication。M1卡的内存分成16个扇区每个扇区有4个块要读某一块数据必须先对所在扇区进行密码认证。认证命令是0x60Key A或0x61Key B发送时把扇区号、块地址、6字节密钥一起发给卡片。M1出厂默认密钥是6个0xFF这也是课程设计里最常用的密钥。第五步读块Read。认证通过后发送0x30 块地址卡片返回16字节数据。写块类似发送0xA0 块地址然后跟16字节数据再接CRC校验RC522硬件会自动计算不用MCU管。4.2 命令帧在51代码里的落地写法上面五步每一步都要填FIFO、发命令、查询中断标志、读结果。以寻卡为例unsigned char RC522_Request(unsigned char reqMode, unsigned char *tagType) { unsigned char status; RC522_WriteReg(Status2Reg, 0x00); // 清状态 RC522_WriteReg(ComIrqReg, 0x7F); // 清中断 RC522_WriteReg(FIFOLevelReg, 0x80); // 清FIFO // 把寻卡命令写入FIFO RC522_WriteReg(FIFODataReg, reqMode); // 执行Transceive命令 RC522_WriteReg(CommandReg, PCD_TRANSCEIVE); // 置位BitFramingReg表示最后发送的字节只有7位有效 RC522_WriteReg(BitFramingReg, 0x07); // 等待完成轮询ComIrqReg的RxIRq位和TimerIRq位 status RC522_WaitForResponse(); // 读取返回数据长度一般是2字节 *tagType RC522_ReadReg(FIFODataReg); RC522_ReadReg(FIFODataReg); return status; }RC522_WaitForResponse是轮询超时函数核心逻辑是循环检测ComIrqReg寄存器的bit0RxIRq接收完成和bit4TimerIRq定时器溢出。如果读到RxIRq置位说明卡片有响应如果读到TimerIRq置位就说明超时直接返回MI_ERR。一个常被忽略的细节是BitFramingReg 0x07这行。寻卡命令reqMode0x26或0x52只有7位有效而FIFO本身按字节存储通过BitFramingReg告诉RC522“最后一个字节只发7位”这样卡片才能正确识别这是一次完整的7位命令帧。很多移植了例程却始终寻不到卡的人多半是把这行删了或注释掉了。4.3 读UID和读扇区的区别搜RC522资料时经常会看到“读卡号”和“读卡数据”两种说法这是两码事一定要分清楚。读UID不需要认证。UID存在于每张卡的只读区第0扇区第0块的前4字节MCU只要做寻卡、防冲突、选卡三步就能拿到。这也是为什么“显示卡号”的例程里看不到认证代码。读扇区数据就要走完五步在第0块里还包含厂商信息自己往扇区写入的数据也存在块1、块2这种数据块里。所以如果你看到例程里有0x60开头的数组那是认证命令看到0x30开头的是读块命令看到0xA0开头的是写块命令。提醒M1卡的默认密钥是公开常识6个0xFF课设、毕设用默认密钥读写自己的卡完全没问题。但不要尝试对陌生人的卡做暴力枚举密钥或绕过认证的操作那涉及安全问题不在本文讨论范围内。5. 多卡识别的真相防碰撞算法到底是什么51上又是怎么实现的“RC522 多卡识别”是搜索热度很高的词。很多人以为多卡识别就是RC522能同时读好几张卡的数据其实不是。RFID系统在半双工通信下同一时刻只能和一张卡完成数据交换。所谓“多卡识别”指的是多张卡同时进场时系统能逐个把每张卡的UID读出来中间不发生冲突或漏读。5.1 位级防碰撞的工作过程M1卡的防碰撞是一种典型的二进制搜索算法RC522和卡片的交互过程是这样的第一轮MCU发送防碰撞命令0x93、0x20场上所有卡同时响应把自己UID的每一位都发出来。如果两张卡的UID在某个bit位上一个发0、一个发1这个位就会产生冲突RC522检测到冲突后会把该位置1MCU从返回的40位数据32位UID 8位BCC里能知道冲突发生在哪个bit。第二轮MCU在冲突位处强制指定一个分支比如要求UID的第N位必须是0再发一次防碰撞命令。这样只有UID符合要求的那张卡会继续响应另一张卡会进入静默状态于是成功分离出一张卡。第三轮MCU对这张卡的UID执行选卡操作完成读取后对它执行Halt命令0x50 0x00让它退场。然后再对剩下的卡重复上述过程。在51上做这件事的最大开销是时间。M1卡的防碰撞是一个逐位逼近的过程UID有32位最坏情况下要做33轮32位加最后确认。每轮通信还要等RC522的RF收发时间。实测在11.0592MHz的STC89C52上完整识别两张卡大概需要50-80ms这个速度对门禁、考勤这种场景完全够用。5.2 轮询切换和一卡一读的工程实现实际工程中更常见的需求是读卡器不停地扫任何一张卡靠近都能读到UID而不是一次性把场内所有卡全读一遍。做法是主循环里不断执行“寻卡→防冲突→选卡→Halt”这四步每次循环只处理一张卡。寻卡用0x26Request只响应进入场内的卡还是0x52Wake-up可以唤醒处于Halt状态的卡取决于具体场景。场景寻卡命令原因门禁人走卡走0x26卡移出后重新进入时才触发多卡轮流刷比如消费机0x52能唤醒已Halt的卡连续刷不同卡更顺畅一卡一读防重复刷0x26 做标记读完之后记录UID同一张卡短时间内不重复处理如果你要做“一卡一读”的项目比如刷一次卡亮一次灯同一张卡不能连续亮最简单的做法是维护一个上次UID的变量每次读到新UID后和上次比对相同就忽略不同才执行动作。不要试图在RC522层面彻底禁止卡重复响应——芯片本身的机制决定了卡只要在场上就能被反复读到业务逻辑应该在单片机层面解决。6. 我自己踩过的坑和几条能让你少走弯路的验证方法这几年在不少群和论坛里看到新手问RC522的问题我自己早期也翻过车挑几个出现频率最高的集中说一下。6.1 卡片完全读不到程序卡死在等待响应排查顺序永远是先硬件后软件。第一步量RC522的VCC是不是3.3V用万用表测模块上的电压。第二步示波器看SCK引脚有没有时钟波形没有波形说明模拟SPI代码没跑起来检查IO口初始化有没有把对应引脚设为准双向口。第三步看SDA片选信号很多模拟SPI的例程里SDA是P1.2但有些模块丝印上写的是SDA其实是SPI的NSS片选不要和I2C的SDA搞混。排除硬件问题后把初始化代码里的自检打开。RC522有个自检功能向CommandReg写入0x00等待自检完成后读BufferReg如果返回值是0x00 0x22 0x35 0x44 0x36 0x1F 0x35不同版本芯片略有差异说明芯片和MCU之间的SPI通信是通的。这个测试比盲改代码高效得多。6.2 读卡距离只有1cm怎么调都上不去先确认天线附近有没有金属再看供电电流。RC522发射功率不小如果供电线又细又长压降会导致天线输出功率不足。把杜邦线换成粗线或者直接飞短线读卡距离往往能明显改善。如果还不够可以小心地调整RFCfgReg的接收增益。默认0x7F对应约48dB可以试着改成0x9F约54dB或0xBF约60dB但增益太高会提高误码率导致读卡反而变慢或不稳定。调这个寄存器没有标准答案要根据实际环境一点一点试。6.3 卡片能读到UID但读扇区数据时报认证错误这个几乎是模块默认密钥和卡不匹配导致的。很多新买的白卡出厂默认密钥是6个0xFF但如果有人之前用别的工具改过卡密钥默认密钥就失效了。解决办法是换一张没改过的M1卡测试或者用已知密钥的卡验证代码。不要怀疑认证命令写错——0x60 扇区号 块地址 6字节密钥这个组合反复核对几遍基本不会有问题。另外特别提醒「扇区号」和「块地址」不是一个概念。M1卡第0扇区包含块0到块3扇区号0对应的块地址是0、1、2、3。如果你想读第2扇区的块0发送的块地址应该是2×408。这块搞错的话认证时明明选了扇区2读块却用了块2操作自然会失败。6.4 一个省事的调试思路先读UID再做别的如果你第一次在51上驱动RC522不要上来就写完整的多扇区读写程序。建议分三步走先实现寻卡和防冲突能用串口或者LCD输出4字节UID这一步通了说明SPI和基础命令都对。再加上Halt命令让一张卡只能被读一次养成“读一次停一次”的习惯。最后加认证和读写块用一个已知默认密钥的新卡测试。这样每一层都有明确的验证标准出问题了能快速缩小范围。不要指望一步到位写完所有代码再调RC522这类射频外设的定位手段本来就不像I2C传感器那么直观逐层验证是最省时间的路径。说了这么多RC522这套东西真跑起来之后你会觉得其实并不神秘——SPI模拟通信打通了剩下的就是对着数据手册填寄存器、发命令、读FIFO和操作I2C、UART外设没有本质区别。真正有价值的经验反而是那些手册上不会写的部分比如电平匹配、天线周围的金属干扰、M1卡的防碰撞行为这些东西只有亲手搭过一套硬件、真实卡在手里一张一张试过才会理解。希望这篇帖子能让你少走点弯路顺利把这颗小芯片跑起来。本文还有配套的精品资源点击获取