一、RC522说明
1.RC522 的内部结构仅包含专用功能模块(模拟电路:调制解调、天线驱动;数字电路:FIFO 缓冲区、CRC 协处理器、定时器、中断系统、寄存器组),无独立 CPU,也无程序存储器,无法自主运行用户编写的程序,仅能通过外部主机(如 STM32)发送的 “寄存器配置 + 命令” 被动响应操作(如寻卡、读写)。手册 15 章(命令集)进一步说明:RC522 的所有功能(如复位、发送数据、认证)均需外部主机通过 SPI/I2C/UART 接口发送特定命令(如PCD_RESETPHASE、PCD_TRANSCEIVE)才能触发,自身无法主动发起任何操作。
也就是说RC522也只是一个从机模块,任何流程性操作都是MCU安排的,但是因为他自己又具备一定的自主功能,所以需要在初始化的时候配置一些寄存器。而初始化时的软件复位会导致上一次使用的参数失效,它自身默认的参数又不一定符合工程要求,因此需要在初始化复位后,重新配置他的定时器、中断、适配协议等寄存器,才能使其具有完整的收发控制、通信等功能
2.在后续操作中,均需要MCU向RC522发送命令。如下文配置流程的那些步骤,都需要MCU发送对应的命令。同时在数据收发校验中,RC522会有标志位存在内部寄存器中,需要MCU读出来判断当前的工作状态(也就是是否成功)。
二、程序配置流程:RC522芯片初始化、相关基础寄存器配置——寻卡——防冲突&获取UID——选择卡片——认证扇区——读写数据——休眠。
(一)RC522芯片初始化:通过SPI等通信协议,配置RC522内部寄存器的一些参数。如设置工作模式、传输速率、定时器、天线驱动、RFID通信协议。
0.总配置
// RC522 初始化入口:配置 GPIO、复位、天线与 ISO 类型
void RC522_Init(void)
{
RC522_GPIO_Init(); // 初始化 GPIO 引脚
RC522_Reset(); // 复位并基础初始化寄存器
RC522_AntennaOff(); // 先关闭天线(清理状态)
RC522_AntennaOn(); // 再打开天线(确保已开启)
RC522_ConfigISOType('A'); // 配置为 ISO14443-A 并打开相关设置
}
1.RC522内部基本寄存器配置
// 復位 RC522 芯片并进行基础寄存器初始化
int8_t RC522_Reset(void)
{
RC522_RST_HIGH(); // 先拉高复位引脚
Delay_ms(1); // 短延时
RC522_RST_LOW(); // 拉低复位
Delay_ms(1); // 短延时
RC522_RST_HIGH(); // 再拉高复位以完成复位序列
Delay_ms(1); // 等待芯片稳定
RC522_WriteReg(CommandReg, PCD_RESETPHASE); // 发送软复位命令
Delay_ms(1); // 等待复位完成
// 基本模式与定时器/发送参数设置(常用初始化值)
RC522_WriteReg(ModeReg, 0x3D); // 模式寄存器设置。厂商推荐的默认值
RC522_WriteReg(TReloadRegL, 30); // 定时器重载低字节
RC522_WriteReg(TReloadRegH, 0); // 定时器重载高字节
RC522_WriteReg(TModeReg, 0x8D); // 计时模式寄存器
RC522_WriteReg(TPrescalerReg, 0x3E); // 预分频寄存器
RC522_WriteReg(TxAutoReg, 0x40); // 自动 Tx 设置(脉冲宽度等)
return MI_OK; // 返回成功
}
2.先关闭天线清理旧状态,再开启天线。
// 打开天线(设置驱动位)
void RC522_AntennaOn(void)
{
uint8_t i;
i = RC522_ReadReg(TxControlReg); // 读取当前天线控制寄存器
if(!(i & 0x03)) // 若低两位未置位(天线未开启)
{
RC522_SetBitMask(TxControlReg, 0x03); // 打开天线驱动位(置 0/1 位)
}
}
// 关闭天线
void RC522_AntennaOff(void)
{
RC522_ClearBitMask(TxControlReg, 0x03); // 清除天线控制低两位以关闭天线
}
3.配置RC522内部天线相关寄存器(选择读写器和IC卡的通信协议)
// 配置 ISO 类型(当前实现仅支持 ISO14443-A)
// 参数: type - 'A' 表示 ISO14443_A
// 返回: MI_OK / MI_ERR
int8_t RC522_ConfigISOType(uint8_t type)
{
if(type == 'A') // ISO14443_A
{
RC522_ClearBitMask(Status2Reg, 0x08); // 清除 MIFARE 相关状态位
RC522_WriteReg(ModeReg, 0x3D); // 设置模式寄存器
RC522_WriteReg(RxSelReg, 0x86); // 接收选择寄存器设置
RC522_WriteReg(RFCfgReg, 0x7F); // 射频配置
RC522_WriteReg(TReloadRegL, 30); // 定时器重载低字节
RC522_WriteReg(TReloadRegH, 0); // 定时器重载高字节
RC522_WriteReg(TModeReg, 0x8D); // 计时模式
RC522_WriteReg(TPrescalerReg, 0x3E); // 预分频
Delay_ms(1); // 短延时
RC522_AntennaOn(); // 打开天线
}
else
{
return MI_ERR; // 不支持其他类型则返回错误
}
return MI_OK; // 成功返回
}
(二)寻卡/卡片探测:RC522向天线辐射范围内发送寻卡指令。处于场中的卡品被激活,并恢复一个ATQA(应答请求)信号
1.寻卡(找到当前相同协议的、空闲状态的卡。)
和读写器协议不同的IC卡的是找不到的。空闲是指读写器发出射频信号后能给出回复的卡。寻卡阶段只是得知有没有空闲卡,没有进行获取IC卡信息的操作。
/* 寻卡(请求卡片类型)
// 参数: req_code - PICC_REQIDL / PICC_REQALL 等
PICC_REQIDL:用于检测 “空闲状态” 的卡片(未进入休眠的卡片);
PICC_REQALL:用于检测所有状态的卡片(包括已激活但未休眠的卡片);
// pTagType - 输出 2 字节,表示卡类型
*/ 返回: MI_OK / MI_ERR
int8_t RC522_Request(uint8_t req_code, uint8_t *pTagType)
{
int8_t status; // 返回状态
uint16_t unLen; // 输出位长度(位)
uint8_t ucComMF522Buf[MAXRLEN]; // 通用缓冲区
RC522_ClearBitMask(Status2Reg, 0x08); // 清除 MIFARE 状态位
RC522_WriteReg(BitFramingReg, 0x07); // 设置发送 7 位(请求帧长度)
RC522_SetBitMask(TxControlReg, 0x03); // 打开天线驱动(如果未打开)
ucComMF522Buf[0] = req_code; // 请求代码放入缓冲区
status = RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, &unLen); // 发送并接收
if((status == MI_OK) && (unLen == 0x10)) // 期望返回 16 位(类型码)
{
pTagType[0] = ucComMF522Buf[0]; // 保存类型码字节0
pTagType[1] = ucComMF522Buf[1]; // 保存类型码字节1
}
else
{
status = MI_ERR; // 未按预期返回则视为错误
}
return status; // 返回结果
}
(三)获取当前响应卡片的卡号,同时防冲突(也就是多个卡片响应时选择出其中一张卡并且识别出它的卡号)(当前防冲突的代码处理不全面,暂不适用多卡识别)
防冲突只是获得IC卡的UID,并没有直接进行读写相关的通信操作,需要在后面的选卡操作中真正选上。
防冲突的原理是
“当只有一张卡片响应是,所有寄存器收到的bit位数据都是稳定的值;当多张卡片响应时,个别bit位会出现0、1的交替接收,不稳定,就说明收到了不止一张卡片的信号”
发现冲突后的选卡原理是
“在已有的但不冲突的序列作为开头,之后从冲突位开始用0/1进行枚举尝试选中其中一张卡”
// 防冲突,获取卡序列号(4 字节)
// 参数: pSnr - 输出序列号缓冲(至少 4 字节)
// 返回: MI_OK / MI_ERR
int8_t RC522_Anticoll(uint8_t *pSnr)
{
int8_t status;
uint8_t i, snr_check = 0; // snr_check 用于计算校验字节
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
RC522_ClearBitMask(Status2Reg, 0x08); // 清除 MIFARE 状态位
RC522_WriteReg(BitFramingReg, 0x00); // 字节对齐(发送整字节)
RC522_ClearBitMask(CollReg, 0x80); // 清除碰撞标志位
ucComMF522Buf[0] = PICC_ANTICOLL1; // 防冲突命令
ucComMF522Buf[1] = 0x20; // 指定防冲突命令与长度(NVB = 0x20)
status = RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, &unLen); // 发送并接收
if(status == MI_OK)
{
for(i = 0; i < 4; i++) // 复制 4 字节序列号并计算异或校验
{
pSnr[i] = ucComMF522Buf[i]; // 保存序列号字节
snr_check ^= ucComMF522Buf[i]; // 异或累积
}
// 校验位(第5字节)是否匹配
if(snr_check != ucComMF522Buf[i]) // ucComMF522Buf[4] 为校验字节
{
status = MI_ERR; // 校验失败标记错误
}
}
RC522_SetBitMask(CollReg, 0x80); // 恢复碰撞寄存器原始位
return status; // 返回结果
}
(四)选择卡片
根据防冲突阶段获得的卡片UID,选择该卡进行通讯。
// 选择卡片(基于序列号)
// 参数: pSnr - 4 字节序列号
// 返回: MI_OK / MI_ERR
int8_t RC522_Select(uint8_t *pSnr)
{
int8_t status;
uint8_t i;
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_ANTICOLL1; // SELECT(第一级命令)标识
ucComMF522Buf[1] = 0x70; // SELECT 命令 + NVB(指示发送 7 字节含校验)
ucComMF522Buf[6] = 0; // 累加校验初始化
for(i = 0; i < 4; i++)
{
ucComMF522Buf[i + 2] = pSnr[i]; // 将序列号字节放入请求缓冲
ucComMF522Buf[6] ^= pSnr[i]; // 计算校验(异或)
}
RC522_CalculateCRC(ucComMF522Buf, 7, &ucComMF522Buf[7]); // 计算并追加 CRC 到缓冲区尾部
RC522_ClearBitMask(Status2Reg, 0x08); // 清除状态位
status = RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, &unLen); // 发送 SELECT 请求
if((status == MI_OK) && (unLen == 0x18)) // 期望返回 24 位(ACK/SAK 长度)
{
status = MI_OK; // 选卡成功
}
else
{
status = MI_ERR; // 否则选卡失败
}
return status; // 返回结果
}
(五)认证
不同的卡片和厂商对扇区和块区的大小分配是不同的。RC522是按 MIFARE Classic内存组织和访问控制规范实现的,其内容如下
扇区:4个块区组成一个扇区
块区:16Byte组成的一个最小读写单位。
在对卡片今昔读写等敏感操作前,必须进行三次相互认证。RC522使用存储在它里面的密钥和卡的UID,与卡片内部存储的密钥进行复杂的加密认证。
// 验证密钥(认证一个扇区块)
// 参数: auth_mode - AUTHENT_A/AUTHENT_B, addr - 块地址, pKey - 6 字节密钥, pSnr - 卡序列号(4 字节)
// 返回: MI_OK / MI_ERR
int8_t RC522_AuthState(uint8_t auth_mode, uint8_t addr, uint8_t *pKey, uint8_t *pSnr)
{
int8_t status;
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = auth_mode; // 认证模式(A/B)
ucComMF522Buf[1] = addr; // 块地址
memcpy(&ucComMF522Buf[2], pKey, 6); // 复制 6 字节密钥到缓冲
memcpy(&ucComMF522Buf[8], pSnr, 4); // 复制卡序列号到缓冲
status = RC522_Command(PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, &unLen); // 发送认证命令
// 检查认证后 Status2Reg 的 MIFARE bit(0x08)
if((status != MI_OK) || (!(RC522_ReadReg(Status2Reg) & 0x08)))
{
status = MI_ERR; // 若状态不正确则认证失败
}
return status; // 返回认证结果
}
/**
* @brief 尝试认证指定区块
* @param snr: 卡片序列号
* @param block_addr: 区块地址
* @retval 认证状态 MI_OK 或 MI_ERR
*/
int8_t TryAuthBlock(uint8_t *snr, uint8_t block_addr)
{
printf("正在认证区块 %d: ", block_addr);
int8_t status = RC522_AuthState(PICC_AUTHENT1A, block_addr, default_key, snr);
if(status == MI_OK)
{
printf("成功!\r\n");
// 读取当前数据
uint8_t read_data[16];
if(RC522_Read(block_addr, read_data) == MI_OK)
{
printf("区块 %d 当前数据: ", block_addr);
for(int i = 0; i < 16; i++)
{
printf("%02X ", read_data[i]);
}
printf("\r\n");
}
return MI_OK;
}
else
{
printf("失败\r\n");
return MI_ERR;
}
}
(六)数据操作:读写卡片
(七)休眠:RC522向卡片发送休眠指令,卡片进入低功耗的休眠状态,不再响应寻卡指令,知道离开后重新进入射频场。
二、RC522代码
RC522.c
#include "rc522.h"
#include "delay.h"
#include <string.h>
// SPI延时(短延时,确保时序)
static void RC522_Delay(void)
{
volatile uint8_t i;
for(i = 0; i < 10; i++);
}
// SPI读取一个字节(软件模拟 SPI 模式,从 MOSI/MISO 时序看)
static uint8_t RC522_SPI_ReadByte(void)
{
uint8_t i;
uint8_t data = 0;
for(i = 0; i < 8; i++)
{
data <<= 1;
RC522_SCK_LOW(); // 时钟低
RC522_Delay();
if(RC522_MISO_READ()) // 读 MISO 电平
{
data |= 0x01;
}
RC522_SCK_HIGH(); // 时钟上升采样
RC522_Delay();
}
return data;
}
// SPI写入一个字节(软件模拟,将数据按位输出到 MOSI)
static void RC522_SPI_WriteByte(uint8_t data)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
RC522_SCK_LOW();
RC522_Delay();
if(data & 0x80)
{
RC522_MOSI_HIGH();
}
else
{
RC522_MOSI_LOW();
}
data <<= 1;
RC522_Delay();
RC522_SCK_HIGH();
RC522_Delay();
}
}
// GPIO 初始化:配置 CS/SCK/MOSI/MISO/RST 引脚模式与初始电平
void RC522_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能端口时钟(示例为 GPIOA,根据硬件修改)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// CS、SCK、MOSI、RST - 推挽输出
GPIO_InitStructure.GPIO_Pin = RC522_CS_PIN | RC522_SCK_PIN | RC522_MOSI_PIN | RC522_RST_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RC522_CS_PORT, &GPIO_InitStructure);
// MISO - 浮空输入
GPIO_InitStructure.GPIO_Pin = RC522_MISO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(RC522_MISO_PORT, &GPIO_InitStructure);
// 初始电平(片选高,时钟高,MOSI 高,复位高)
RC522_CS_HIGH();
RC522_SCK_HIGH();
RC522_MOSI_HIGH();
RC522_RST_HIGH();
}
// 读取 RC522 寄存器
// 参数: addr - 寄存器地址
// 返回: 寄存器值
uint8_t RC522_ReadReg(uint8_t addr)
{
uint8_t value;
uint8_t addr_byte;
RC522_CS_LOW();
// 地址字节:7位地址 + 读位 (MSB=1),并左移1位(RC522 通信格式)
addr_byte = ((addr << 1) & 0x7E) | 0x80;
RC522_SPI_WriteByte(addr_byte);
value = RC522_SPI_ReadByte();
RC522_CS_HIGH();
return value;
}
// 写 RC522 寄存器
// 参数: addr - 寄存器地址, value - 要写入的值
void RC522_WriteReg(uint8_t addr, uint8_t value)
{
uint8_t addr_byte;
RC522_CS_LOW();
// 写操作:MSB=0
addr_byte = ((addr << 1) & 0x7E);
RC522_SPI_WriteByte(addr_byte);
RC522_SPI_WriteByte(value);
RC522_CS_HIGH();
}
// 设置寄存器位掩码(将 mask 指定的位置1)
void RC522_SetBitMask(uint8_t reg, uint8_t mask)
{
uint8_t tmp;
tmp = RC522_ReadReg(reg);
RC522_WriteReg(reg, tmp | mask);
}
// 清除寄存器位掩码(将 mask 指定的位置0)
void RC522_ClearBitMask(uint8_t reg, uint8_t mask)
{
uint8_t tmp;
tmp = RC522_ReadReg(reg);
RC522_WriteReg(reg, tmp & ~mask);
}
// 使用内部 CRC 计算数据的 CRC 值
// 参数: pIndata - 输入数据指针, len - 数据长度, pOutData - 输出 2 字节 CRC(低位, 高位)
void RC522_CalculateCRC(uint8_t *pIndata, uint8_t len, uint8_t *pOutData)
{
uint8_t i, n;
RC522_ClearBitMask(DivIrqReg, 0x04); // 清除 CRC 中断标志
RC522_WriteReg(CommandReg, PCD_IDLE); // 取消当前命令
RC522_SetBitMask(FIFOLevelReg, 0x80); // 清空 FIFO
for(i = 0; i < len; i++)
{
RC522_WriteReg(FIFODataReg, pIndata[i]); // 写入数据到 FIFO
}
RC522_WriteReg(CommandReg, PCD_CALCCRC); // 启动 CRC 计算
i = 0xFF;
do
{
n = RC522_ReadReg(DivIrqReg);
i--;
} while((i != 0) && !(n & 0x04)); // 等待 CRC 完成中断
pOutData[0] = RC522_ReadReg(CRCResultRegL); // CRC 低位
pOutData[1] = RC522_ReadReg(CRCResultRegM); // CRC 高位
}
// 向 RC522 发送命令并处理返回(通用命令函数)
// 参数:
// command - PCD_* 命令
// pInData - 输入数据缓冲区
// inLen - 输入数据长度(字节)
// pOutData - 输出数据缓冲区(用于接收数据)
// pOutLen - 输出比特长度(以位为单位)
// 返回: MI_OK / MI_ERR / MI_NOTAGERR 等
int8_t RC522_Command(uint8_t command, uint8_t *pInData, uint8_t inLen,
uint8_t *pOutData, uint16_t *pOutLen)
{
int8_t status = MI_ERR;
uint8_t irqEn = 0x00;
uint8_t waitFor = 0x00;
uint8_t lastBits;
uint8_t n;
uint16_t i;
switch(command)
{
case PCD_AUTHENT:
irqEn = 0x12; // 认证中断
waitFor = 0x10;
break;
case PCD_TRANSCEIVE:
irqEn = 0x77; // 发送接收相关中断使能
waitFor = 0x30; // 等待接收完成标志
break;
default:
break;
}
RC522_WriteReg(ComIEnReg, irqEn | 0x80); // 允许中断并打开最高位
RC522_ClearBitMask(ComIrqReg, 0x80); // 清空中断标志
RC522_WriteReg(CommandReg, PCD_IDLE); // 取消命令
RC522_SetBitMask(FIFOLevelReg, 0x80); // 清空 FIFO
for(i = 0; i < inLen; i++)
{
RC522_WriteReg(FIFODataReg, pInData[i]); // 写入发送数据
}
RC522_WriteReg(CommandReg, command); // 执行命令
if(command == PCD_TRANSCEIVE)
{
RC522_SetBitMask(BitFramingReg, 0x80); // 启动发送
}
i = 2000; // 超时计数(循环等待)
do
{
n = RC522_ReadReg(ComIrqReg);
i--;
} while((i != 0) && !(n & 0x01) && !(n & waitFor));
RC522_ClearBitMask(BitFramingReg, 0x80); // 清除发送启动标志
if(i != 0)
{
if(!(RC522_ReadReg(ErrorReg) & 0x1B)) // 检查错误寄存器(BufferOvfl, ParityErr, ProtocolErr 等)
{
status = MI_OK;
if(n & irqEn & 0x01)
{
status = MI_NOTAGERR; // 没有标签
}
if(command == PCD_TRANSCEIVE)
{
// 读取接收数据长度与最后的有效位数
n = RC522_ReadReg(FIFOLevelReg);
lastBits = RC522_ReadReg(ControlReg) & 0x07;
if(lastBits)
{
*pOutLen = (n - 1) * 8 + lastBits; // 有效位数
}
else
{
*pOutLen = n * 8;
}
if(n == 0)
{
n = 1;
}
if(n > MAXRLEN)
{
n = MAXRLEN;
}
for(i = 0; i < n; i++)
{
pOutData[i] = RC522_ReadReg(FIFODataReg); // 读取 FIFO 数据
}
}
}
else
{
status = MI_ERR;
}
}
RC522_SetBitMask(ControlReg, 0x80); // 设置照明/控制位(根据库原意保留)
RC522_WriteReg(CommandReg, PCD_IDLE);
return status;
}
// 寻卡(请求卡片类型)
// 参数: req_code - PICC_REQIDL / PICC_REQALL 等
// pTagType - 输出 2 字节,表示卡类型
// 返回: MI_OK / MI_ERR
int8_t RC522_Request(uint8_t req_code, uint8_t *pTagType)
{
int8_t status;
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
RC522_ClearBitMask(Status2Reg, 0x08); // 清除 MIFARE 相关状态
RC522_WriteReg(BitFramingReg, 0x07); // 设置位帧(发送 7 位)
RC522_SetBitMask(TxControlReg, 0x03); // 打开天线驱动
ucComMF522Buf[0] = req_code;
status = RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, &unLen);
if((status == MI_OK) && (unLen == 0x10)) // 返回 16 位
{
pTagType[0] = ucComMF522Buf[0];
pTagType[1] = ucComMF522Buf[1];
}
else
{
status = MI_ERR;
}
return status;
}
// 防冲突,获取卡序列号(4 字节)
// 参数: pSnr - 输出序列号缓冲(至少 4 字节)
// 返回: MI_OK / MI_ERR
int8_t RC522_Anticoll(uint8_t *pSnr)
{
int8_t status;
uint8_t i, snr_check = 0;
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
RC522_ClearBitMask(Status2Reg, 0x08);
RC522_WriteReg(BitFramingReg, 0x00); // 以字节对齐
RC522_ClearBitMask(CollReg, 0x80);
ucComMF522Buf[0] = PICC_ANTICOLL1;
ucComMF522Buf[1] = 0x20; // 指定防冲突命令与长度
status = RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, &unLen);
if(status == MI_OK)
{
for(i = 0; i < 4; i++)
{
pSnr[i] = ucComMF522Buf[i];
snr_check ^= ucComMF522Buf[i];
}
// 校验位(第5字节)是否匹配
if(snr_check != ucComMF522Buf[i])
{
status = MI_ERR;
}
}
RC522_SetBitMask(CollReg, 0x80);
return status;
}
// 选择卡片(基于序列号)
// 参数: pSnr - 4 字节序列号
// 返回: MI_OK / MI_ERR
int8_t RC522_Select(uint8_t *pSnr)
{
int8_t status;
uint8_t i;
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_ANTICOLL1;
ucComMF522Buf[1] = 0x70; // SELECT 命令 + NVB
ucComMF522Buf[6] = 0;
for(i = 0; i < 4; i++)
{
ucComMF522Buf[i + 2] = pSnr[i];
ucComMF522Buf[6] ^= pSnr[i]; // 计算校验
}
RC522_CalculateCRC(ucComMF522Buf, 7, &ucComMF522Buf[7]); // 追加 CRC
RC522_ClearBitMask(Status2Reg, 0x08);
status = RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, &unLen);
if((status == MI_OK) && (unLen == 0x18)) // 应返回 24 位(应答位长度)
{
status = MI_OK;
}
else
{
status = MI_ERR;
}
return status;
}
// 验证密钥(认证一个扇区块)
// 参数: auth_mode - AUTHENT_A/AUTHENT_B, addr - 块地址, pKey - 6 字节密钥, pSnr - 卡序列号(4 字节)
// 返回: MI_OK / MI_ERR
int8_t RC522_AuthState(uint8_t auth_mode, uint8_t addr, uint8_t *pKey, uint8_t *pSnr)
{
int8_t status;
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = auth_mode;
ucComMF522Buf[1] = addr;
memcpy(&ucComMF522Buf[2], pKey, 6);
memcpy(&ucComMF522Buf[8], pSnr, 4);
status = RC522_Command(PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, &unLen);
// 检查认证后 Status2Reg 的 MIFARE bit(0x08)
if((status != MI_OK) || (!(RC522_ReadReg(Status2Reg) & 0x08)))
{
status = MI_ERR;
}
return status;
}
// 读块(读取 16 字节数据)
// 参数: addr - 块地址, pData - 输出缓冲长度至少 16 字节
// 返回: MI_OK / MI_ERR
int8_t RC522_Read(uint8_t addr, uint8_t *pData)
{
int8_t status;
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_READ;
ucComMF522Buf[1] = addr;
RC522_CalculateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]); // 追加 CRC
status = RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
if((status == MI_OK) && (unLen == 0x90)) // 16 字节 * 8 = 0x80 (加上控制位为 0x90)
{
memcpy(pData, ucComMF522Buf, 16);
}
else
{
status = MI_ERR;
}
return status;
}
// 写块(写入 16 字节数据)
// 参数: addr - 块地址, pData - 16 字节写入数据
// 返回: MI_OK / MI_ERR
int8_t RC522_Write(uint8_t addr, uint8_t *pData)
{
int8_t status;
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_WRITE;
ucComMF522Buf[1] = addr;
RC522_CalculateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]); // 写命令带 CRC
status = RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
// 首次应答检查(ACK)
if((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
{
status = MI_ERR;
}
if(status == MI_OK)
{
memcpy(ucComMF522Buf, pData, 16);
RC522_CalculateCRC(ucComMF522Buf, 16, &ucComMF522Buf[16]); // 数据后追加 CRC
status = RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, &unLen);
// 写结束应答检查
if((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
{
status = MI_ERR;
}
}
return status;
}
// 休眠卡片(发送 HALT 命令)
// 返回: MI_OK (不检查应答)
int8_t RC522_Halt(void)
{
uint16_t unLen;
uint8_t ucComMF522Buf[MAXRLEN];
ucComMF522Buf[0] = PICC_HALT;
ucComMF522Buf[1] = 0;
RC522_CalculateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);
// 发送 HALT 命令,忽略返回结果
RC522_Command(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
return MI_OK;
}
// 復位 RC522 芯片并进行基础寄存器初始化
int8_t RC522_Reset(void)
{
RC522_RST_HIGH();
Delay_ms(1);
RC522_RST_LOW();
Delay_ms(1);
RC522_RST_HIGH();
Delay_ms(1);
RC522_WriteReg(CommandReg, PCD_RESETPHASE);
Delay_ms(1);
// 基本模式与定时器/发送参数设置(常用初始化值)
RC522_WriteReg(ModeReg, 0x3D);
RC522_WriteReg(TReloadRegL, 30);
RC522_WriteReg(TReloadRegH, 0);
RC522_WriteReg(TModeReg, 0x8D);
RC522_WriteReg(TPrescalerReg, 0x3E);
RC522_WriteReg(TxAutoReg, 0x40);
return MI_OK;
}
// 打开天线(设置驱动位)
void RC522_AntennaOn(void)
{
uint8_t i;
i = RC522_ReadReg(TxControlReg);
if(!(i & 0x03))
{
RC522_SetBitMask(TxControlReg, 0x03); // 打开天线驱动位
}
}
// 关闭天线
void RC522_AntennaOff(void)
{
RC522_ClearBitMask(TxControlReg, 0x03);
}
// 配置 ISO 类型(当前实现仅支持 ISO14443-A)
// 参数: type - 'A' 表示 ISO14443_A
// 返回: MI_OK / MI_ERR
int8_t RC522_ConfigISOType(uint8_t type)
{
if(type == 'A') // ISO14443_A
{
RC522_ClearBitMask(Status2Reg, 0x08);
RC522_WriteReg(ModeReg, 0x3D);
RC522_WriteReg(RxSelReg, 0x86);
RC522_WriteReg(RFCfgReg, 0x7F);
RC522_WriteReg(TReloadRegL, 30);
RC522_WriteReg(TReloadRegH, 0);
RC522_WriteReg(TModeReg, 0x8D);
RC522_WriteReg(TPrescalerReg, 0x3E);
Delay_ms(1);
RC522_AntennaOn();
}
else
{
return MI_ERR;
}
return MI_OK;
}
// RC522 初始化入口:配置 GPIO、复位、天线与 ISO 类型
void RC522_Init(void)
{
RC522_GPIO_Init();
RC522_Reset();
RC522_AntennaOff();
RC522_AntennaOn();
RC522_ConfigISOType('A');
}
RC522.h
#ifndef __RC522_H
#define __RC522_H
#include "stm32f10x.h"
#include <stdint.h>
// RC522命令定义
#define PCD_IDLE 0x00
#define PCD_AUTHENT 0x0E
#define PCD_RECEIVE 0x08
#define PCD_TRANSMIT 0x04
#define PCD_TRANSCEIVE 0x0C
#define PCD_RESETPHASE 0x0F
#define PCD_CALCCRC 0x03
// Mifare卡片命令
#define PICC_REQIDL 0x26
#define PICC_REQALL 0x52
#define PICC_ANTICOLL1 0x93
#define PICC_ANTICOLL2 0x95
#define PICC_AUTHENT1A 0x60
#define PICC_AUTHENT1B 0x61
#define PICC_READ 0x30
#define PICC_WRITE 0xA0
#define PICC_DECREMENT 0xC0
#define PICC_INCREMENT 0xC1
#define PICC_RESTORE 0xC2
#define PICC_TRANSFER 0xB0
#define PICC_HALT 0x50
// FIFO长度
#define DEF_FIFO_LENGTH 64
#define MAXRLEN 18
// RC522寄存器定义
#define CommandReg 0x01
#define ComIEnReg 0x02
#define DivlEnReg 0x03
#define ComIrqReg 0x04
#define DivIrqReg 0x05
#define ErrorReg 0x06
#define Status1Reg 0x07
#define Status2Reg 0x08
#define FIFODataReg 0x09
#define FIFOLevelReg 0x0A
#define WaterLevelReg 0x0B
#define ControlReg 0x0C
#define BitFramingReg 0x0D
#define CollReg 0x0E
#define ModeReg 0x11
#define TxModeReg 0x12
#define RxModeReg 0x13
#define TxControlReg 0x14
#define TxAutoReg 0x15
#define TxSelReg 0x16
#define RxSelReg 0x17
#define RxThresholdReg 0x18
#define DemodReg 0x19
#define MifareReg 0x1C
#define SerialSpeedReg 0x1F
#define CRCResultRegM 0x21
#define CRCResultRegL 0x22
#define ModWidthReg 0x24
#define RFCfgReg 0x26
#define GsNReg 0x27
#define CWGsCfgReg 0x28
#define ModGsCfgReg 0x29
#define TModeReg 0x2A
#define TPrescalerReg 0x2B
#define TReloadRegH 0x2C
#define TReloadRegL 0x2D
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F
#define TestSel1Reg 0x31
#define TestSel2Reg 0x32
#define TestPinEnReg 0x33
#define TestPinValueReg 0x34
#define TestBusReg 0x35
#define AutoTestReg 0x36
#define VersionReg 0x37
#define AnalogTestReg 0x38
#define TestDAC1Reg 0x39
#define TestDAC2Reg 0x3A
#define TestADCReg 0x3B
// 返回值定义
#define MI_OK 0
#define MI_NOTAGERR (-1)
#define MI_ERR (-2)
// GPIO引脚定义 - 根据实际连接修改
#define RC522_CS_PORT GPIOA
#define RC522_CS_PIN GPIO_Pin_4
#define RC522_SCK_PORT GPIOA
#define RC522_SCK_PIN GPIO_Pin_5
#define RC522_MOSI_PORT GPIOA
#define RC522_MOSI_PIN GPIO_Pin_7
#define RC522_MISO_PORT GPIOA
#define RC522_MISO_PIN GPIO_Pin_6
#define RC522_RST_PORT GPIOA
#define RC522_RST_PIN GPIO_Pin_3
// 控制宏定义
#define RC522_CS_LOW() GPIO_ResetBits(RC522_CS_PORT, RC522_CS_PIN)
#define RC522_CS_HIGH() GPIO_SetBits(RC522_CS_PORT, RC522_CS_PIN)
#define RC522_SCK_LOW() GPIO_ResetBits(RC522_SCK_PORT, RC522_SCK_PIN)
#define RC522_SCK_HIGH() GPIO_SetBits(RC522_SCK_PORT, RC522_SCK_PIN)
#define RC522_MOSI_LOW() GPIO_ResetBits(RC522_MOSI_PORT, RC522_MOSI_PIN)
#define RC522_MOSI_HIGH() GPIO_SetBits(RC522_MOSI_PORT, RC522_MOSI_PIN)
#define RC522_RST_LOW() GPIO_ResetBits(RC522_RST_PORT, RC522_RST_PIN)
#define RC522_RST_HIGH() GPIO_SetBits(RC522_RST_PORT, RC522_RST_PIN)
#define RC522_MISO_READ() GPIO_ReadInputDataBit(RC522_MISO_PORT, RC522_MISO_PIN)
// 函数声明
void RC522_GPIO_Init(void);
uint8_t RC522_ReadReg(uint8_t addr);
void RC522_WriteReg(uint8_t addr, uint8_t value);
void RC522_SetBitMask(uint8_t reg, uint8_t mask);
void RC522_ClearBitMask(uint8_t reg, uint8_t mask);
void RC522_CalculateCRC(uint8_t *pIndata, uint8_t len, uint8_t *pOutData);
int8_t RC522_Command(uint8_t command, uint8_t *pInData, uint8_t inLen,
uint8_t *pOutData, uint16_t *pOutLen);
int8_t RC522_Request(uint8_t req_code, uint8_t *pTagType);
int8_t RC522_Anticoll(uint8_t *pSnr);
int8_t RC522_Select(uint8_t *pSnr);
int8_t RC522_AuthState(uint8_t auth_mode, uint8_t addr, uint8_t *pKey, uint8_t *pSnr);
int8_t RC522_Read(uint8_t addr, uint8_t *pData);
int8_t RC522_Write(uint8_t addr, uint8_t *pData);
int8_t RC522_Halt(void);
int8_t RC522_Reset(void);
void RC522_AntennaOn(void);
void RC522_AntennaOff(void);
void RC522_Init(void);
int8_t RC522_ConfigISOType(uint8_t type);
#endif
main.c
#include "stm32f10x.h"
#include "rc522.h"
#include "delay.h"
#include "usart.h"
#include <stdio.h>
#include <string.h>
// 默认密钥
uint8_t default_key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
// 员工信息结构体
typedef struct {
uint8_t id[4]; // 职工ID (4字节)
uint8_t name[8]; // 姓名 (8字节)
uint8_t department[4]; // 部门 (4字节)
} EmployeeInfo;
// 创建自定义员工数据
void CreateCustomEmployee(EmployeeInfo *employee)
{
// === 在这里修改员工数据 ===
// 职工ID (4字节) - 修改这里的数字
uint32_t emp_id = 0001; // 改为您想要的职工ID
// 姓名 (最多8个字符) - 修改这里的名字
char *emp_name = "QL.ql"; // 改为您想要的姓名
// 部门 (最多4个字符) - 修改这里的部门
char *emp_dept = "IT"; // 改为您想要的部门
// === 不要修改下面的代码 ===
// 设置职工ID
employee->id[0] = (emp_id >> 24) & 0xFF;
employee->id[1] = (emp_id >> 16) & 0xFF;
employee->id[2] = (emp_id >> 8) & 0xFF;
employee->id[3] = emp_id & 0xFF;
// 设置姓名,不足补空格
memset(employee->name, ' ', 8);
int name_len = strlen(emp_name);
if(name_len > 8) name_len = 8;
memcpy(employee->name, emp_name, name_len);
// 设置部门,不足补空格
memset(employee->department, ' ', 4);
int dept_len = strlen(emp_dept);
if(dept_len > 4) dept_len = 4;
memcpy(employee->department, emp_dept, dept_len);
}
// 尝试认证块
int8_t TryAuthBlock(uint8_t *snr, uint8_t block_addr)
{
printf("尝试认证块 %d: ", block_addr);
int8_t status = RC522_AuthState(PICC_AUTHENT1A, block_addr, default_key, snr);
if(status == MI_OK)
{
printf("成功!\r\n");
// 读取当前数据
uint8_t read_data[16];
if(RC522_Read(block_addr, read_data) == MI_OK)
{
printf("块 %d 当前数据: ", block_addr);
for(int i = 0; i < 16; i++)
{
printf("%02X ", read_data[i]);
}
printf("\r\n");
}
return MI_OK;
}
else
{
printf("失败\r\n");
return MI_ERR;
}
}
// 写入员工信息到卡片
int8_t WriteEmployeeInfo(uint8_t *snr, uint8_t block_addr, EmployeeInfo *employee)
{
int8_t status;
uint8_t write_data[16];
// 清空数据缓冲区
memset(write_data, 0, sizeof(write_data));
// 将员工信息复制到写入缓冲区
memcpy(&write_data[0], employee->id, 4);
memcpy(&write_data[4], employee->name, 8);
memcpy(&write_data[12], employee->department, 4);
printf("准备写入员工信息到块 %d:\r\n", block_addr);
printf("职工ID: %02X %02X %02X %02X (十进制: %u)\r\n",
employee->id[0], employee->id[1], employee->id[2], employee->id[3],
(employee->id[0] << 24) | (employee->id[1] << 16) |
(employee->id[2] << 8) | employee->id[3]);
printf("姓名: ");
for(int i = 0; i < 8; i++)
{
if(employee->name[i] >= 32 && employee->name[i] <= 126)
printf("%c", employee->name[i]);
else
printf(" ");
}
printf("\r\n");
printf("部门: ");
for(int i = 0; i < 4; i++)
{
if(employee->department[i] >= 32 && employee->department[i] <= 126)
printf("%c", employee->department[i]);
else
printf(" ");
}
printf("\r\n");
// 写入数据
status = RC522_Write(block_addr, write_data);
if(status == MI_OK)
{
printf("员工信息写入成功!\r\n");
// 验证写入
uint8_t read_data[16];
if(RC522_Read(block_addr, read_data) == MI_OK)
{
// 检查数据是否一致
int match = 1;
for(int i = 0; i < 16; i++)
{
if(read_data[i] != write_data[i])
{
match = 0;
break;
}
}
if(match)
{
printf("数据验证成功! 员工信息已正确写入\r\n");
}
else
{
printf("数据验证失败!\r\n");
status = MI_ERR;
}
}
}
else
{
printf("写入失败! 状态: %d\r\n", status);
}
return status;
}
// 读取并显示员工信息
int8_t ReadEmployeeInfo(uint8_t *snr, uint8_t block_addr)
{
int8_t status;
uint8_t read_data[16];
EmployeeInfo employee;
status = RC522_Read(block_addr, read_data);
if(status == MI_OK)
{
// 解析员工信息
memcpy(employee.id, &read_data[0], 4);
memcpy(employee.name, &read_data[4], 8);
memcpy(employee.department, &read_data[12], 4);
printf("=== 读取到的员工信息 ===\r\n");
printf("块地址: %d\r\n", block_addr);
printf("职工ID: %02X %02X %02X %02X",
employee.id[0], employee.id[1], employee.id[2], employee.id[3]);
printf(" (十进制: %u)\r\n",
(employee.id[0] << 24) | (employee.id[1] << 16) |
(employee.id[2] << 8) | employee.id[3]);
printf("姓名: ");
for(int i = 0; i < 8; i++)
{
if(employee.name[i] >= 32 && employee.name[i] <= 126)
printf("%c", employee.name[i]);
else
printf(" ");
}
printf("\r\n");
printf("部门: ");
for(int i = 0; i < 4; i++)
{
if(employee.department[i] >= 32 && employee.department[i] <= 126)
printf("%c", employee.department[i]);
else
printf(" ");
}
printf("\r\n");
printf("========================\r\n");
}
else
{
printf("读取员工信息失败!\r\n");
}
return status;
}
int main(void)
{
uint8_t status;
uint8_t snr[4];
uint8_t card_type[2];
USART1_Init(115200);
printf("=== RFID员工信息写入系统 ===\r\n");
RC522_Init();
printf("RC522 初始化成功\r\n");
printf("等待卡片...\r\n");
while(1)
{
// 寻卡
status = RC522_Request(PICC_REQIDL, card_type);
if(status == MI_OK)
{
printf("\r\n=== 检测到卡片 ===\r\n");
// 防冲突获取序列号
status = RC522_Anticoll(snr);
if(status == MI_OK)
{
printf("卡片序列号: %02X %02X %02X %02X\r\n",
snr[0], snr[1], snr[2], snr[3]);
// 选择卡片
status = RC522_Select(snr);
if(status == MI_OK)
{
printf("卡片已选中\r\n");
// 尝试认证块1
uint8_t target_block = 1;
status = TryAuthBlock(snr, target_block);
if(status == MI_OK)
{
// 创建自定义员工数据
EmployeeInfo employee;
CreateCustomEmployee(&employee);
// 写入员工信息
printf("\r\n--- 写入员工信息 ---\r\n");
status = WriteEmployeeInfo(snr, target_block, &employee);
if(status == MI_OK)
{
Delay_ms(100);
// 重新读取验证
printf("\r\n--- 重新读取验证 ---\r\n");
ReadEmployeeInfo(snr, target_block);
}
}
else
{
printf("认证失败,无法写入数据\r\n");
}
// 停止卡片
RC522_Halt();
printf("卡片已停止\r\n");
printf("=== 操作完成 ===\r\n\r\n");
}
else
{
printf("卡片选择失败\r\n");
}
}
else
{
printf("防冲突失败\r\n");
}
}
Delay_ms(500);
}
}
1911

被折叠的 条评论
为什么被折叠?



