RFID模块 RC522

STM32F103C8T6 · HAL库 · 软件SPI通信 · ISO/IEC 14443 Type A

3.3V
工作电压
13.56MHz
工作频率
~5cm
读写距离
SPI
通信方式
106kbps
传输速率

01传感器原理与功能

MFRC522是NXP半导体公司生产的高度集成非接触式读写芯片,工作于13.56MHz,支持ISO/IEC 14443 Type A标准

1.1 工作原理

RC522读写器 天线驱动 13.56MHz 电磁耦合 MIFARE卡 天线线圈 储能供电 IC ① 读写器天线发射13.56MHz电磁波 ② 卡片线圈感应取电,激活芯片 ③ REQA/ATQA 完成寻卡与防冲撞 ④ Crypto1密钥验证(Key A/B) ⑤ 认证通过后读写数据块

电磁感应耦合:MFRC522内部发射器通过天线引脚(TX1/TX2)驱动13.56MHz载波,产生交变磁场。当无源MIFARE卡进入磁场范围(约5cm内),卡片内置天线线圈通过电磁感应获取能量,激活卡片IC。

通信流程:读写器发送REQA(Request Type A)命令寻卡,卡片返回ATQA应答;随后执行ISO 14443A防冲撞序列,获取卡片唯一序列号UID;选卡后进行Crypto1流密码认证(Key A或Key B),认证通过即可读写数据块。

调制方式:读写器到卡片采用100% ASK调制;卡片到读写器采用ASK负载调制,通过13.56MHz副载波传递数据。MFRC522内置64字节FIFO缓冲区和CRC协处理器,自动处理帧格式、校验和编解码。

1.2 核心功能

📡

寻卡与防冲撞

发送REQA命令检测磁场范围内的ISO 14443A卡片,执行防冲撞算法隔离单张卡并获取4字节UID序列号

🔐

密码认证

支持MIFARE Classic的Crypto1流密码认证,使用6字节Key A或Key B进行挑战-应答式安全验证

📖

读写数据块

认证通过后可对16字节数据块进行读取或写入操作,支持MIFARE 1K(16扇区×4块)和4K卡

1.3 引脚定义

序号引脚名称类型功能说明STM32连接
1SDA / SS数字I/OSPI片选信号(低电平有效)PA7
2SCK数字输入SPI时钟线(由主机提供)PA6
3MOSI数字输入SPI主出从入(MCU→RC522数据)PA5
4MISO数字输出SPI主入从出(RC522→MCU数据)PA4
5IRQ数字输出中断请求(卡片检测/命令完成触发)未连接
6GND电源接地GND
7RST数字输入复位/掉电控制(低电平复位)PA0
83.3V电源供电(3.3V,不可接5V)3.3V
注意:RC522模块供电电压为3.3V,不可使用5V供电。STM32F103C8T6同样为3.3V器件,两者可直接连接,无需电平转换。本项目使用软件模拟SPI(GPIO位操作),非硬件SPI外设。

02模块外观展示

RFID-RC522模块外观实物展示

RC522模块外观1
外观图1
RC522模块外观2
外观图2

03原理图与PCB封装

模块电路原理图与PCB封装设计

原理图
PCB封装

04程序分析

基于STM32 HAL库的RC522驱动程序与主程序逻辑分析

4.1 系统时钟与初始化

系统时钟配置 — SystemClock_Config()

使用外部高速晶振(HSE 8MHz),通过PLL倍频9倍获得72MHz系统主频。APB1总线36MHz,APB2总线72MHz。

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  // HSE外部晶振8MHz,PLL倍频9倍 = 72MHz
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;        // 8MHz × 9 = 72MHz
  HAL_RCC_OscConfig(&RCC_OscInitStruct);

  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;    // HCLK = 72MHz
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;     // APB1 = 36MHz
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;     // APB2 = 72MHz
  HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}

主函数初始化序列 — main()

