RC522作为一款NFC读写芯片,性价比还是很高的,因为在项目里需要采用NFC OOB配对,所以需要读取配对方模拟的NFC卡片信息

读取对象采用NRF52832,使用其NFC功能模拟type2 tag,但是读取方式和M1卡不一样,踩了不少坑,网上的资料都是调取现有接口没啥用处,也没有什么资料可以参考,只能自己看技术规范来实现了。

NFC Forum Type2 Tag属于Mifare Ultralight卡,和M1卡一样都遵循IEC14443-3/A规范,但是Mifare Ultralight有7位UID,这点需要额外注意;

查阅IEC14443-3/A规范得知卡片读取流程如下:

开始读取流程后,先需要发送查询指令得到卡片的ATQA值,对于Mifare Ultralight卡来说ATQA值为0x4400,识别到这个ATQA值即可进入Mifare Ultralight卡的UID读取流程

如何得到ATQA值呢?有两种方式,可以通过发送REQA或者WUPA都可以得到:

在RC522的例程中,采用WUPA进行查询,代码如下:

ClearBitMask(Status2Reg,0x08);//清除Status2Reg寄存器
WriteRawRC(BitFramingReg,0x07);//停止当前RC522的指令
SetBitMask(TxControlReg,0x03);//准备发送数据
ucComMF522Buf[0] = 0x52;//查询指令
status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,&unLen);//和RC522通讯,若有卡片存在则会将卡片的ATQA值保存在ucComMF522Buf中
if ((status == MI_OK) && (unLen == 0x10))
   {    
       *pTagType     = ucComMF522Buf[0];
       *(pTagType+1) = ucComMF522Buf[1];
   }

得到的返回值pTagType即为卡片的ATQA值;

若有Mifare Ultralight卡位于RF场内,则进行第一次选择卡片操作,即流程图中的“Select cascade level 1”

这里有个大坑,对于Mifare Ultralight卡来说,需要先进行一次防冲撞操作得到前三位UID值后才能继续往下进行选择卡片操作,否则不会得到任何返回值,当时在这里卡了好久

PcdRequest(0x52,TagType);//第一步,查询卡片
if(TagType[0]==0x44 && TagType[1]==0x00)//确认卡片为Ultralight
{
    PcdAnticoll(SelectedSnr);//第一次防冲撞,得到卡片的第一组UID值,保存在SelectedSnr内,长度为4位
    //假设返回值为88:5F:D1:6E,5F:D1:6E才是卡片UID的一部分88只是卡片代码,在后续的获取UID的操作中应当忽略掉这个值
    UID[0]=SelectedSnr[1];
	UID[1]=SelectedSnr[2];
	UID[2]=SelectedSnr[3];//得到前三位UID值
    PcdSelect(SelectedSnr);//第一次选择卡片,这里的卡号参数需要连着0x88一起发送出去
    PcdAnticoll_type2(SelectedSnr+4);//第二次防冲撞,将后四位UID号储存在SelectedSnr[4]之后的数组内
    UID[3]=SelectedSnr[4];
	UID[4]=SelectedSnr[5];
	UID[5]=SelectedSnr[6];
	UID[6]=SelectedSnr[7];//得到后四位UID卡号
    PcdSelect2(SelectedSnr+4);//第二次选择卡片,这样才可以得到卡片的访问权限
    /*从此处之后即可正常读取卡片信息,应注意Ultralight卡片不需要密钥访问,因此无需调用            
      PcdAuthState写入访问密钥,直接调用PcdRead函数即可
      例如Pcd_Read(0x00, buf),会直接返回卡片0,1,2,3扇区的16位内容,虽然Ultralight
      的每个区块只有四位,但是返回值和M1卡是一样的,都是16位,即范围所选区块后四块的内容,可以与
      M1卡做兼容处理*/

}

 

读取操作的指令和M1卡片是一样的,因此在取得卡片的全部UID后可以采用同一个函数对Ultralight卡片进行读取

应注意, PcdAnticoll_type2是我自己写的函数,实际内容和 PcdAnticoll内容是一样的,只是将查询的指令由0x93变更为0x95,即ucComMF522Buf[0]的值需要修改

/
//Mifare Ultralight 卡二次防冲撞
/  
char PcdAnticoll_type2(unsigned char *pSnr)
{
    char status;
    unsigned char i,snr_check=0;
    unsigned int  unLen;
    unsigned char ucComMF522Buf[MAXRLEN]; 
    

    ClearBitMask(Status2Reg,0x08);
    WriteRawRC(BitFramingReg,0x00);
    ClearBitMask(CollReg,0x80);
 
    ucComMF522Buf[0] = PICC_ANTICOLL2;//0x95,Ultralight二次选择需要的指令
    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;
}

在移植到PHY6212平台时,只需要重新对几个引脚操作进行重定义即可

#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 LED_WHITE												 GPIO_P34
#define LED_WARM												 GPIO_P00//我自己加的错误指示灯,可以不用
#define LED_ON													 hal_gpio_write(LED_WHITE, 1)
#define LED_OFF													 hal_gpio_write(LED_WHITE, 0)
#define ERR_ON													 hal_gpio_write(LED_WARM, 1)//我自己加的错误指示灯,可以不用
#define ERR_OFF													 hal_gpio_write(LED_WARM, 0)//我自己加的错误指示灯,可以不用
#define RST_H                            hal_gpio_fast_write(MF522_RST_PIN, 1)
#define RST_L                            hal_gpio_fast_write(MF522_RST_PIN, 0)
#define MOSI_H                           hal_gpio_fast_write(MF522_MOSI_PIN, 1)
#define MOSI_L                           hal_gpio_fast_write(MF522_MOSI_PIN, 0)
#define SCK_H                            hal_gpio_fast_write(MF522_SCK_PIN, 1)
#define SCK_L                            hal_gpio_fast_write(MF522_SCK_PIN, 0)
#define NSS_H                            hal_gpio_fast_write(MF522_NSS_PIN, 1)
#define NSS_L                            hal_gpio_fast_write(MF522_NSS_PIN, 0)
#define READ_MISO                        hal_gpio_read(MF522_MISO_PIN)//hal_gpio_read(MF522_MISO_PIN)

移植到其他平台也是一样,重新定义这些GPIO操作和引脚即可,我将完整代码上传到CSDN了,有需要可以下载,不闲麻烦的话可以自己根据这个流程改一下RC522的例程代码,有问题大家随时沟通,一起学习

下载地址:https://download.csdn.net/download/weixin_47047654/16817998

Logo

鸿蒙生态一站式服务平台。

更多推荐