一、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.核心: 

        读写器通过发射特定协议的“敲门信号”,来探测其可对话的卡片。这个过程在物理层协议层进行了双重过滤。即找到当前相同协议的、空闲状态的卡。

2.完整过程:

​​​​​​​
  1. 物理层过滤(硬件层面,自动发生):

    • 频率匹配:RC522工作在 13.56MHz。只有同样谐振在13.56MHz的高频(HF)卡的线圈才能从磁场中获得能量并激活。低频(125kHz)卡因频率完全不同,根本不会“醒来”。

    • 调制与解调匹配:RC522以 ISO/IEC 14443 Type A 协议规定的 ASK 100%(改进米勒编码) 方式调制信号。只有内置了能解调此特定波形的电路的卡片,才能正确理解读写器发出的“0”和“1”。

    → 此步过滤掉了所有低频卡和不支持Type A解调方式的卡。

  2. 协议层交互(软件指令层面):
    当卡片通过物理层考验后,真正的“对话”才开始。

    • 读写器“敲门”:RC522发送协议规定的 REQA(请求,代码0x26) 或 WUPA(唤醒,代码0x52) 指令帧。

    • 卡片“应答”:能听懂Type A协议的卡片,在正确的时序内,以协议规定的编码方式回复一个 ATQA(对请求的应答) 信号。这个应答是一个简短的2字节数据,主要告诉读写器:“我是Type A卡,我的大致卡类(如MIFARE Classic)是什么”。

    → 此步过滤掉了同为13.56MHz但使用其他协议(如Type B, 15693)的卡,它们听不懂REQA命令,所以不会回复ATQA

3.状态说明:

  • REQA 只能唤醒并探测处于 “IDLE(空闲)/ READY(就绪)”状态的卡片。

  • 如果卡片因之前操作而进入 “HALT(休眠)”状态,则会对REQA保持沉默。此时必须使用WUPA指令才能将其唤醒并应答。

/* 寻卡(请求卡片类型)
// 参数: 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; // 返回结果
}

(三)获取当前响应卡片的卡号,同时防冲突

也就是多个卡片响应时选择出其中一张卡并且识别出它的卡号(当前防冲突的代码处理不全面,暂不适用多卡识别)

1.核心: 

        当多张同类型卡片同时进入场区时,通过特定算法选取其中一张进行后续操作。

防冲突只是获得IC卡的UID,并没有直接进行读写相关的通信操作,需要在后面的选卡操作中真正选上。

2.防冲突:

        当只有一张卡片响应时,所有寄存器收到的bit位数据都是稳定的值;当多张卡片响应时,个别bit位会出现0、1的交替接收,不稳定,就说明收到了不止一张卡片的信号;发现冲突后,在已有的但不冲突的序列作为开头,之后从冲突位开始用0/1进行枚举尝试选中其中一张卡。

下面是举例:

假设场内有3张MIFARE Classic卡,它们的UID分别是:

  • 卡A0x12 0x34 0x56 0x78

  • 卡B0x12 0x34 0x55 0xAA

  • 卡C0x12 0x35 0xAA 0xBB

第1步:发送防冲突命令,接收第一次响应
读写器发送 ANTICOLLISION 命令(代码 0x93 0x20),要求所有卡片回复其UID的前面部分。

  • 卡A 回复: 1 2 3 4 (为简化,用10进制表示)

  • 卡B 回复: 1 2 3 5

  • 卡C 回复: 1 2 3 5

信号在空中叠加,读写器接收到的是:在第一个字节(12)和第二个字节(34)位置,所有卡回复一致,信号稳定。但在第三个字节,卡A回复56(二进制0101 0110),卡B和卡C回复550101 0101。读写器会在比特位级别检测到冲突:例如,5655的最后一个比特位分别是01,导致该位电平异常,被标记为“冲突位”。

第2步:定位并解决冲突(“二叉树点名法”)
读写器发现第3个字节有冲突。它会记录下目前无冲突的部分,即 UID前两个字节 = 0x12 0x34

然后,它会在冲突发生的那个比特位(以及其后的位)上,通过发送不同的“选择条件”来筛选卡片。这个过程就像老师在一群同时报学号的学生中,从冲突的那一位开始,挨个问“下一位是0的请举手?”、“是1的请举手?”。

  1. 第一次筛选:读写器发送一个新的命令,内容是“寻找UID前两个字节为 0x12 0x34,且第三个字节的冲突位(假设为最低位)为 0 的卡”。

    • 卡A (56的末位是0):符合条件,响应。

    • 卡B卡C (55的末位是1):不符合,保持沉默。

    • 结果:此时只有卡A响应,冲突消失。读写器顺利接收到卡A完整的第3、4字节 0x56 0x78,并计算出校验位,从而获得了卡A的完整UID0x12 0x34 0x56 0x78

  2. 完成第一张卡的选择:读写器立即对卡A的完整UID执行 SELECT(选择) 命令。卡A被选中,进入活跃状态。其他卡片(B和C)虽然也听到了这个广播,但因为UID不匹配,所以不会响应,继续处于等待状态。

  3. 处理剩余卡片:读写器完成与卡A的通信并发送 HALT 命令让其休眠后,可以从头开始新一轮的寻卡和防冲突流程。

    • 这次,卡A已休眠,不会响应ANTICOLLISION命令。

    • 当读写器再次发送命令“寻找UID前两个字节为 0x12 0x34...”时,卡B和卡C会再次响应,并在第三个字节(55)上达成一致,没有冲突。读写器可以顺利获得卡B的完整UID(0x12 0x34 0x55 0xAA)并选中它。

    • 如此循环,直到场内所有卡都被处理完毕。

===================================================================

开始防冲突
  │
  ↓
发送ANTICOLLISION命令,接收卡片回复的UID部分
  │
  ↓
检测回复信号 ----> 发现冲突位?
  │                    │
  │无冲突              │有冲突
  │                    │
  ↓                    ↓
获得完整UID          记录已确定的无冲突前缀
  │                    │
  ↓                    ↓
进入选卡流程          从冲突位开始,发送带“0”或“1”选择条件的
                      新ANTICOLLISION命令,筛选出一组卡
                      │
                      └───→ 返回“检测回复信号”步骤,直到无冲突

// 防冲突,获取卡序列号(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; // 返回结果
}

(五)认证

下面的连接中,介绍了认证相关的内容。

https://blog.csdn.net/QL_SD/article/details/156640098?fromshare=blogdetail&sharetype=blogdetail&sharerId=156640098&sharerefer=PC&sharesource=QL_SD&sharefrom=from_linkhttps://blog.csdn.net/QL_SD/article/details/156640098?fromshare=blogdetail&sharetype=blogdetail&sharerId=156640098&sharerefer=PC&sharesource=QL_SD&sharefrom=from_link

// 验证密钥(认证一个扇区块)
// 参数: 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;
    }
}

(六)数据操作:

读写卡片

(七)休眠:

休眠(HALT)是一个“硬重启”信号,而非“待机”。

  1. 状态:卡片休眠后,仅能响应唯一的WUPA唤醒指令,对所有其他命令(包括REQA和数据指令)无响应。

  2. 后果:休眠会强制终止当前的整个通信会话,包括加密通道和逻辑连接。

  3. 唤醒后:卡片被唤醒后,其状态等同于一张刚进入射频场的新卡,必须从头开始完整的“寻卡→防冲突→选卡→认证”流程,才能再次进行数据操作。

二、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);
    }
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