
项目地址https://github.com/xbronze/rfid_physical_access_control/tree/main/chapter_04STM32RC522读取MIFARE 1K卡从UID到加密读写的“排坑”实录前言最近在做一个基于STM32F103C8T6和RC522的RFID门禁系统项目。由于使用现成的封装库本以为读取UID、完成扇区密码认证和读写是分分钟的事结果却在“认证失败”和“写块只写进一个字节”的泥潭里挣扎了好久。本文将复盘整个调试过程重点解析ISO14443A 协议的状态机流转、硬件CRC的陷阱以及底层收发时序截断这三个经典大坑。如果你也在用 RC522 调 MIFARE 1K (M1) 卡希望这篇避坑指南能帮你节省几个小时。实验现象与硬件环境硬件STM32F103C8T6 最小系统板、RC522 模块3.3V供电、MIFARE Classic 1K 白卡。软件STM32CubeMX Keil HAL库串口波特率 115200。目标读取卡号 UID - 使用默认密钥FFFFFFFFFFFF认证 - 读块 - 写块 - 读回验证。坑点一UID能读出但死活认证不过Auth Failed现象寻卡、防冲突获取 UID 一切正常但调用MIFARE_Auth时始终返回失败。起初怀疑是卡被加密了用手机 NFC 软件MCT一测发现卡确实出厂默认密钥FFFFFFFFFFFF根本没加密原因与解决根据 ISO14443A 协议卡片的状态机是严格递进的REQA(寻卡) - 卡片进入READY态。Anticoll(防冲突) - 获取 4 字节 UID卡片依然停留在 READY 态。SELECT(选卡) - 发送 UID 给卡片校验卡片进入ACTIVE态。AUTHENT(认证) -只有进入 ACTIVE 态后卡片才接受密码认证很多简陋的开源库省去了SELECT步骤导致卡片一直处于 READY 态你发认证指令它当然不理你。我们需要补上选卡逻辑// 选卡函数uint8_tRC522_Select(uint8_t*serNum){uint8_tstatus;uint16_tunLen;uint8_tbuffer[7];uint8_ti;buffer[0]0x93;/* Select 指令 */buffer[1]0x70;/* 选卡 */for(i0;i4;i){buffer[2i]serNum[i];}/* 计算 BCCUID 4字节异或 */buffer[6]serNum[0]^serNum[1]^serNum[2]^serNum[3];RC522_WriteReg(BitFramingReg,0x00);statusRC522_ToCard(PCD_TRANSCEIVE,buffer,7,buffer,unLen);returnstatus;}坑点二加上选卡后程序彻底卡死或通信超时现象加上Select指令后不仅认证没过连串口都不打印信息了或者打印Select failed。原因与解决这涉及两个致命细节CRC校验的开启时机与SPI通信速率。硬件 CRC 陷阱在 ISO14443A 中寻卡0x52和防冲突0x9320是不带 CRC 的但是选卡0x9370、认证0x0E、读写0x30/0xA0必须带 CRC。如果你的代码全局开启或关闭 CRC都会导致通信失败。SPI 速率过高用面包板和杜邦线连接时如果 SPI 速率跑到 9MHz在带加密和 CRC 的复杂数据交换中极易发生时序错位丢帧。解决思路降低 SPI 速率在 CubeMX 中将 SPI1 的 Baud Rate Prescaler 从 8 改为64 或 128降至 1MHz 左右非常稳定。动态控制 CRC在底层发送函数RC522_ToCard中根据指令动态修改TxModeReg和RxModeReg。// RC522_ToCard 函数中的 CRC 动态判断if(commandPCD_AUTHENT){RC522_WriteReg(TxModeReg,0x80);// 认证开启发送 CRCRC522_WriteReg(RxModeReg,0x80);// 认证开启接收 CRC}elseif(commandPCD_TRANSCEIVE){// 寻卡和防冲突不需要 CRCif((sendData[0]0x93sendData[1]0x20)||sendData[0]0x52||sendData[0]0x26){RC522_WriteReg(TxModeReg,0x00);RC522_WriteReg(RxModeReg,0x00);}else{// 选卡和读写需要 CRCRC522_WriteReg(TxModeReg,0x80);RC522_WriteReg(RxModeReg,0x80);}}坑点三认证和读块成功但写块只写进了第一个字节现象认证通过读块数据全00正常调用写块函数返回成功但读回的数据却只有首字节变成了32后面全是00例如写入32 30 32 33...读回32 00 00 00...。原因与解决这是 RC522 硬件底层的一个“时序截断”Bug。MIFARE 卡的写块协议是半双工的主机发0xA0块地址(2字节)。卡片回复ACK。主机紧接着发16字节数据。卡片回复ACK。当我们用底层函数一次性把 18 字节全部塞进 FIFO 时RC522 在发完前 2 个字节后收到了卡片的ACK此时中断标志位RxIRq置位。代码误以为发送已经结束提前打断了发送过程导致后面的 15 个数据字节全被丢弃。解决思路在底层等待中断的逻辑中“打个补丁”如果是写块指令不等待接收中断而是强等待发送完成中断IdleIRq 0x10确保 18 字节全部吐完。或者最稳妥的做法是将写块拆分为两次发送// 最稳妥的写块函数实现拆分法uint8_tMIFARE_WriteBlock(uint8_tblockAddr,uint8_t*data){uint8_tstatus;uint16_tunLen;uint8_tbuffer[2];// 1. 发送写块指令和地址 (0xA0 Addr)buffer[0]0xA0;buffer[1]blockAddr;RC522_WriteReg(BitFramingReg,0x00);statusRC522_ToCard(PCD_TRANSCEIVE,buffer,2,NULL,unLen);if(status!MI_OK)returnstatus;// 2. 发送 16 字节数据statusRC522_ToCard(PCD_TRANSCEIVE,data,16,NULL,unLen);returnstatus;}最终的完美输出经过上述三道“手术”重新编译烧录。刷卡后串口终于打出了令人极其舒适的完美回显UART Direct Test OK RC522 Version: 0x92 MFRC522 Version 2.0 detected. --- New Card Detected --- Card UID: 02 5B 0E 9E --- 1. Read Test --- Auth(default key FFFFFFFFFFFF) OK Block 4 raw data: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --- 2. Write Test --- Write block 4 OK Read back block 4: 32 30 32 33 30 30 30 31 5A 68 61 6E 67 00 00 00 --- 3. Wrong Key Test --- Wrong key rejected, auth failed as expected总结与思考在使用 RC522 等 RFID 模块时往往“能读出UID”只是个开始真正的挑战在于吃透协议规范ISO14443A 的状态机流转READY - ACTIVE是死规定不能省步骤。硬件调试需降速面包板长线连接时SPI 降速往往能解决 90% 的玄学问题。注意芯片特性RC522 在收发非对称长度数据如写块时的中断打断问题需要仔细查阅芯片手册FIFO 与 中断控制。