依次完成HAL库初始化、时钟配置、GPIO初始化、OLED显示屏初始化和RC522模块初始化。

int main(void)
{
  HAL_Init();                  // HAL库初始化
  SystemClock_Config();        // 系统时钟配置 72MHz
  MX_GPIO_Init();              // GPIO端口时钟使能

  OLED_Init();                 // OLED显示屏初始化
  RC522_Init();                // RFID模块初始化

  while (1)
  {
    /* 主循环逻辑 */
  }
}

4.2 RC522引脚初始化(软件SPI)

RC522_GPIO_Init() — GPIO引脚配置

SDA/SCK/MOSI/RST配置为推挽输出,MISO配置为上拉输入。通过GPIO位操作模拟SPI时序,而非使用硬件SPI外设。

void RC522_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;

  // 引脚时钟使能(均在GPIOA)
  RC522_SDA_GPIO_CLK_ENABLE();
  RC522_SCK_GPIO_CLK_ENABLE();
  RC522_MOSI_GPIO_CLK_ENABLE();
  RC522_MISO_GPIO_CLK_ENABLE();
  RC522_RST_GPIO_CLK_ENABLE();

  // SDA(PA7)、SCK(PA6)、MOSI(PA5)、RST(PA0) → 推挽输出
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

  GPIO_InitStruct.Pin = RC522_SDA_Pin;   HAL_GPIO_Init(RC522_SDA_Port, &GPIO_InitStruct);
  GPIO_InitStruct.Pin = RC522_SCK_Pin;   HAL_GPIO_Init(RC522_SCK_Port, &GPIO_InitStruct);
  GPIO_InitStruct.Pin = RC522_MOSI_Pin;  HAL_GPIO_Init(RC522_MOSI_Port, &GPIO_InitStruct);
  GPIO_InitStruct.Pin = RC522_RST_Pin;   HAL_GPIO_Init(RC522_RST_Port, &GPIO_InitStruct);

  // MISO(PA4) → 上拉输入
  GPIO_InitStruct.Pin  = RC522_MISO_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(RC522_MISO_Port, &GPIO_InitStruct);
}

引脚宏定义 — mfrc522.h

所有RC522引脚均映射到GPIOA端口,通过宏定义简化操作。RC522_SDA(a)等宏封装了HAL_GPIO_WritePin调用。

#define RC522_SDA_Port   GPIOA
#define RC522_SCK_Port   GPIOA
#define RC522_MOSI_Port  GPIOA
#define RC522_MISO_Port  GPIOA
#define RC522_RST_Port   GPIOA

#define RC522_SDA_Pin    GPIO_PIN_7    // SPI片选
#define RC522_SCK_Pin    GPIO_PIN_6    // SPI时钟
#define RC522_MOSI_Pin   GPIO_PIN_5    // 主出从入
#define RC522_MISO_Pin   GPIO_PIN_4    // 主入从出
#define RC522_RST_Pin    GPIO_PIN_0    // 复位

#define RC522_SDA(a)  HAL_GPIO_WritePin(RC522_SDA_Port,RC522_SDA_Pin,(a)?GPIO_PIN_SET:GPIO_PIN_RESET)
#define RC522_SCK(a)  HAL_GPIO_WritePin(RC522_SCK_Port,RC522_SCK_Pin,(a)?GPIO_PIN_SET:GPIO_PIN_RESET)
#define RC522_MOSI(a) HAL_GPIO_WritePin(RC522_MOSI_Port,RC522_MOSI_Pin,(a)?GPIO_PIN_SET:GPIO_PIN_RESET)
#define RC522_MISO()  HAL_GPIO_ReadPin(RC522_MISO_Port,RC522_MISO_Pin)

4.3 寄存器读写(软件SPI时序)

WriteRawRC() — 写RC522寄存器

通过GPIO位操作模拟SPI MODE 0时序:拉低SDA片选,按MSB优先逐位发送地址字节和数据字节。地址格式:(addr<<1)&0x7E,最高位为0表示写操作。

