从规范到代码:RC522驱动Mifare Ultralight Type2 Tag的实战避坑指南(基于PHY6212平台)

张开发
2026/4/13 23:17:03 15 分钟阅读

分享文章

从规范到代码:RC522驱动Mifare Ultralight Type2 Tag的实战避坑指南(基于PHY6212平台)
1. RC522与Mifare Ultralight Type2 Tag基础认知第一次接触RC522和Mifare Ultralight Type2 Tag时我完全被各种专业术语搞晕了。后来在实际项目中踩了不少坑才明白其实RC522就是个读卡器芯片而Mifare Ultralight Type2 Tag是NFC卡片的一种类型就像超市的条形码和扫码枪的关系。RC522这款芯片最大的优势就是便宜又好用市场价只要十几块钱但性能完全够用。它支持ISO/IEC 14443 TypeA协议这个协议就像是NFC世界的普通话大部分卡片都支持。而Mifare Ultralight Type2 Tag就是遵循这个协议的卡片之一特别适合需要低成本NFC解决方案的场景。这里有个容易混淆的点Mifare Ultralight和Mifare Classic常说的M1卡虽然都姓Mifare但区别很大。Ultralight存储空间小通常只有64字节没有加密功能但胜在价格便宜M1卡有1K/4K容量支持加密价格也贵些。我在项目中选择Ultralight就是看中它的性价比毕竟我们只需要存储简单的配对信息。2. PHY6212平台环境搭建在PHY6212平台上使用RC522第一步就是搭建硬件环境。我刚开始以为直接插上就能用结果发现引脚定义不对直接导致芯片不工作。这里分享下我的接线方案RC522的RST引脚接PHY6212的GPIO_P20MOSI接GPIO_P03SCK接GPIO_P01NSS接GPIO_P02MISO接GPIO_P31软件方面需要特别注意SPI的初始化配置。PHY6212的SPI时钟频率建议设置在1MHz左右太高了可能导致通信不稳定。我最初设成8MHz结果读取经常失败后来降到1MHz就稳定了。// PHY6212 SPI初始化示例 void SPI_Init(void) { hal_spi_master_config_t spi_config { .mode HAL_SPI_MODE_0, .freq HAL_SPI_MASTER_FREQ_1M, .bit_order HAL_SPI_MSB_FIRST }; hal_spi_master_init(HAL_SPI_MASTER_0, spi_config); }3. 卡片检测与ATQA获取实战卡片检测是NFC读取的第一步也是最容易出错的地方。根据IEC14443-3/A规范我们需要先发送查询指令获取ATQA响应。这里有个关键点Mifare Ultralight卡的ATQA固定是0x4400这个值就像是卡片的身份证开头号码。实际操作中我发现有两种查询指令可用REQA(0x26)和WUPA(0x52)。RC522官方例程用的是WUPA实测下来确实更稳定。下面是我优化过的查询代码uint8_t PcdRequest(uint8_t req_code, uint8_t *pTagType) { uint8_t status; uint16_t unLen; uint8_t ucComMF522Buf[MAXRLEN]; ClearBitMask(Status2Reg, 0x08); WriteRawRC(BitFramingReg, 0x07); SetBitMask(TxControlReg, 0x03); ucComMF522Buf[0] req_code; // 0x52 for WUPA status PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, unLen); if ((status MI_OK) (unLen 0x10)) { pTagType[0] ucComMF522Buf[0]; pTagType[1] ucComMF522Buf[1]; } return status; }常见问题排查如果返回状态不是MI_OK先检查硬件连接如果ATQA值不是0x4400可能是卡片类型不匹配如果unLen长度不对尝试降低SPI时钟频率4. UID读取与防冲撞机制详解这里是我踩坑最多的地方。Mifare Ultralight卡的UID读取需要分两次完成第一次获取前3字节第二次获取后4字节。和M1卡最大的不同是Ultralight卡在第一次防冲撞后会返回4字节数据但第一个字节(0x88)是固定前缀实际UID只有后3字节。uint8_t PcdAnticoll(uint8_t *pSnr) { uint8_t status; uint8_t i, snr_check 0; uint16_t unLen; uint8_t ucComMF522Buf[MAXRLEN]; ClearBitMask(Status2Reg, 0x08); WriteRawRC(BitFramingReg, 0x00); ClearBitMask(CollReg, 0x80); ucComMF522Buf[0] PICC_ANTICOLL1; // 0x93 ucComMF522Buf[1] 0x20; status PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, unLen); if (status MI_OK) { for (i 0; i 4; i) { pSnr[i] ucComMF522Buf[i]; snr_check ^ ucComMF522Buf[i]; } if (snr_check ! ucComMF522Buf[i]) { status MI_ERR; } } SetBitMask(CollReg, 0x80); return status; }第二次防冲撞需要使用不同的指令码(0x95)这就是为什么需要单独实现PcdAnticoll_type2函数。我当初没注意这个区别调试了一整天才发现问题。5. 卡片选择与数据读取技巧完成UID获取后还需要进行两次选择卡片操作。这里有个关键细节第一次选择卡片时需要包含0x88前缀而第二次选择时不需要。这个细节在规范里没有明确说明我是通过反复试验才发现的。数据读取相对简单因为Ultralight卡不需要认证。直接使用PcdRead函数即可但要注意返回的数据格式uint8_t PcdRead(uint8_t addr, uint8_t *pData) { uint8_t status; uint16_t unLen; uint8_t ucComMF522Buf[MAXRLEN]; ucComMF522Buf[0] PICC_READ; ucComMF522Buf[1] addr; status PcdComMF522(PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, unLen); if ((status MI_OK) (unLen 0x90)) { memcpy(pData, ucComMF522Buf, 16); } return status; }虽然Ultralight每个区块只有4字节但RC522会返回16字节数据这是为了兼容M1卡的读取接口。实际使用时只需要处理前4字节即可。6. PHY6212平台移植要点将RC522驱动移植到PHY6212平台主要需要修改GPIO操作部分。PHY6212的GPIO操作接口和标准单片机有些不同需要特别注意#define MF522_RST_PIN GPIO_P20 #define MF522_MOSI_PIN GPIO_P03 #define MF522_SCK_PIN GPIO_P01 #define MF522_NSS_PIN GPIO_P02 #define MF522_MISO_PIN GPIO_P31 #define RST_H hal_gpio_fast_write(MF522_RST_PIN, 1) #define RST_L hal_gpio_fast_write(MF522_RST_PIN, 0) #define NSS_H hal_gpio_fast_write(MF522_NSS_PIN, 1) #define NSS_L hal_gpio_fast_write(MF522_NSS_PIN, 0)调试时可以添加LED指示灯辅助调试比如在每次成功读取时点亮LED遇到错误时闪烁LED。这个小技巧帮我节省了大量调试时间。7. 常见问题与解决方案在实际项目中我遇到过几个典型问题卡片检测不稳定通常是天线匹配问题可以尝试调整匹配电路的电容值。我在天线两端并联了一个33pF的电容后稳定性大幅提升。UID读取不全检查两次防冲撞的顺序是否正确特别注意第一次防冲撞后要去掉0x88前缀。通信超时降低SPI时钟频率PHY6212平台建议使用1MHz以下频率。功耗异常确保在不操作RC522时将NSS引脚拉高否则芯片会保持高功耗状态。有个特别隐蔽的坑是PHY6212的GPIO驱动能力问题。我发现当同时使用多个外设时RC522的供电可能会不稳定。解决方法是在RC522的VCC引脚就近加一个0.1uF的退耦电容。

更多文章