
简介这份资料面向电子、物联网及嵌入式方向的初学者与开发者围绕RC522射频识别芯片与51单片机的配合使用展开帮助读者打通13.56MHz非接触式通信的软硬件实现路径。压缩包为zip格式整体约91.47MB内容涵盖SPI接口配置、初始化命令框架、ISO/IEC 14443A协议解析、读写程序示例以及天线设计与电源管理等模块可支撑门禁、智能卡读写器等典型场景的开发练习。目前已有1218人学习下载说明其在入门与实战之间具备一定参考价值。读者可借助其中的示例代码与调试思路理解RC522与51单片机之间的数据交互流程掌握卡片检测、防冲突、数据收发及常见通信错误的排查方法并对照实际应用案例体会从硬件连接到程序落地的完整过程适合作为课程设计、毕业设计或自学RFID技术的实践素材。1. 从一块白板卡开始这套 RC522 加 51 单片机资料到底能跑出什么很多人第一次接触 RFID 是拿着白板卡在门禁前反复晃灯闪一下就没然后了。这套 RC522 基于 51 单片机的资料解决的就是这个“灯闪一下”之后的所有事读卡号、写数据、改密钥、做扣费逻辑全部能在最熟悉的 51 平台上跑通。它适合三类人——电子类专业要做课程设计的学生、想从零理解 SPI 时序的嵌入式新手、以及手里压着一堆 STC89C52 最小系统板想找点事做的开发者。整套资料围绕 RC522 读卡模块和 51 单片机展开核心是让你用最少的硬件成本把一张 M1 卡的完整读写流程吃透。你不需要先学 STM32也不需要 Linux 基础会点 C 语言、能看懂电路图就能把卡号读出来显示在串口或者 LCD 上。2. 硬件与通信链路RC522 和 51 之间到底怎么连2.1 模块引脚与最小系统接线RC522 模块引出 8 个脚真正跟 51 打交道的就 5 根SDA片选、SCK时钟、MOSI主出从入、MISO主入从出、RST复位。供电 3.3V这点必须先说清楚——很多翻车现场就是直接怼 5V模块当场发烫。51 单片机 IO 口输出 5V 电平跟 RC522 的 3.3V 逻辑之间需要处理常见做法是串 1K 电阻限流或者用电平转换模块最省事的方案是给 RC522 单独供 3.3V信号线串电阻。接线对应关系如下RC522 引脚51 单片机引脚说明SDAP2.0片选低电平有效SCKP2.1SPI 时钟由主机产生MOSIP2.2主机发、从机收MISOP2.3从机发、主机收RSTP2.4复位低电平复位VCC3.3V严禁接 5VGNDGND共地这套接法用的是软件模拟 SPI不是 51 硬件 SPI。原因很直接经典 51 大多没有 SPI 外设就算有寄存器配置也绕软件模拟反而时序可控、移植方便。代价是速度慢一点但 RC522 在 13.56MHz 载波下跟卡通信对 51 来说瓶颈根本不在 SPI 速率而在协议处理。2.2 软件模拟 SPI 的时序实现软件 SPI 的核心就是四件事片选拉低、按时序翻转 SCK、在时钟边沿读写数据、片选拉高。下面这段是资料里最基础的字节收发函数几乎每个 RC522 驱动都长这样// 软件模拟 SPI 读写一个字节mode 0空闲低上升沿采样 unsigned char RC522_SPI_RW(unsigned char byte) { unsigned char i, recv 0; for (i 0; i 8; i) { // 准备发送位最高位先出 if (byte 0x80) MOSI 1; else MOSI 0; byte 1; SCK 1; // 上升沿从机采样 recv 1; if (MISO) // 主机在上升沿后读从机数据 recv | 0x01; SCK 0; // 下降沿准备下一位 } return recv; }逻辑说明循环 8 次每次先摆好 MOSI 电平再拉高 SCK 让从机采样同时读 MISO 拼接收到的字节。参数byte是要发送的字节返回值是从机返回的字节。这里用的是 SPI 模式 0即空闲低电平、上升沿采样。如果你换到别的 51 芯片只要改MOSI、MISO、SCK这几个宏定义就能移植。注意SCK 拉高和读 MISO 的顺序不能反反了读出来全是 0xFF 或 0x00这是新手最容易踩的时序坑。2.3 寄存器读写与寻址方式RC522 内部有一堆寄存器操作它们靠的是地址字节的最高位区分读写最高位为 1 表示读为 0 表示写。地址低 6 位是寄存器编号中间还有一位用于地址对齐。资料里通常封装成两个函数// 写寄存器地址最高位清零 void RC522_WriteReg(unsigned char addr, unsigned char val) { RC522_CS_Low(); RC522_SPI_RW((addr 1) 0x7E); // 写操作地址左移一位 RC522_SPI_RW(val); RC522_CS_High(); } // 读寄存器地址最高位置一 unsigned char RC522_ReadReg(unsigned char addr) { unsigned char val; RC522_CS_Low(); RC522_SPI_RW(((addr 1) 0x7E) | 0x80); // 读操作 val RC522_SPI_RW(0x00); RC522_CS_High(); return val; }参数说明addr是寄存器地址比如0x01是 CommandReg0x04是 ComIrqReg。左移一位是因为 RC522 地址字节的最低位固定为 0读写位在最高位。写操作掩码0x7E保证最高位为 0读操作再或上0x80。片选在每次操作前后拉低拉高保证一次完整的事务边界。3. 卡片操作核心流程寻卡、防冲突、选卡、读写3.1 寻卡与防冲突的协议层实现RC522 跟 M1 卡通信走的是 ISO14443-A 协议流程固定先寻卡REQA/WUPA再防冲突拿卡号最后选卡。资料里一般把这几步封装成PcdRequest、PcdAnticoll、PcdSelect三个函数。寻卡命令用0x52或0x26前者寻未进入休眠的卡后者寻所有卡。// 寻卡req_code: 0x52 寻空闲卡0x26 寻所有卡 char PcdRequest(unsigned char req_code, unsigned char *pTagType) { char status; unsigned int unLen; ClearBitMask(Status2Reg, 0x08); // 关掉加密寻卡阶段不需要 WriteRawRC(BitFramingReg, 0x07); // 发送 7 位最后一位为 0 SetBitMask(TxControlReg, 0x03); // 打开天线 status PcdComMF522(PCD_TRANSCEIVE, req_code, 1, pTagType, unLen); if (status MI_OK) status (unLen 0x10) ? MI_OK : MI_ERR; // 返回两字节卡类型 return status; }逻辑说明ClearBitMask清掉 Status2Reg 的加密位因为寻卡阶段还没到认证。BitFramingReg设为 0x07 表示发送 7 位数据这是 REQA 命令的固定格式。PcdComMF522是底层收发封装负责把数据塞进 FIFO、触发收发、等中断、读回结果。参数pTagType返回两字节卡类型比如 0x4400 是 M1 卡。防冲突阶段用0x93命令通过位碰撞检测拿到 4 字节 UID选卡阶段再用0x93加上 UID 和 BCC 校验。3.2 M1 卡认证与块读写拿到 UID 之后要读写卡里的数据块必须先过认证。M1 卡每个扇区有独立的密钥 A 和密钥 B认证命令是0x60验证密钥 A或0x61验证密钥 B。认证通过后才能对扇区内块操作。// 验证密钥mode: 0x60 验 A 密钥0x61 验 B 密钥 char PcdAuthState(unsigned char mode, unsigned char addr, unsigned char *key, unsigned char *uid) { char status; unsigned int unLen; unsigned char ucComMF522Buf[12]; ucComMF522Buf[0] mode; ucComMF522Buf[1] addr; // 块地址 memcpy(ucComMF522Buf[2], key, 6); // 6 字节密钥 memcpy(ucComMF522Buf[8], uid, 4); // 4 字节卡号 status PcdComMF522(PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, unLen); if ((status ! MI_OK) || (!(ReadRawRC(Status2Reg) 0x08))) status MI_ERR; // 认证失败标志位没置起来 return status; }参数说明mode决定验 A 还是 B 密钥addr是要访问的块地址0 到 63key是 6 字节密钥uid是前面拿到的 4 字节卡号。认证成功后 Status2Reg 的 0x08 位会被硬件置起代码里必须检查这一位否则会出现“函数返回成功但读写还是失败”的玄学现象。读写块就简单了读用0x30写用0xA0都是 16 字节一个块。写之前建议先读一次确认权限写之后回读校验这套资料里通常带一个PcdWrite和一个PcdRead的完整封装。3.3 一个完整的读卡主循环把上面几步串起来主循环大概长这样void main(void) { unsigned char tagType[2]; unsigned char uid[4]; unsigned char data[16]; RC522_Init(); // 复位、配置定时器、天线 while (1) { if (PcdRequest(0x52, tagType) MI_OK) { // 寻卡 if (PcdAnticoll(uid) MI_OK) { // 防冲突拿 UID if (PcdSelect(uid) MI_OK) { // 选卡 // 默认密钥全 FF实际项目要改 if (PcdAuthState(0x60, 4, DefaultKey, uid) MI_OK) { PcdRead(4, data); // 读第 4 块 UART_SendArray(data, 16); // 串口输出 } } } } Delay_ms(200); // 别太频繁给卡留点时间 } }逻辑说明每 200ms 轮询一次寻卡成功才继续防冲突和选卡选卡成功再认证第 4 块第 1 扇区的数据块认证通过读出 16 字节从串口发出去。DefaultKey是出厂密钥0xFF 0xFF 0xFF 0xFF 0xFF 0xFF实际项目里必须改否则等于没锁。这个循环就是整套资料的最小可运行骨架LCD 显示、蜂鸣器、继电器控制都是在这个骨架上挂外设。4. 避坑与排查那些让你怀疑人生的常见问题4.1 寻卡一直返回失败现象串口一直打印寻卡失败天线附近放卡也没反应。原因通常有三个一是天线没打开TxControlReg的 0x03 位没置起来二是供电不足3.3V 电流不够导致模块反复复位三是 SPI 时序反了读回来的寄存器值全是 0xFF。解决先用读寄存器函数读VersionReg0x37正常应该返回 0x91 或 0x92读不到就说明 SPI 通信本身有问题先查接线和时序别急着调协议。4.2 认证通过但读写失败现象PcdAuthState返回 MI_OK但紧接着读块返回错误。原因认证函数里没检查 Status2Reg 的 0x08 位或者认证的块地址和读写的块地址不在同一个扇区。M1 卡认证是按扇区生效的认证了第 4 块只能操作第 4 到第 7 块。解决确认认证地址和读写地址在同一扇区并在认证后强制检查 0x08 位。4.3 写数据后卡被锁死现象写块操作之后卡再也读不出来。原因误写了扇区尾块块 3、7、11…把密钥或访问控制位改坏了。解决写操作前先判断块地址尾块绝对不要随便写如果非要改密钥先用密钥 A 认证按“新密钥 A 访问位 新密钥 B”的格式写写完立刻用新密钥验证。资料里一般会强调“先读后写、写后回读”就是防这个。4.4 串口输出乱码现象卡号读出来了但串口助手显示乱码。原因晶振频率和波特率不匹配或者串口初始化时定时器配置算错。51 常用 11.0592MHz 晶振就是为了串口波特率整数分频如果你用的是 12MHz 晶振9600 波特率会有误差。解决换 11.0592MHz 晶振或者用定时器 2 做波特率发生器部分增强型 51 支持。4.5 多张卡同时靠近时读卡不稳定现象两张卡叠在一起读出来的 UID 跳来跳去。原因防冲突算法没处理完整或者轮询间隔太短。解决防冲突阶段要完整走完位碰撞检测拿到完整 UID 再选卡轮询间隔加到 200ms 以上给卡足够的应答时间。实际项目里还会加“同一张卡连续读到 N 次才确认”的去抖逻辑。5. 二次开发改造从读卡号到做一个小型消费机5.1 数据区规划与金额扣减逻辑拿到这套资料跑通读卡之后最自然的改造方向就是做消费扣减。M1 卡一个扇区 4 个块块 0 存厂商数据不可写块 1、2 可以存余额和交易记录块 3 是密钥和访问控制。我一般会把块 1 规划成余额区前 4 字节存余额单位分小端后 4 字节存卡号后四位做校验剩下 8 字节留扩展。扣减流程必须遵循“先读后写、写后回读”// 扣减余额成功返回 0失败返回错误码 char DeductBalance(unsigned char *uid, unsigned int amount) { unsigned char block[16]; unsigned int balance; if (PcdAuthState(0x60, 4, DefaultKey, uid) ! MI_OK) return 1; // 认证失败 if (PcdRead(4, block) ! MI_OK) return 2; // 读失败 balance block[0] | (block[1] 8); // 小端拼余额 if (balance amount) return 3; // 余额不足 balance - amount; block[0] balance 0xFF; block[1] (balance 8) 0xFF; if (PcdWrite(4, block) ! MI_OK) return 4; // 写失败 if (PcdRead(4, block) ! MI_OK) return 5; // 回读失败 if ((block[0] | (block[1] 8)) ! balance) return 6; // 校验不一致 return 0; }参数说明uid是卡号amount是扣减金额单位分。返回码区分了认证、读、余额、写、回读、校验六个阶段方便定位问题。这里用默认密钥只是演示实际项目必须换成自定义密钥并且把密钥存在 51 的 Flash 或者外部 EEPROM 里不要硬编码在代码里。5.2 从裸机到状态机把读卡、显示、扣费串起来裸机主循环写消费机很容易写成面条代码我的习惯是拆成三个状态IDLE寻卡、AUTH认证读余额、DEAL扣减显示。每个状态只做一件事用switch驱动超时回到 IDLE。这样加 LCD 显示、加蜂鸣器、加继电器控制都不会互相干扰。验证方法也简单拿一张卡反复刷看余额是不是每次减固定值拿两张卡交替刷看会不会串卡。我一般还会在串口打印每一步的状态码出问题直接看日志比拿示波器量 SPI 快得多。从那以后我每次改 RC522 的代码都强制先跑一遍“读 VersionReg 确认通信、再寻卡、再认证、再读写”的四步验证少一步都不往下写。这套 51 加 RC522 的资料最大的价值不是代码本身而是让你把 ISO14443-A 的完整链路在资源最紧的平台上走一遍走通了再上 STM32 或者更复杂的平台心里就有底了。希望帮到你。本文还有配套的精品资源点击获取