void WriteRawRC(unsigned char Address, unsigned char value)
{
  unsigned char i, ucAddr;

  RC522_SCK(0);                    // SCK拉低
  RC522_SDA(0);                    // 片选拉低(选中RC522)
  ucAddr = ((Address<<1)&0x7E);    // 地址格式:bit7=0(写), bit6=0, addr[6:1]

  // 发送地址字节(MSB优先)
  for(i=8;i>0;i--)
  {
    if((ucAddr&0x80)==0x80) RC522_MOSI(1);
    else RC522_MOSI(0);
    delay_us(1);
    RC522_SCK(1);                 // 上升沿锁存数据
    delay_us(1);
    ucAddr <<= 1;
    RC522_SCK(0);
    delay_us(1);
  }

  // 发送数据字节(MSB优先)
  for(i=8;i>0;i--)
  {
    if((value&0x80)==0x80) RC522_MOSI(1);
    else RC522_MOSI(0);
    delay_us(1);
    RC522_SCK(1);
    delay_us(1);
    value <<= 1;
    RC522_SCK(0);
    delay_us(1);
  }
  RC522_SDA(1);                    // 释放片选
  RC522_SCK(1);
}

4.4 RFID卡片处理流程

RC522_Process() — 完整读卡流程

依次执行寻卡→防冲撞→选卡→密码验证四个步骤,全部成功返回MI_OK。使用默认密钥FFFFFFFFFFFF验证块2(扇区0)的Key A。

signed char RC522_Process(void)
{
  // 1. 寻卡:发送0x26(REQIDL),返回卡片类型到Temp数组
  if(PcdRequest(0x26, Temp) == MI_OK)
  {
    // 2. 防冲撞:获取4字节卡片序列号到UID数组
    if(PcdAnticoll(UID) == MI_OK)
    {
      // 3. 选卡:选中该序列号的卡片
      if(PcdSelect(UID) == MI_OK)
      {
        // 4. 密码验证:Key A(0x60), 块地址2, 默认密钥FFFFFFFFFFFF
        if(PcdAuthState(0x60, 2, (unsigned char *)DefaultKey, UID) == MI_OK)
        {
          return MI_OK;
        }
      }
    }
  }
  return MI_ERR;
}

卡片类型代码对照表

PcdRequest成功后,Temp数组存储2字节卡片类型代码ATQA

类型代码卡片型号存储容量
0x4400Mifare_UltraLight512 字节
0x0400Mifare_One(S50)1024 字节(1K)
0x0200Mifare_One(S70)4096 字节(4K)
0x0800Mifare_Pro(X)更大容量
0x4403Mifare_DESFireDES加密

4.5 数据读写操作

PcdWrite() / PcdRead() — 读写16字节数据块

认证通过后,可对指定块地址进行读写。每块16字节,读写命令自动附加CRC校验。MIFARE 1K卡共16扇区×4块=64块,块地址0~63。

// 写数据到指定块(addr为块地址,pData为16字节数据)
char PcdWrite(unsigned char addr, unsigned char *pData)
{
  ucComMF522Buf[0] = PICC_WRITE;       // 0xA0 写块命令
  ucComMF522Buf[1] = addr;
  CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);

  // 第一阶段:发送写命令+块地址,等待卡片ACK
  status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
  if(status == MI_OK)
  {
    // 第二阶段:发送16字节数据+CRC
    for(i=0;i<16;i++) ucComMF522Buf[i] = *(pData+i);
    CalulateCRC(ucComMF522Buf, 16, &ucComMF522Buf[16]);
    status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, &unLen);
  }
  return status;
}

