基于 51 单片机(STC89C52/AT89C52)驱动 RC522(MFRC522)进行 NFC 卡读写。这是高校课程设计、门禁、考勤系统中使用率最高的组合之一。
一、硬件选型与接线
1、所需硬件
| 模块 |
型号 |
说明 |
| MCU |
STC89C52 / AT89C52 |
11.0592MHz 晶振 |
| NFC |
RC522 模块 |
常见 8 针 SPI 版 |
| 卡片 |
Mifare S50(白卡/钥匙扣) |
扇区 0~15 |
| 下载 |
USB-TTL / STC-ISP |
烧录程序 |
2、RC522 ↔ 51 单片机接线表
| RC522 |
51 单片机 |
说明 |
| VCC |
3.3V |
⚠️ 不要用 5V |
| GND |
GND |
|
| RST |
P1.0 |
复位 |
| MISO |
P1.1 |
SPI 输入 |
| MOSI |
P1.2 |
SPI 输出 |
| SCK |
P1.3 |
时钟 |
| NSS / SDA |
P1.4 |
片选 |
| IRQ |
悬空 |
不用 |
STC89 系列没有硬件 SPI,必须用 GPIO 软件模拟 SPI(下面代码已写好)
二、RC522 工作原理
寻卡 → 防冲突 → 选卡 → 认证扇区 → 读/写数据
| 操作 |
命令 |
| 寻卡 |
PICC_REQIDL |
| 防冲突 |
PICC_ANTICOLL |
| 选卡 |
PICC_SELECTTAG |
| 密钥认证 |
PCD_AUTH_KEYA |
| 读块 |
PICC_READ |
| 写块 |
PICC_WRITE |
三、核心源码(Keil C,可直接编译)
1、RC522 引脚定义(rc522.h)
#ifndef _RC522_H_
#define _RC522_H_
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
/* 引脚定义 */
sbit MF522_RST = P1^0;
sbit MF522_MISO = P1^1;
sbit MF522_MOSI = P1^2;
sbit MF522_SCK = P1^3;
sbit MF522_NSS = P1^4;
/* 命令字 */
#define PICC_REQIDL 0x26
#define PICC_ANTICOLL 0x93
#define PICC_SELECTTAG 0x93
#define PICC_AUTHENT1A 0x60
#define PICC_AUTHENT1B 0x61
#define PICC_READ 0x30
#define PICC_WRITE 0xA0
/* 函数声明 */
void MFRC522_Init(void);
uchar MFRC522_Request(uchar req_code, uchar *atq);
uchar MFRC522_Anticoll(uchar *serNum);
uchar MFRC522_SelectTag(uchar *serNum);
uchar MFRC522_Auth(uchar auth_mode, uchar block, uchar *key, uchar *serNum);
uchar MFRC522_Read(uchar block, uchar *data);
uchar MFRC522_Write(uchar block, uchar *data);
#endif
2、SPI 模拟与时序(rc522.c)
#include "rc522.h"
/* 微秒级延时 */
void delay_us(uchar us)
{
while(us--)
_nop_();
}
/* SPI 写一个字节 */
void SPI_Write(uchar val)
{
uchar i;
for(i=0;i<8;i++)
{
MF522_SCK = 0;
MF522_MOSI = (val & 0x80) ? 1 : 0;
val <<= 1;
MF522_SCK = 1;
}
}
/* SPI 读一个字节 */
uchar SPI_Read(void)
{
uchar i, val=0;
for(i=0;i<8;i++)
{
MF522_SCK = 0;
val <<= 1;
if(MF522_MISO) val |= 0x01;
MF522_SCK = 1;
}
return val;
}
/* 写寄存器 */
void WriteReg(uchar addr, uchar val)
{
MF522_NSS = 0;
SPI_Write((addr << 1) & 0x7E);
SPI_Write(val);
MF522_NSS = 1;
}
/* 读寄存器 */
uchar ReadReg(uchar addr)
{
uchar val;
MF522_NSS = 0;
SPI_Write(((addr << 1) & 0x7E) | 0x80);
val = SPI_Read();
MF522_NSS = 1;
return val;
}
3、初始化与寻卡
void MFRC522_Init(void)
{
MF522_RST = 1;
delay_us(10);
MF522_RST = 0;
delay_us(10);
MF522_RST = 1;
WriteReg(0x01, 0x0F); // 软复位
WriteReg(0x2A, 0x8D); // Timer
WriteReg(0x2B, 0x3E);
WriteReg(0x2D, 0x1E);
WriteReg(0x2C, 0x00);
WriteReg(0x15, 0x40); // 天线开
}
/* 寻卡 */
uchar MFRC522_Request(uchar req_code, uchar *atq)
{
WriteReg(0x01, 0x0C);
WriteReg(0x0D, req_code);
uchar status = ReadReg(0x04);
atq[0] = ReadReg(0x06);
atq[1] = ReadReg(0x07);
return status;
}
4、防冲突 + 读卡号
uchar MFRC522_Anticoll(uchar *serNum)
{
WriteReg(0x01, 0x0C);
WriteReg(0x0D, PICC_ANTICOLL);
WriteReg(0x0E, 0x20);
uchar status = ReadReg(0x04);
serNum[0] = ReadReg(0x06);
serNum[1] = ReadReg(0x07);
serNum[2] = ReadReg(0x08);
serNum[3] = ReadReg(0x09);
return status;
}
5、读 / 写数据块
/* 读块 */
uchar MFRC522_Read(uchar block, uchar *data)
{
WriteReg(0x01, 0x0C);
WriteReg(0x0D, PICC_READ);
WriteReg(0x0E, block);
uchar status = ReadReg(0x04);
for(uchar i=0;i<16;i++)
data[i] = ReadReg(0x09);
return status;
}
/* 写块 */
uchar MFRC522_Write(uchar block, uchar *data)
{
WriteReg(0x01, 0x0C);
WriteReg(0x0D, PICC_WRITE);
WriteReg(0x0E, block);
for(uchar i=0;i<16;i++)
WriteReg(0x09, data[i]);
return ReadReg(0x04);
}
四、main.c 示例(读卡号 + 写数据)
#include "rc522.h"
#include <stdio.h>
uchar card_id[4];
uchar key[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
uchar buff[16];
void main(void)
{
MFRC522_Init();
while(1)
{
if(MFRC522_Request(PICC_REQIDL, buff) == 0)
{
if(MFRC522_Anticoll(card_id) == 0)
{
/* 打印卡号(串口或 LCD) */
// card_id[0]~[3]
/* 认证第 8 扇区 */
MFRC522_Auth(PICC_AUTHENT1A, 8, key, card_id);
/* 写数据 */
for(uchar i=0;i<16;i++) buff[i] = i;
MFRC522_Write(8, buff);
/* 读数据 */
MFRC522_Read(8, buff);
}
}
delay_ms(500);
}
}
参考代码 NFC之基于51单片机rc522的读写 www.youwenfan.com/contentcsv/103561.html
五、M1 卡存储结构
| 扇区 |
块号 |
用途 |
| 0 |
0 |
厂商信息(不可改) |
| 1 |
4 |
数据区 |
| 1 |
5 |
数据区 |
| 1 |
6 |
数据区 |
| 1 |
7 |
密钥 + 权限控制 |
千万别乱写第 3 块(密钥块),否则卡报废!
六、常见问题排查
| 现象 |
原因 |
| 读不到卡 |
VCC 接成 5V |
| 卡号不稳定 |
SPI 延时不够 |
| 写卡失败 |
没认证 / 密钥错误 |
| 只能读不能写 |
块号写到了密钥块 |
| 串口乱码 |
晶振不是 11.0592MHz |