简介:这份资料面向使用51系列单片机驱动RC522射频识别模块的学习者与开发者,聚焦13.56MHz频段下ISO/IEC 14443A协议的非接触式读写实现,可用于门禁系统、智能卡读写器及物联网终端等场景的入门与进阶实践。压缩包为zip格式,整体约91.47MB,文件总数与类型明细上游暂未提供,从内容预览看应包含SPI接口配置、初始化命令框架、读写程序示例及天线调谐等模块化资料。已有1218人学习下载,具备一定参考热度。读者可从中获取RC522与51单片机通信的完整思路,包括引脚连接、SPI时序设置、防冲突与数据加密流程、错误处理与调试技巧,以及C语言或汇编示例代码,便于对照搭建实验环境、理解读写操作细节并排查通信故障,适合电子设计与嵌入式方向的学生及工程师参考。
1. 从一块白板到读卡成功:RC522 与 51 单片机的第一次握手
很多人第一次接触 RFID 是在实验室角落里翻出一块蓝色小板子,上面印着 RC522,旁边躺着一块最小系统板,芯片是 STC89C52。接线图看着简单,代码烧进去却死活读不出卡号,串口助手一片空白。这个项目就是冲着这个场景来的:它把 RC522 读卡模块和 51 单片机之间的 SPI 通信、寄存器配置、寻卡流程全部拆开,配好引脚定义和可编译的 Keil 工程,让新手能照着接线跑通,也让熟手能直接拿去做门禁、考勤、消费机这类二次开发。整套资料围绕一个目标:让读卡这件事从玄学变成可复现的流程。
2. 硬件与工程结构:先看清板子怎么连、代码怎么放
2.1 RC522 模块的引脚与 51 单片机的接线逻辑
RC522 模块对外一般引出 8 个脚:SDA(片选)、SCK、MOSI、MISO、IRQ、GND、RST、3.3V。51 单片机没有硬件 SPI,所以通信全靠软件模拟,引脚可以任意指定,但工程里通常固定一组方便接线。常见做法是:
| RC522 引脚 | 51 单片机引脚 | 作用 |
|---|---|---|
| SDA | P2.0 | 片选,低电平有效 |
| SCK | P2.1 | 时钟,软件翻转 |
| MOSI | P2.2 | 主机输出从机输入 |
| MISO | P2.3 | 主机输入从机输出 |
| RST | P2.4 | 复位,低电平复位 |
| GND | GND | 共地 |
| 3.3V | 3.3V | 供电 |
这里有个血泪经验:RC522 是 3.3V 器件,直接接 5V 供电大概率当场冒烟,或者时好时坏。51 单片机最小系统板如果只有 5V 输出,中间要加 AMS1117-3.3 降压模块。另外 MISO 是 RC522 输出到单片机,5V 单片机的高电平输入阈值通常能识别 3.3V,但反过来单片机输出 5V 到 RC522 的 MOSI、SCK、SDA 时,最好串 1k 电阻限流,否则长期跑会缩短模块寿命。
2.2 Keil 工程目录与文件职责
资料里的工程一般按功能拆成几个文件,方便移植:
RC522_51/ ├── main.c // 主循环:寻卡、防冲突、选卡、读卡号 ├── rc522.c // RC522 底层驱动:SPI 读写、寄存器操作 ├── rc522.h // 引脚宏定义、函数声明、寄存器地址 ├── uart.c // 串口初始化与打印,用于调试输出卡号 ├── uart.h └── delay.c // 软件延时,SPI 时序和复位时序都靠它main.c 只负责业务逻辑,rc522.c 封装所有和模块相关的操作,uart.c 把卡号打到串口助手。这样分层的好处是换单片机型号时,只需要改 rc522.h 里的引脚宏和 delay.c 里的延时,业务代码基本不动。
2.3 软件模拟 SPI 的时序要点
51 单片机没有 SPI 外设,所有时序靠 GPIO 翻转。RC522 支持 SPI 模式 0,也就是空闲时 SCK 为低,数据在 SCK 上升沿采样。一个字节的发送和接收是同时进行的,代码通常这样写:
// rc522.c 中软件 SPI 读写一个字节 unsigned char RC522_SPI_ReadWrite(unsigned char data) { unsigned char i, recv = 0; for (i = 0; i < 8; i++) { SCK = 0; // 时钟拉低 if (data & 0x80) // 先发最高位 MOSI = 1; else MOSI = 0; data <<= 1; delay_us(1); // 给从机准备时间 SCK = 1; // 上升沿,从机采样 delay_us(1); recv <<= 1; if (MISO) // 上升沿后读从机输出 recv |= 0x01; SCK = 0; // 时钟拉低,准备下一位 } return recv; }这段代码里 delay_us(1) 是关键,太快 RC522 反应不过来,太慢读卡速度下降。不同晶振频率下延时函数要重新校准,常见做法是用示波器看 SCK 频率,控制在 1MHz 以内比较稳。参数 data 是要发送的字节,返回值是同时接收到的字节,RC522 的寄存器读写都靠它。
3. 核心驱动拆解:寻卡、防冲突、选卡三步走
3.1 寄存器读写与复位流程
RC522 内部有一堆寄存器,操作方式分两种:写寄存器是“地址左移一位 + 写标志”,读寄存器是“地址左移一位 + 读标志”。代码里通常封装成两个函数:
// 写 RC522 寄存器 void RC522_WriteReg(unsigned char addr, unsigned char value) { SDA = 0; // 片选拉低 RC522_SPI_ReadWrite((addr << 1) & 0x7E); // 地址左移,最低位0表示写 RC522_SPI_ReadWrite(value); SDA = 1; // 片选拉高 } // 读 RC522 寄存器 unsigned char RC522_ReadReg(unsigned char addr) { unsigned char value; SDA = 0; RC522_SPI_ReadWrite(((addr << 1) & 0x7E) | 0x80); // 最低位1表示读 value = RC522_SPI_ReadWrite(0x00); SDA = 1; return value; }复位流程是上电后先拉低 RST 至少 100ns,再拉高,然后写 CommandReg 软复位,等内部振荡器起振。很多新手卡在这一步,读出来全是 0xFF 或 0x00,多半是复位没做对或者供电不稳。
3.2 寻卡命令与防冲突机制
寻卡用 PICC_REQALL 或 PICC_REQA 命令,通过 Transceive 发送。防冲突是当多张卡同时在感应区时,用 PICC_ANTICOLL 命令逐位比较卡号,最终选出一张。核心代码:
// 寻卡并读取卡号 unsigned char RC522_ReadCard(unsigned char *cardID) { unsigned char status; unsigned int unLen; unsigned char buffer[16]; // 1. 寻卡,请求所有卡进入感应区 status = PcdRequest(PICC_REQALL, buffer); if (status != MI_OK) return status; // 2. 防冲突,获取卡号 status = PcdAnticoll(buffer); if (status != MI_OK) return status; // 3. 选卡,为后续读写块做准备 status = PcdSelect(buffer); if (status != MI_OK) return status; // 4. 拷贝 4 字节卡号 cardID[0] = buffer[0]; cardID[1] = buffer[1]; cardID[2] = buffer[2]; cardID[3] = buffer[3]; return MI_OK; }PcdRequest 的第二个参数是返回的 ATQA 信息,PcdAnticoll 返回 4 字节卡号加 1 字节校验,PcdSelect 用卡号选中卡片。每一步都要判断返回值,任何一步失败都要重新寻卡,否则会卡死。
3.3 串口输出与调试观察
读到的卡号通过串口打印出来,方便确认。串口初始化一般用定时器 1 模式 2,波特率 9600 或 115200:
// 串口发送一个字节 void UART_SendByte(unsigned char byte) { SBUF = byte; while (!TI); // 等待发送完成 TI = 0; // 清除标志 } // 打印卡号 void PrintCardID(unsigned char *id) { unsigned char i; for (i = 0; i < 4; i++) { UART_SendByte(id[i]); } UART_SendByte('\r'); UART_SendByte('\n'); }串口助手设成 HEX 显示,每次刷卡应该看到 4 个字节变化。如果一直没输出,先确认串口线接对、波特率一致,再回头查 RC522 的供电和片选。
4. 避坑与常见问题:读不出卡时先查这几处
4.1 现象:串口一直无输出,模块发热
原因:RC522 接了 5V 供电,芯片过压。解决:立刻断电,改用 3.3V,检查降压模块输出是否稳定。如果模块已经发热,多半已经损坏,换一块重新接线。
4.2 现象:能读到卡但卡号每次都不一样
原因:防冲突没做完整,或者 PcdAnticoll 后没有正确选卡。解决:确认 PcdAnticoll 返回的 buffer 前 4 字节是卡号,第 5 字节是 BCC 校验,选卡时把这 5 字节都传给 PcdSelect。
4.3 现象:偶尔读得到,偶尔读不到
原因:SPI 延时太短,或者天线附近有金属干扰。解决:把 delay_us 调大一点,SCK 频率降到 500kHz 以下;天线远离金属,刷卡距离控制在 3cm 以内。
4.4 现象:编译报错“RC522.h 找不到”
原因:Keil 工程里头文件路径没加。解决:在 Options for Target → C/C++ → Include Paths 里把 rc522.h 所在目录加进去,或者把 .h 和 .c 放同一目录。
4.5 现象:换了一块 51 板子就读不出
原因:引脚定义和新板子对不上,或者新板子晶振频率不同导致延时不准。解决:改 rc522.h 里的引脚宏,重新校准 delay_us,用示波器确认 SCK 频率。
5. 二次开发改造:从读卡号到门禁考勤的落地思路
拿到能读卡号的工程之后,下一步通常是把它变成能用的东西。我一般会先做三件事:把卡号存进 EEPROM 做白名单、加一个继电器控制电磁锁、加一个按键做管理员模式。下面是一个简单的白名单比对逻辑:
// 假设 EEPROM 里存了 N 张卡的 4 字节卡号 unsigned char WhiteList[10][4] = { {0x12, 0x34, 0x56, 0x78}, {0x11, 0x22, 0x33, 0x44}, // ... }; // 比对卡号是否在白名单内 unsigned char CheckWhiteList(unsigned char *id) { unsigned char i, j; for (i = 0; i < 10; i++) { for (j = 0; j < 4; j++) { if (WhiteList[i][j] != id[j]) break; } if (j == 4) return 1; // 匹配成功 } return 0; // 不在白名单 }改造时注意几个点:卡号是 4 字节,存 EEPROM 要按页写,别频繁擦写;继电器控制锁的时候加续流二极管,否则线圈反向电动势会干扰单片机;管理员模式用按键进入,长按 3 秒再刷卡才能添加或删除卡号,避免误操作。
验证方法很简单:先烧录原工程确认能读卡,再逐步加白名单、加继电器、加按键,每加一个功能就用串口打印状态。不要一次全写完再调,否则出问题不知道是哪一层。
从那以后我每次拿到新的 RC522 模块,都强制先跑一遍最小读卡工程,确认供电、接线、时序都没问题,再往上叠业务代码。希望帮到你。
本文还有配套的精品资源,点击获取