// 读取指定块的16字节数据
char PcdRead(unsigned char addr, unsigned char *pData)
{
  ucComMF522Buf[0] = PICC_READ;        // 0x30 读块命令
  ucComMF522Buf[1] = addr;
  CalulateCRC(ucComMF522Buf, 2, &ucComMF522Buf[2]);

  status = PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, &unLen);
  if(status == MI_OK && unLen == 0x90)  // 0x90位 = 144位 = 18字节(16数据+2CRC)
  {
    for(i=0;i<16;i++) *(pData+i) = ucComMF522Buf[i];
  }
  return status;
}

4.6 主循环逻辑

while(1) 主循环 — main.c

主循环持续调用RC522_Process()检测卡片。检测成功后在OLED上显示UID和卡片类型,将"Hello RC522!"写入块2并回读显示,最后令卡片休眠并延时500ms防止重复触发。

while (1)
{
  /* RFID逻辑控制 */
  if(RC522_Process() == MI_OK)
  {
    OLED_Clear();

    // 显示卡片UID(4字节十六进制)
    OLED_CenterPrintf(0, "UID");
    OLED_CenterPrintf(16, "%02X %02X %02X %02X", UID[0],UID[1],UID[2],UID[3]);

    // 显示卡片类型代码
    OLED_CenterPrintf(32, "Temp:0X%02X%02X", Temp[0],Temp[1]);

    // 将数据写入卡片块2
    strcpy((char *)RC522_Buf, "Hello RC522!");
    PcdWrite(2, RC522_Buf);

    // 读取块2数据并显示
    if(PcdRead(2, RC522_Buf) == MI_OK)
    {
      OLED_CenterPrintf(48, "%s", RC522_Buf);
    }

    OLED_Update();              // 刷新OLED显示

    PcdHalt();                  // 卡片进入休眠
    HAL_Delay(500);           // 延时500ms防止临界点反复触发
  }
}

MIFARE Classic 1K 卡存储结构

本程序操作块2(扇区0的数据块),每扇区最后一个块(块3)为密钥尾块

MIFARE Classic 1K — 16扇区 × 4块 × 16字节 = 1024字节 扇区 0 块0:厂商块(UID) 只读 块1:数据块 读写 块2:数据块 ← 本程序操作 块3:密钥尾块 KeyA|访问位|KeyB 扇区 1~14 块0~2:数据 块3:密钥尾块 14个扇区 结构相同 扇区 15 块0~2:数据 块3:密钥尾块 默认密钥 FF FF FF FF FF FF 每扇区独立密钥 Key A:6字节 Key B:6字节 访问位:4字节

05程序流程图

STM32主程序执行流程,包含while(1)主循环与RC522读卡完整流程

开始 系统初始化 HAL_Init() / SystemClock_Config() MX_GPIO_Init() — GPIO时钟使能 OLED_Init() — 显示屏初始化 RC522_Init() — RFID初始化 复位 / 开天线 / ISO14443配置 while(1) RC522_Process() — 读卡流程 寻卡→防冲撞→选卡→密码验证 返回MI_OK? 否 是 OLED_Clear() — 清屏 显示卡片UID(4字节HEX) 显示卡片类型代码 PcdWrite(2, "Hello RC522!") PcdRead(2, buf) — 回读数据 OLED_Update() — 显示数据 PcdHalt() + HAL_Delay(500)

06交互式动画演示

模拟RC522模块读取MIFARE卡的完整过程,观察电磁场激活、卡片检测、数据读写的实时变化

RC522 读卡器模拟

RC522 SDA SCK MOS MIS IRQ GND RST 3V3 MIFARE 1K 点击"开始检测"启动模拟 将卡片靠近RC522读卡器
IDLE › 等待启动...

OLED 显示屏输出

等待读卡...

电磁场:RC522天线发射13.56MHz交变磁场,卡片线圈感应取电

寻卡:发送REQA命令,卡片返回ATQA类型代码

防冲撞:获取4字节唯一序列号UID

认证:使用Key A(FFFFFFFFFFFF)验证扇区0

读写:写入"Hello RC522!"到块2,回读并显示