1. 引言RC522是一款常用的13.56MHz非接触式读写芯片广泛应用于门禁、考勤、智能交通等领域。它支持ISO14443A/MIFARE协议可与M1卡、Ultralight卡等通信。这个模块我买了不少时间了之前帮做智慧宿舍的时候有用到过用来做卡片识别实现开门这个模块在大学做课设毕设的时候基本都是用这个模块。今天咱们一起来驱动这个模块实现卡片识别一般买这个模块都会配套送两个卡片咱们就用这两个卡片来做测试。2. 硬件连接RC522模块引脚定义与STM32连接如下RC522引脚STM32F103C8T6说明SDA (NSS)PA3片选软件控制SCKPA5SPI时钟MOSIPA7主出从入MISOPA6主入从出RSTPA2复位低电平复位IRQ不接中断输出本例未用3.3V3.3V供电需稳定GNDGND共地注意RC522模块电流可达50mA建议单独供电或确保3.3V稳压器输出能力足够。3. CubeMX配置使用STM32CubeMX生成初始化代码关键配置如下RCC高速外部时钟HSE使能系统时钟配置为72MHz。GPIOPA3NSS、PA2RST设为推挽输出。PA5SCK、PA7MOSI设为推挽输出。PA6MISO设为输入浮空。USART1可选用于打印调试信息配置为异步模式波特率115200。4. 驱动核心代码解析驱动采用软件模拟SPI便于移植。以下仅展示关键函数。4.1 引脚宏定义rc522_hal.h// 引脚定义可根据实际连接修改 #define RC522_CS_PORT GPIOA #define RC522_CS_PIN GPIO_PIN_3 #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_2 // 片选控制宏 #define RC522_CS_ENABLE() HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_RESET) #define RC522_CS_DISABLE() HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_SET) // 复位控制宏 #define RC522_RST_ENABLE() HAL_GPIO_WritePin(RC522_RST_PORT, RC522_RST_PIN, GPIO_PIN_RESET) #define RC522_RST_DISABLE() HAL_GPIO_WritePin(RC522_RST_PORT, RC522_RST_PIN, GPIO_PIN_SET) // 软件SPI控制宏 #define RC522_SCK_HIGH() HAL_GPIO_WritePin(RC522_SCK_PORT, RC522_SCK_PIN, GPIO_PIN_SET) #define RC522_SCK_LOW() HAL_GPIO_WritePin(RC522_SCK_PORT, RC522_SCK_PIN, GPIO_PIN_RESET) #define RC522_MOSI_HIGH() HAL_GPIO_WritePin(RC522_MOSI_PORT, RC522_MOSI_PIN, GPIO_PIN_SET) #define RC522_MOSI_LOW() HAL_GPIO_WritePin(RC522_MOSI_PORT, RC522_MOSI_PIN, GPIO_PIN_RESET) #define RC522_MISO_READ() HAL_GPIO_ReadPin(RC522_MISO_PORT, RC522_MISO_PIN)4.2 微秒延时函数软件SPI需要微秒级延时这里使用简单的空循环实现实际项目可根据系统时钟精确调整。void Delay_us(uint32_t us) { uint32_t i; for (i 0; i us * 8; i) { __NOP(); } }4.3 寄存器读写rc522_hal.c通过软件SPI完成RC522寄存器的读写。/* 写寄存器 */ static void WriteRawRC(uint8_t ucAddress, uint8_t ucValue) { uint8_t ucAddr (ucAddress 1) 0x7E; // 写地址最高位0 RC522_CS_ENABLE(); SPI_RC522_SendByte(ucAddr); SPI_RC522_SendByte(ucValue); RC522_CS_DISABLE(); } /* 读寄存器 */ static uint8_t ReadRawRC(uint8_t ucAddress) { uint8_t ucAddr ((ucAddress 1) 0x7E) | 0x80; // 读地址最高位1 uint8_t ucReturn; RC522_CS_ENABLE(); SPI_RC522_SendByte(ucAddr); ucReturn SPI_RC522_ReadByte(); RC522_CS_DISABLE(); return ucReturn; }4.4 RC522初始化void RC522_Init(void) { RC522_GPIO_Init(); // 初始化GPIO RC522_RST_DISABLE(); // 释放复位 RC522_CS_DISABLE(); // 片选无效 PcdReset(); // 复位RC522 M500PcdConfigISOType(A); // 配置ISO14443_A } void PcdReset(void) { RC522_RST_DISABLE(); Delay_us(1); RC522_RST_ENABLE(); Delay_us(1); RC522_RST_DISABLE(); Delay_us(1); WriteRawRC(CommandReg, 0x0F); // 软复位 while (ReadRawRC(CommandReg) 0x10); // 等待复位完成 Delay_us(1); WriteRawRC(ModeReg, 0x3D); // 设置CRC初值0x6363 WriteRawRC(TReloadRegL, 30); WriteRawRC(TReloadRegH, 0); WriteRawRC(TModeReg, 0x8D); WriteRawRC(TPrescalerReg, 0x3E); WriteRawRC(TxAutoReg, 0x40); // 调制发送信号100%ASK }4.5 寻卡与防冲突/* 寻卡获取ATQA */ char PcdRequest(uint8_t req_code, uint8_t *pTagType) { char cStatus; uint8_t ucComBuf[MAXRLEN]; uint32_t ulLen; ClearBitMask(Status2Reg, 0x08); WriteRawRC(BitFramingReg, 0x07); SetBitMask(TxControlReg, 0x03); ucComBuf[0] req_code; cStatus PcdComMF522(PCD_TRANSCEIVE, ucComBuf, 1, ucComBuf, ulLen); if ((cStatus MI_OK) (ulLen 0x10)) { *pTagType ucComBuf[0]; *(pTagType 1) ucComBuf[1]; } else { cStatus MI_ERR; } return cStatus; } /* 防冲突获取UID */ char PcdAnticoll(uint8_t *pSnr) { char cStatus; uint8_t uc, ucSnr_check 0; uint8_t ucComBuf[MAXRLEN]; uint32_t ulLen; ClearBitMask(Status2Reg, 0x08); WriteRawRC(BitFramingReg, 0x00); ClearBitMask(CollReg, 0x80); ucComBuf[0] 0x93; ucComBuf[1] 0x20; cStatus PcdComMF522(PCD_TRANSCEIVE, ucComBuf, 2, ucComBuf, ulLen); if (cStatus MI_OK) { for (uc 0; uc 4; uc) { *(pSnr uc) ucComBuf[uc]; ucSnr_check ^ ucComBuf[uc]; } if (ucSnr_check ! ucComBuf[uc]) cStatus MI_ERR; } SetBitMask(CollReg, 0x80); return cStatus; }4.6 与卡片通信核心函数PcdComMF522负责发送命令并接收响应包含超时和错误处理。static char PcdComMF522(uint8_t ucCommand, uint8_t *pInData, uint8_t ucInLenByte, uint8_t *pOutData, uint32_t *pOutLenBit) { char cStatus MI_ERR; uint8_t ucIrqEn 0x00; uint8_t ucWaitFor 0x00; uint8_t ucLastBits; uint8_t ucN; uint32_t ul; // 根据命令配置中断使能 switch (ucCommand) { case PCD_AUTHENT: ucIrqEn 0x12; ucWaitFor 0x10; break; case PCD_TRANSCEIVE: ucIrqEn 0x77; ucWaitFor 0x30; break; default: break; } WriteRawRC(ComIEnReg, ucIrqEn | 0x80); // 允许中断 ClearBitMask(ComIrqReg, 0x80); // 清除中断标志 WriteRawRC(CommandReg, PCD_IDLE); // 空闲 SetBitMask(FIFOLevelReg, 0x80); // 清FIFO // 写入发送数据 for (ul 0; ul ucInLenByte; ul) WriteRawRC(FIFODataReg, pInData[ul]); WriteRawRC(CommandReg, ucCommand); if (ucCommand PCD_TRANSCEIVE) SetBitMask(BitFramingReg, 0x80); // 启动发送 // 等待命令完成 ul 1000; do { ucN ReadRawRC(ComIrqReg); ul--; } while ((ul ! 0) !(ucN 0x01) !(ucN ucWaitFor)); ClearBitMask(BitFramingReg, 0x80); if (ul ! 0) { // 检查错误寄存器 if (!(ReadRawRC(ErrorReg) 0x1B)) { cStatus MI_OK; if (ucN ucIrqEn 0x01) cStatus MI_NOTAGERR; if (ucCommand PCD_TRANSCEIVE) { // 读取接收数据 ucN ReadRawRC(FIFOLevelReg); ucLastBits ReadRawRC(ControlReg) 0x07; if (ucLastBits) *pOutLenBit (ucN - 1) * 8 ucLastBits; else *pOutLenBit ucN * 8; if (ucN 0) ucN 1; if (ucN MAXRLEN) ucN MAXRLEN; for (ul 0; ul ucN; ul) pOutData[ul] ReadRawRC(FIFODataReg); } } else { cStatus MI_ERR; } } SetBitMask(ControlReg, 0x80); // 停止定时器 WriteRawRC(CommandReg, PCD_IDLE); return cStatus; }5. 测试例程在main.c中实现循环读取卡片UID并通过串口打印。#include rc522_hal.h #include stdio.h int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); RC522_Init(); printf(RC522 Ready\r\n); uint8_t uid[4]; uint8_t tagType[2]; while (1) { if (PcdRequest(PICC_REQALL, tagType) MI_OK) { if (PcdAnticoll(uid) MI_OK) { printf(Card UID: %02X %02X %02X %02X\r\n, uid[0], uid[1], uid[2], uid[3]); PcdSelect(uid); // 选卡后卡片休眠 PcdHalt(); } } HAL_Delay(500); } }6. 常见问题与解决现象可能原因解决方法版本寄存器读取为0x00/0xFFSPI通信失败检查引脚连接、延时函数是否准确、电源是否稳定寻卡失败始终返回MI_ERR天线未开启、卡片距离太远确认TxControlReg低两位为1将卡片贴近天线读取UID偶尔成功时序问题、电源噪声适当增加Delay_us延时值电源并联10μF0.1μF电容某些卡片如Mifare Plus无法读取协议不兼容此类卡片可能需要额外命令进入MIFARE兼容模式或更换标准M1卡测试认证失败密钥错误非默认密钥或扇区已被锁使用正确密钥或先测试默认密钥0xFF×67. 测试结果需要参考代码请联系我哈
【已验证】STM32驱动RC522_RFID模块实现卡片识别
发布时间:2026/5/25 22:21:12
1. 引言RC522是一款常用的13.56MHz非接触式读写芯片广泛应用于门禁、考勤、智能交通等领域。它支持ISO14443A/MIFARE协议可与M1卡、Ultralight卡等通信。这个模块我买了不少时间了之前帮做智慧宿舍的时候有用到过用来做卡片识别实现开门这个模块在大学做课设毕设的时候基本都是用这个模块。今天咱们一起来驱动这个模块实现卡片识别一般买这个模块都会配套送两个卡片咱们就用这两个卡片来做测试。2. 硬件连接RC522模块引脚定义与STM32连接如下RC522引脚STM32F103C8T6说明SDA (NSS)PA3片选软件控制SCKPA5SPI时钟MOSIPA7主出从入MISOPA6主入从出RSTPA2复位低电平复位IRQ不接中断输出本例未用3.3V3.3V供电需稳定GNDGND共地注意RC522模块电流可达50mA建议单独供电或确保3.3V稳压器输出能力足够。3. CubeMX配置使用STM32CubeMX生成初始化代码关键配置如下RCC高速外部时钟HSE使能系统时钟配置为72MHz。GPIOPA3NSS、PA2RST设为推挽输出。PA5SCK、PA7MOSI设为推挽输出。PA6MISO设为输入浮空。USART1可选用于打印调试信息配置为异步模式波特率115200。4. 驱动核心代码解析驱动采用软件模拟SPI便于移植。以下仅展示关键函数。4.1 引脚宏定义rc522_hal.h// 引脚定义可根据实际连接修改 #define RC522_CS_PORT GPIOA #define RC522_CS_PIN GPIO_PIN_3 #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_2 // 片选控制宏 #define RC522_CS_ENABLE() HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_RESET) #define RC522_CS_DISABLE() HAL_GPIO_WritePin(RC522_CS_PORT, RC522_CS_PIN, GPIO_PIN_SET) // 复位控制宏 #define RC522_RST_ENABLE() HAL_GPIO_WritePin(RC522_RST_PORT, RC522_RST_PIN, GPIO_PIN_RESET) #define RC522_RST_DISABLE() HAL_GPIO_WritePin(RC522_RST_PORT, RC522_RST_PIN, GPIO_PIN_SET) // 软件SPI控制宏 #define RC522_SCK_HIGH() HAL_GPIO_WritePin(RC522_SCK_PORT, RC522_SCK_PIN, GPIO_PIN_SET) #define RC522_SCK_LOW() HAL_GPIO_WritePin(RC522_SCK_PORT, RC522_SCK_PIN, GPIO_PIN_RESET) #define RC522_MOSI_HIGH() HAL_GPIO_WritePin(RC522_MOSI_PORT, RC522_MOSI_PIN, GPIO_PIN_SET) #define RC522_MOSI_LOW() HAL_GPIO_WritePin(RC522_MOSI_PORT, RC522_MOSI_PIN, GPIO_PIN_RESET) #define RC522_MISO_READ() HAL_GPIO_ReadPin(RC522_MISO_PORT, RC522_MISO_PIN)4.2 微秒延时函数软件SPI需要微秒级延时这里使用简单的空循环实现实际项目可根据系统时钟精确调整。void Delay_us(uint32_t us) { uint32_t i; for (i 0; i us * 8; i) { __NOP(); } }4.3 寄存器读写rc522_hal.c通过软件SPI完成RC522寄存器的读写。/* 写寄存器 */ static void WriteRawRC(uint8_t ucAddress, uint8_t ucValue) { uint8_t ucAddr (ucAddress 1) 0x7E; // 写地址最高位0 RC522_CS_ENABLE(); SPI_RC522_SendByte(ucAddr); SPI_RC522_SendByte(ucValue); RC522_CS_DISABLE(); } /* 读寄存器 */ static uint8_t ReadRawRC(uint8_t ucAddress) { uint8_t ucAddr ((ucAddress 1) 0x7E) | 0x80; // 读地址最高位1 uint8_t ucReturn; RC522_CS_ENABLE(); SPI_RC522_SendByte(ucAddr); ucReturn SPI_RC522_ReadByte(); RC522_CS_DISABLE(); return ucReturn; }4.4 RC522初始化void RC522_Init(void) { RC522_GPIO_Init(); // 初始化GPIO RC522_RST_DISABLE(); // 释放复位 RC522_CS_DISABLE(); // 片选无效 PcdReset(); // 复位RC522 M500PcdConfigISOType(A); // 配置ISO14443_A } void PcdReset(void) { RC522_RST_DISABLE(); Delay_us(1); RC522_RST_ENABLE(); Delay_us(1); RC522_RST_DISABLE(); Delay_us(1); WriteRawRC(CommandReg, 0x0F); // 软复位 while (ReadRawRC(CommandReg) 0x10); // 等待复位完成 Delay_us(1); WriteRawRC(ModeReg, 0x3D); // 设置CRC初值0x6363 WriteRawRC(TReloadRegL, 30); WriteRawRC(TReloadRegH, 0); WriteRawRC(TModeReg, 0x8D); WriteRawRC(TPrescalerReg, 0x3E); WriteRawRC(TxAutoReg, 0x40); // 调制发送信号100%ASK }4.5 寻卡与防冲突/* 寻卡获取ATQA */ char PcdRequest(uint8_t req_code, uint8_t *pTagType) { char cStatus; uint8_t ucComBuf[MAXRLEN]; uint32_t ulLen; ClearBitMask(Status2Reg, 0x08); WriteRawRC(BitFramingReg, 0x07); SetBitMask(TxControlReg, 0x03); ucComBuf[0] req_code; cStatus PcdComMF522(PCD_TRANSCEIVE, ucComBuf, 1, ucComBuf, ulLen); if ((cStatus MI_OK) (ulLen 0x10)) { *pTagType ucComBuf[0]; *(pTagType 1) ucComBuf[1]; } else { cStatus MI_ERR; } return cStatus; } /* 防冲突获取UID */ char PcdAnticoll(uint8_t *pSnr) { char cStatus; uint8_t uc, ucSnr_check 0; uint8_t ucComBuf[MAXRLEN]; uint32_t ulLen; ClearBitMask(Status2Reg, 0x08); WriteRawRC(BitFramingReg, 0x00); ClearBitMask(CollReg, 0x80); ucComBuf[0] 0x93; ucComBuf[1] 0x20; cStatus PcdComMF522(PCD_TRANSCEIVE, ucComBuf, 2, ucComBuf, ulLen); if (cStatus MI_OK) { for (uc 0; uc 4; uc) { *(pSnr uc) ucComBuf[uc]; ucSnr_check ^ ucComBuf[uc]; } if (ucSnr_check ! ucComBuf[uc]) cStatus MI_ERR; } SetBitMask(CollReg, 0x80); return cStatus; }4.6 与卡片通信核心函数PcdComMF522负责发送命令并接收响应包含超时和错误处理。static char PcdComMF522(uint8_t ucCommand, uint8_t *pInData, uint8_t ucInLenByte, uint8_t *pOutData, uint32_t *pOutLenBit) { char cStatus MI_ERR; uint8_t ucIrqEn 0x00; uint8_t ucWaitFor 0x00; uint8_t ucLastBits; uint8_t ucN; uint32_t ul; // 根据命令配置中断使能 switch (ucCommand) { case PCD_AUTHENT: ucIrqEn 0x12; ucWaitFor 0x10; break; case PCD_TRANSCEIVE: ucIrqEn 0x77; ucWaitFor 0x30; break; default: break; } WriteRawRC(ComIEnReg, ucIrqEn | 0x80); // 允许中断 ClearBitMask(ComIrqReg, 0x80); // 清除中断标志 WriteRawRC(CommandReg, PCD_IDLE); // 空闲 SetBitMask(FIFOLevelReg, 0x80); // 清FIFO // 写入发送数据 for (ul 0; ul ucInLenByte; ul) WriteRawRC(FIFODataReg, pInData[ul]); WriteRawRC(CommandReg, ucCommand); if (ucCommand PCD_TRANSCEIVE) SetBitMask(BitFramingReg, 0x80); // 启动发送 // 等待命令完成 ul 1000; do { ucN ReadRawRC(ComIrqReg); ul--; } while ((ul ! 0) !(ucN 0x01) !(ucN ucWaitFor)); ClearBitMask(BitFramingReg, 0x80); if (ul ! 0) { // 检查错误寄存器 if (!(ReadRawRC(ErrorReg) 0x1B)) { cStatus MI_OK; if (ucN ucIrqEn 0x01) cStatus MI_NOTAGERR; if (ucCommand PCD_TRANSCEIVE) { // 读取接收数据 ucN ReadRawRC(FIFOLevelReg); ucLastBits ReadRawRC(ControlReg) 0x07; if (ucLastBits) *pOutLenBit (ucN - 1) * 8 ucLastBits; else *pOutLenBit ucN * 8; if (ucN 0) ucN 1; if (ucN MAXRLEN) ucN MAXRLEN; for (ul 0; ul ucN; ul) pOutData[ul] ReadRawRC(FIFODataReg); } } else { cStatus MI_ERR; } } SetBitMask(ControlReg, 0x80); // 停止定时器 WriteRawRC(CommandReg, PCD_IDLE); return cStatus; }5. 测试例程在main.c中实现循环读取卡片UID并通过串口打印。#include rc522_hal.h #include stdio.h int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); RC522_Init(); printf(RC522 Ready\r\n); uint8_t uid[4]; uint8_t tagType[2]; while (1) { if (PcdRequest(PICC_REQALL, tagType) MI_OK) { if (PcdAnticoll(uid) MI_OK) { printf(Card UID: %02X %02X %02X %02X\r\n, uid[0], uid[1], uid[2], uid[3]); PcdSelect(uid); // 选卡后卡片休眠 PcdHalt(); } } HAL_Delay(500); } }6. 常见问题与解决现象可能原因解决方法版本寄存器读取为0x00/0xFFSPI通信失败检查引脚连接、延时函数是否准确、电源是否稳定寻卡失败始终返回MI_ERR天线未开启、卡片距离太远确认TxControlReg低两位为1将卡片贴近天线读取UID偶尔成功时序问题、电源噪声适当增加Delay_us延时值电源并联10μF0.1μF电容某些卡片如Mifare Plus无法读取协议不兼容此类卡片可能需要额外命令进入MIFARE兼容模式或更换标准M1卡测试认证失败密钥错误非默认密钥或扇区已被锁使用正确密钥或先测试默认密钥0xFF×67. 测试结果需要参考代码请联系我哈