PN512使用调研

PN512使用调研PN512概述  PN512是一个高度集成的非接触读写芯片,集成了13.56MHz下的各种主动/被动式非接触通信方法和协议。  PN512传输模块支持4种不同的工作模式:  1、读写器模式,支持ISO14443A/MIFARE®和FeliCa机制  2、读写器模式,支持ISO14443B机制  3、卡操作模式,支持ISO14443A/MIFARE®和FeliCa机制  4、NFCIP-1模式其中ISO14443A主要用于读取普通IC卡,ISO14443B主要读取CP

PN512概述

  PN512是一个高度集成的非接触读写芯片,集成了13.56MHz下的各种主动/被动式非接触通信方法和协议。

  PN512传输模块支持4种不同的工作模式:

  1、读写器模式,支持ISO 14443A / MIFARE®和FeliCa机制

  2、读写器模式,支持ISO 14443B机制

  3、卡操作模式,支持ISO 14443A / MIFARE®和FeliCa机制

  4、NFCIP-1模式

其中ISO14443A主要用于读取普通IC卡,ISO14443B主要读取CPU卡的程序,这里我们用于读取身份证的UID。读取身份证UID的方式如下图。

身份证UID读取方式

PN512支持一下几种不同的接口:位并行接口,SPI接口,串行UART接口,I2C接口。其中SPI接口支持传输速率高达10Mbit/s,I2C在快速模式下传输速率高达400kbit/s,在高速模式下可以高达3400kbit/s。

阅读官方文档及代码

阅读来自恩智浦官方的《Guide about how to port the Passive Target example from the NFC Reader Library to another MCU》,了解移植NFC库的相关内容。

SNEP(Simple NDEF Exchange Protocol )简单数据交换格式交换协议式通过LLCP交换NDEF信息的请求/响应协议。SNEP API分为以下三类功能:会话建立、数据交换、会话释放。

阅读来自恩智浦官方的《PNEV512B QuickStartup Guide》,这是官方使用电路板的工程说明,使用的单片机是LPC11U68。了解官方提供的样版工程的使用逻辑,该文档还介绍了部分改写可以大大减少Flash和Ram的使用。

基础代码为官方代码文件夹下NFC Reader Library v4.040.05.011646 R1 for PNEV512B including all software examples的代码,其中有对应的库函数可供查看。有部分官方函数可见NXP NFC Reader Library查阅。

官方代码文件

还有部分拓展库函数位于NFC Reader Library v4.040.05.011646 R1 for PNEV512B including all software examples_\NxpNfcRdLib\comps中。

image-20210213154027522

Example 1 – Basic Discovery Loop

image-20210213153601144

轮询扫描封闭环境中的NFC标签及设备。实现示例以在发现循环的POLL和LISTEN模式下工作。 检测到的标签的信息(如UID,SAK和基于MIFARE产品的卡的产品类型)被打印出来,并且当它被外部启动程序/阅读器激活为目标时,它也会打印信息。 每当检测到多种技术时,示例都会选择首先检测到的技术并加以解决。在被动轮询模式下,使能LPCD (Low Power Card Detection)功能。此示例的核心功能是“ BasicDiscoveryLoop_Demo()”,其中实现了NFC Reader库的初始化和针对NFC技术的轮询。 在每个轮询循环之后,应用程序将检查轮询结果以及有关检测到的标签或设备的打印输出信息。

void BasicDiscoveryLoop_Demo(void  *pDataParams)
{
    phStatus_t    status = PHAC_DISCLOOP_LPCD_NO_TECH_DETECTED;
    uint16_t      wTagsDetected = 0;
    uint16_t      wNumberOfTags = 0;
    uint16_t      wEntryPoint;
    uint16_t      wValue;
    uint8_t       bIndex;
​
    status = phApp_HALConfigAutoColl();//轮询检测的结果函数
    CHECK_STATUS(status);
​
    /* Get Poll Configuration */
    status = phacDiscLoop_GetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_PAS_POLL_TECH_CFG, &bSavePollTechCfg);
    //轮询设置函数及状态重置
    CHECK_STATUS(status);
​
    /* Start in poll mode */
    wEntryPoint = PHAC_DISCLOOP_ENTRY_POINT_POLL;
    status = PHAC_DISCLOOP_LPCD_NO_TECH_DETECTED;
    //设置轮询模式开启
​
    while(1)
    {
#ifdef PH_EXAMPLE1_LPCD_ENABLE//打开LPCD,即低功耗模式
        /* Configure LPCD */
        if((status & PH_ERR_MASK) == PHAC_DISCLOOP_LPCD_NO_TECH_DETECTED)
        {
            status = phApp_ConfigureLPCD();//重置状态为低功耗模式,不启动轮询工作
            CHECK_STATUS(status);//检测状态并执行
        }
​
        /* Bool to enable LPCD feature. */
        status = phacDiscLoop_SetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_ENABLE_LPCD, PH_ON);
        CHECK_STATUS(status);
#endif /* PH_EXAMPLE1_LPCD_ENABLE */
​
        /* Set Discovery Poll State to Detection */
        status = phacDiscLoop_SetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_NEXT_POLL_STATE, PHAC_DISCLOOP_POLL_STATE_DETECTION);
        CHECK_STATUS(status);
        //未设置成低功耗模式,则启动轮询的相关设置,phacDiscLoop_SetConfig在phacDiscLoop.c
​
        /* Set Poll Configuration */
        status = phacDiscLoop_SetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_PAS_POLL_TECH_CFG, bSavePollTechCfg);
        CHECK_STATUS(status);
​
        /* Switch off RF field */
        status = phhalHw_FieldOff(pHal);
        CHECK_STATUS(status);
​
        /* Start discovery loop */
        status = phacDiscLoop_Run(pDataParams, wEntryPoint);
        //检测轮询开始,更新轮询的状态参量
​
        if(wEntryPoint == PHAC_DISCLOOP_ENTRY_POINT_POLL)//若发现设备
        {
            if((status & PH_ERR_MASK) == PHAC_DISCLOOP_MULTI_TECH_DETECTED)
            {
                DEBUG_PRINTF (" \n Multiple technology detected: \n");
​
                status = phacDiscLoop_GetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_TECH_DETECTED, &wTagsDetected);
                CHECK_STATUS(status);
​
                if(PHAC_DISCLOOP_CHECK_ANDMASK(wTagsDetected, PHAC_DISCLOOP_POS_BIT_MASK_A))
                {
                    DEBUG_PRINTF (" \tType A detected... \n");
                }
                if(PHAC_DISCLOOP_CHECK_ANDMASK(wTagsDetected, PHAC_DISCLOOP_POS_BIT_MASK_B))
                {
                    DEBUG_PRINTF (" \tType B detected... \n");
                }
                if(PHAC_DISCLOOP_CHECK_ANDMASK(wTagsDetected, PHAC_DISCLOOP_POS_BIT_MASK_F212))
                {
                    DEBUG_PRINTF (" \tType F detected with baud rate 212... \n");
                }
                if(PHAC_DISCLOOP_CHECK_ANDMASK(wTagsDetected, PHAC_DISCLOOP_POS_BIT_MASK_F424))
                {
                    DEBUG_PRINTF (" \tType F detected with baud rate 424... \n");
                }
                if(PHAC_DISCLOOP_CHECK_ANDMASK(wTagsDetected, PHAC_DISCLOOP_POS_BIT_MASK_V))
                {
                    DEBUG_PRINTF(" \tType V / ISO 15693 / T5T detected... \n");
                }
​
                /* Select 1st Detected Technology to Resolve*/
                for(bIndex = 0; bIndex < PHAC_DISCLOOP_PASS_POLL_MAX_TECHS_SUPPORTED; bIndex++)
                {
                    if(PHAC_DISCLOOP_CHECK_ANDMASK(wTagsDetected, (1 << bIndex)))
                    {
                        /* Configure for one of the detected technology */
                        status = phacDiscLoop_SetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_PAS_POLL_TECH_CFG, (1 << bIndex));
                        CHECK_STATUS(status);
                        break;
                    }
                }
​
                /* Print the technology resolved */
                phApp_PrintTech((1 << bIndex));
​
                /* Set Discovery Poll State to collision resolution */
                status = phacDiscLoop_SetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_NEXT_POLL_STATE, PHAC_DISCLOOP_POLL_STATE_COLLISION_RESOLUTION);
                CHECK_STATUS(status);
​
                /* Restart discovery loop in poll mode from collision resolution phase */
                status = phacDiscLoop_Run(pDataParams, wEntryPoint);
            }
​
            if((status & PH_ERR_MASK) == PHAC_DISCLOOP_MULTI_DEVICES_RESOLVED)
            {
                /* Get Detected Technology Type */
                status = phacDiscLoop_GetConfig(&sDiscLoop, PHAC_DISCLOOP_CONFIG_TECH_DETECTED, &wTagsDetected);
                CHECK_STATUS(status);
​
                /* Get number of tags detected */
                status = phacDiscLoop_GetConfig(&sDiscLoop, PHAC_DISCLOOP_CONFIG_NR_TAGS_FOUND, &wNumberOfTags);
                CHECK_STATUS(status);
​
                DEBUG_PRINTF (" \n Multiple cards resolved: %d cards \n",wNumberOfTags);
                phApp_PrintTagInfo(pDataParams, wNumberOfTags, wTagsDetected);
​
                if(wNumberOfTags > 1)
                {
                    /* Get 1st Detected Tag and Activate device at index 0 */
                    for(bIndex = 0; bIndex < PHAC_DISCLOOP_PASS_POLL_MAX_TECHS_SUPPORTED; bIndex++)
                    {
                        if(PHAC_DISCLOOP_CHECK_ANDMASK(wTagsDetected, (1 << bIndex)))
                        {
                            DEBUG_PRINTF("\t Activating one card...\n");
                            status = phacDiscLoop_ActivateCard(pDataParams, bIndex, 0);
                            break;
                        }
                    }
​
                    if(((status & PH_ERR_MASK) == PHAC_DISCLOOP_DEVICE_ACTIVATED) ||
                            ((status & PH_ERR_MASK) == PHAC_DISCLOOP_PASSIVE_TARGET_ACTIVATED) ||
                            ((status & PH_ERR_MASK) == PHAC_DISCLOOP_MERGED_SEL_RES_FOUND))
                    {
                        /* Get Detected Technology Type */
                        status = phacDiscLoop_GetConfig(&sDiscLoop, PHAC_DISCLOOP_CONFIG_TECH_DETECTED, &wTagsDetected);
                        CHECK_STATUS(status);
​
                        phApp_PrintTagInfo(pDataParams, 0x01, wTagsDetected);
                    }
                    else
                    {
                        PRINT_INFO("\t\tCard activation failed...\n");
                    }
                }
                /* Switch to LISTEN mode after POLL mode */
            }
            else if (((status & PH_ERR_MASK) == PHAC_DISCLOOP_NO_TECH_DETECTED) ||
                    ((status & PH_ERR_MASK) == PHAC_DISCLOOP_NO_DEVICE_RESOLVED))
            {
                /* Switch to LISTEN mode after POLL mode */
            }
            else if((status & PH_ERR_MASK) == PHAC_DISCLOOP_EXTERNAL_RFON)
            {
                /*
                 * If external RF is detected during POLL, return back so that the application
                 * can restart the loop in LISTEN mode
                 */
            }
            else if((status & PH_ERR_MASK) == PHAC_DISCLOOP_MERGED_SEL_RES_FOUND)
            {
                DEBUG_PRINTF (" \n Device having T4T and NFC-DEP support detected... \n");
​
                /* Get Detected Technology Type */
                status = phacDiscLoop_GetConfig(&sDiscLoop, PHAC_DISCLOOP_CONFIG_TECH_DETECTED, &wTagsDetected);
                CHECK_STATUS(status);
​
                phApp_PrintTagInfo(pDataParams, 1, wTagsDetected);
​
                /* Switch to LISTEN mode after POLL mode */
            }
            else if((status & PH_ERR_MASK) == PHAC_DISCLOOP_DEVICE_ACTIVATED)
            {
                DEBUG_PRINTF (" \n Card detected and activated successfully... \n");
                status = phacDiscLoop_GetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_NR_TAGS_FOUND, &wNumberOfTags);
                CHECK_STATUS(status);
​
                /* Get Detected Technology Type */
                status = phacDiscLoop_GetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_TECH_DETECTED, &wTagsDetected);
                CHECK_STATUS(status);
​
                phApp_PrintTagInfo(pDataParams, wNumberOfTags, wTagsDetected);
​
                /* Switch to LISTEN mode after POLL mode */
            }
            else if((status & PH_ERR_MASK) == PHAC_DISCLOOP_ACTIVE_TARGET_ACTIVATED)
            {
                DEBUG_PRINTF (" \n Active target detected... \n");
                /* Switch to LISTEN mode after POLL mode */
            }
            else if((status & PH_ERR_MASK) == PHAC_DISCLOOP_PASSIVE_TARGET_ACTIVATED)
            {
                DEBUG_PRINTF (" \n Passive target detected... \n");
​
                /* Get Detected Technology Type */
                status = phacDiscLoop_GetConfig(&sDiscLoop, PHAC_DISCLOOP_CONFIG_TECH_DETECTED, &wTagsDetected);
                CHECK_STATUS(status);
​
                phApp_PrintTagInfo(pDataParams, 1, wTagsDetected);
​
                /* Switch to LISTEN mode after POLL mode */
            }
            else if ((status & PH_ERR_MASK) == PHAC_DISCLOOP_LPCD_NO_TECH_DETECTED)
            {
                /* LPCD is succeed but no tag is detected. */
            }
            else
            {
                if((status & PH_ERR_MASK) == PHAC_DISCLOOP_FAILURE)
                {
                    status = phacDiscLoop_GetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_ADDITIONAL_INFO, &wValue);
                    CHECK_STATUS(status);
                    DEBUG_ERROR_PRINT(PrintErrorInfo(wValue));
                }
                else
                {
                    DEBUG_ERROR_PRINT(PrintErrorInfo(status));
                }
            }
​
            /* Update the Entry point to LISTEN mode. */
            wEntryPoint = PHAC_DISCLOOP_ENTRY_POINT_LISTEN;
        }
        else
        {
            if((status & PH_ERR_MASK) == PHAC_DISCLOOP_EXTERNAL_RFOFF)
            {
                /*
                 * Enters here if in the target/card mode and external RF is not available
                 * Wait for LISTEN timeout till an external RF is detected.
                 * Application may choose to go into standby at this point.
                 */
                status = phhalHw_EventConsume(pHal);
                CHECK_STATUS(status);
​
                status = phhalHw_SetConfig(pHal, PHHAL_HW_CONFIG_RFON_INTERRUPT, PH_ON);
                CHECK_STATUS(status);
​
                status = phhalHw_EventWait(pHal, LISTEN_PHASE_TIME_MS);
                if((status & PH_ERR_MASK) == PH_ERR_IO_TIMEOUT)
                {
                    wEntryPoint = PHAC_DISCLOOP_ENTRY_POINT_POLL;
                }
                else
                {
                    wEntryPoint = PHAC_DISCLOOP_ENTRY_POINT_LISTEN;
                }
            }
            else
            {
                if((status & PH_ERR_MASK) == PHAC_DISCLOOP_ACTIVATED_BY_PEER)
                {
                    DEBUG_PRINTF (" \n Device activated in listen mode... \n");
                }
                else if ((status & PH_ERR_MASK) == PH_ERR_INVALID_PARAMETER)
                {
                    /* In case of Front end used is RC663, then listen mode is not supported.
                     * Switch from listen mode to poll mode. */
                }
                else
                {
                    if((status & PH_ERR_MASK) == PHAC_DISCLOOP_FAILURE)//未检测到
                    {
                        status = phacDiscLoop_GetConfig(pDataParams, PHAC_DISCLOOP_CONFIG_ADDITIONAL_INFO, &wValue);
                        CHECK_STATUS(status);
                        DEBUG_ERROR_PRINT(PrintErrorInfo(wValue));
                    }
                    else
                    {
                        DEBUG_ERROR_PRINT(PrintErrorInfo(status));
                    }
                }
​
                /* On successful activated by Peer, switch to LISTEN mode */
                wEntryPoint = PHAC_DISCLOOP_ENTRY_POINT_POLL;
            }
        }
    }
}

Example 2 – Advanced Discovery Loop

除了示例1之外,高级发现循环示例还说明了发现循环的不同配置选项,并根据感兴趣的配置文件NFC或EMVCo将默认值配置为DiscoveryLoop。“ DiscoveryLoop”的配置在“ LoadProfile()”中实现 功能。

Example 3 – NFC Forum

构建NFC网络。解释如何为不同的P2P模式(例如活动模式,目标模式,发起方模式和SNEP客户端/服务器)配置NFC读取器库。 在Snep Server模式下,该示例等待来自Snep Client的连接。 建立客户端与服务器之间的连接后,客户端将发送数据,服务器将读取该数据。 该应用程序在LPCXpresso IDE的控制台窗口中显示读取的数据。在Snep客户端模式下,该应用程序尝试连接到Snep服务器。 建立连接后,它将向服务器发送NDEF消息。

Example 4 – MIFARE Classic

本示例演示如何配置“ DiscoveryLoop”以仅轮询一种技术以及如何解析检测到的卡,在本示例中,将使用MIFARE。一旦激活了MIFARE卡,便会打印应用程序打印输出信息(如UID,ATQA和SAK)并执行身份验证 使用MIFARE卡的默认密钥。 身份验证成功后,将实现基本的读/写操作。 如果仅使用一张卡或查看如何管理MIFARE卡,则此示例是一个很好的开始。

Example 6 – EMVCo Loopback

EMVCo环回的例子是一个环回应用程序,它被用来执行EMVCo 2.5一级数字遵从性验证。示例可以这样使用,而不需要对EMVCo认证进行任何更改。

Example 7 – EMVCo Polling

EMVCo轮询示例演示了如何按照EMVCo规范指定的配置NFC阅读器库并启动对EMVCo卡的轮询。

NFC Reader Library是基于NFC读者库[5]开发的。它的目标是简单,模块化,易于阅读和快速便携的所有客户。这种哲学反映在它的架构中,它分为4层:BAL(总线抽象层),HAL(硬件抽象层),PAL(协议抽象层),AL(抽象层),以对应缩写开头的函数均可在其中找到对应的函数定义。

相关代码阅读

PN512的寄存器

详情参见PN512 Datasheet中的9.1 PN512 registers overview。

page0的寄存器

根据寄存器的功能不同,寄存器的访问条件也不同。原则上,具有相同行为的位在共同寄存器中分组。接入条件如下:r/w可读写,dy动态,r只读,w只写,RFU和RFT保留。

寄存器各数据位的性质说明

参见9.2 Register description章节可以得到各个寄存器的设置内容及说明。

可以通过结构体,将寄存器的值写成常量。

code unsigned char CardTypeSetReg[ ][5]={  
{0x08,0,0x08,0x08,0x08},  
{0x0C,1,0x10,0x10,0x10},  
{0x11,2,0x38,0x39,0x3B},   
{0x12,2,0x92,0x00,0x83},  
{0x13,2,0x92,0x00,0x83},  
{0x14,2,0x83,0x83,0x83},  
{0x15,2,0x00,0x40,0x00},  
{0x16,2,0x10,0x10,0x10},  
{0x17,2,0x84,0x86,0x86},  
{0x18,2,0x55,0x84,0x44},  
{0x19,2,0x41,0x4D,0x4D},  
{0x1A,2,0x00,0x00,0x00},  
{0x1D,2,0x11,0x10,0x10},  
{0x1E,2,0x00,0x00,0x90},  
{0x23,2,0x88,0x88,0x88},  
{0x24,2,0x26,0x26,0x26},  
{0x26,2,0x79,0x7F,0x79},  
{0x27,2,0x88,0x88,0x88},  
{0x28,2,0x30,0x20,0x20},  
{0x29,2,0x06,0x06,0x06}   
};
//第一列是寄存器地址;
//第二列为寄存器设置方法:0表示清0对应位,1表示置1对应位,2表示直接写入;
//第三列是操作FELICA设置值;
//第四列是操作ISO/IEC 14443A/MIFARE设置值;
//第五列是操作ISO/IEC 14443B设置值。

如代码第一行,表示在Status2Reg寄存器中,清零MFCrypto1On位,这表示MIFARE Crypto1单元已打开,因此与卡的所有数据通信都已加密。只有成功执行MFAuthent命令,该位才能设置为逻辑1,而且必须由软件清零。

PN512支持使用SPI,I2C总线或串行UART接口直接连接主机。 PN512在执行上电或硬重置后会重置其接口并自动检查当前主机接口类型。 PN512通过在复位阶段之后检测控制引脚上的逻辑电平来识别主机接口。 这是通过固定引脚连接的组合来完成的。 表140显示了不同的连接配置。

硬件引脚的连接配置

我们选用SPI通信协议与芯片进行通信。

PN512在SPI通信期间充当从机。 SPI时钟信号SCK必须由主机产生。 从主机到从机的数据通信使用MOSI线。 MISO线用于将数据从PN512发送到主机.MOSI和MISO线上的数据字节均首先通过MSB发送。 MOSI和MISO线上的数据必须在时钟的上升沿保持稳定,并且可以在下降沿改变。 数据由PN512在时钟下降沿提供,并在时钟上升沿保持稳定。数据格式如下:

读取数据的数据帧格式

写入数据的数据帧格式

所有数据帧都必须从最高位开始读取或者写入。地址字节必须符合以下格式。第一个字节的MSB定义所使用的模式。 要从PN512读取数据,MSB设置为逻辑1。要将数据写入PN512,MSB必须设置为逻辑0。位6至1定义地址,而LSB设置为逻辑0。

地址字节的格式

硬使能下电:当引脚NRSTPD低时,使能硬下电。这将关闭包括振荡器在内的所有内部电流吸收。所有的数字输入缓冲器都与输入引脚分离,并夹在内部(除了引脚NRSTPD)。输出引脚被冻结在高电平或低电平。

软使能下电:将CommandReg寄存器的PowerDown位设置为逻辑1后,立即进入软掉电模式。所有内部电流吸收器(包括振荡器缓冲器)均被关闭。 但是,数字输入缓冲器并未与输入引脚分开,并保持其功能。 数字输出引脚不会更改其状态。

PN512的命令集

PN512操作由能够执行一组命令的状态机决定。命令是通过向CommandReg寄存器写入命令代码来执行的。

PN512命令集

idle:将PN512置于空闲模式。 空闲命令也会自行终止

mem:从FIFO缓冲区到内部缓冲区传输25个字节。要从内部缓冲区读出25个字节,Mem命令必须从一个空的FIFO缓冲区开始。在这种情况下,25个字节从内部缓冲区传输到FIFO。在硬断电期间(使用引脚NRSTPD),内部缓冲区中的25个字节保持不变,只有当电源从PN512中移除时才会丢失。该命令在完成时自动终止,Idle命令变为活动状态。

Generate RandomID:这个命令生成一个10字节的随机数,最初存储在内部缓冲区中。然后重写内部25字节缓冲区中的10个字节。该命令在完成时自动终止,并且PN512返回到空闲模式。

CalcCRC:FIFO缓冲区内容被转移到CRC协处理器和CRC计算被启动。计算结果存储在CRCResultReg寄存器中。CRC计算不受特定字节数的限制。当数据流期间FIFO缓冲区为空时,计算不会停止。写入FIFO缓冲区的下一个字节被添加到计算中。CRC预设值由ModeReg寄存器的CRCPreset[1:0]位控制。该值在命令启动时加载到CRC协处理器中。该命令必须通过向CommandReg寄存器写入命令(例如Idle命令)来终止。如果AutoTestReg寄存器的SelfTest[3:0]位被正确设置,PN512进入自检模式。启动CalcCRC命令启动数字自检。自我测试的结果被写入FIFO缓冲区。

Transmit:启动此命令后,FIFO缓冲区的内容立即被传输。在传输FIFO缓冲区内容之前,必须为数据传输设置所有相关的寄存器。当FIFO缓冲区为空时,此命令自动终止。它可以被写入commandg寄存器的另一个命令终止。

NoCmdChange:该命令不会影响命令寄存器中正在运行的任何命令。它可以被用来操作除commandregister命令[3:0]之外的任何位,例如RcvOff位或PowerDown位。

Receive:PN512激活接收路径并等待数据流被接收。在启动此命令之前,必须选择正确的设置。当数据流结束时,该命令自动终止。根据所选择的帧类型和速度,这可以通过帧模式的结尾或长度字节来表示。备注:如果RxModeReg寄存器的RxMultiple位设置为逻辑1,接收命令不会自动终止。它必须通过在CommandReg寄存器中启动另一个命令来终止。

Transceive:此命令连续重复从FIFO缓冲区发送数据和从RF字段接收数据。 第一个动作是发送,在发送之后,将命令更改为接收数据流。必须通过将BitFramingReg寄存器的StartSend位设置为逻辑1来启动每个发送过程。必须通过将任何命令写入CommandReg寄存器来清除该命令。注意:如果RxModeReg寄存器的RxMultiple位设置为逻辑1,则Transceive命令将永远不会离开接收状态,因为该状态无法自动取消。

MFAuthent:这个命令管理MIFARE身份验证,以允许与任何MIFARE Mini、MIFARE 1K和MIFARE 4K卡进行安全通信。在命令可以被激活之前,将下列数据写入FIFO缓冲区:•Authentication command code (60h, 61h)•Block address•Sector key byte 0•Sector key byte 1•Sector key byte 2•Sector key byte 3•Sector key byte 4•Sector key byte 5•Card serial number byte 0•Card serial number byte 1•Card serial number byte 2•Card serial number byte 3总共12个字节的数据写进FIFO的缓冲区中。当MFAuthent命令被激活时,所有对FIFO缓冲区的访问都被阻塞。然而,如果有访问FIFO缓冲区,ErrorReg寄存器的WrErr位被设置为1。当MIFARE卡通过身份验证并且Status2Reg寄存器的MFCrypto1On位设置为逻辑1时,此命令自动终止。如果卡未应答,该命令不会自动终止,因此必须将计时器初始化为自动模式。 在这种情况下,除了IdleIRq位之外,TimerIRq位还可以用作终止条件。 在身份验证处理期间,RxIRq位和TxIRq位被阻止。 Crypto1On位仅在终止MFAuthent命令后才有效,无论是在处理协议还是将Idle写入CommandReg寄存器之后。如果在身份验证过程中发生错误,则将ErrorReg寄存器的ProtocolErr位设置为逻辑1,状态2Reg寄存器的Crypto1On位为 设置为逻辑0。

SoftReset:该命令用于复位设备。内部缓冲区的配置数据保持不变。所有寄存器都设置为重置值。该命令在完成时自动终止。

部分代码

使用模拟SPI对芯片进行控制。

//void SPI_GPIO_Initializes(void)
void SPI_Initializes(void)
{
    SPI_GPIO_Initializes();
    SPI_CS_OutHigh;//CS拉低通讯开始
    SPI_SCK_HIGH;//SCK拉低时读取数据
    SPI_MOSI_HIGH;//输出拉高,模拟SPI必须对IO进行初始电平设置,不然会对第一次读取造成问题
}
uint8_t SPI_ReadByte(uchar Address)//读一个字节的数据
{
     unsigned char i, ucAddr;
     unsigned char ucResult=0;
​
     SCK = 0;
     NSS = 0;
     ucAddr = ((Address<<1)&0x7E)|0x80;
    //先发送地址
     for(i=8;i>0;i--)
     {
         MOSI = ((ucAddr&0x80)==0x80);
         SCK = 1;
         ucAddr <<= 1;
         SCK = 0;
     }
    //再读取信息
     for(i=8;i>0;i--)
     {
         SCK = 1;
         ucResult <<= 1;
         ucResult|=MISO;
         SCK = 0;
     }
​
     NSS = 1;
     SCK = 1;
     return ucResult;
}
uint8_t SPI_WriteByte(uchar Address,uchar value)
{
    unsigned char i, ucAddr;
​
    SCK = 0;
    NSS = 0;
    ucAddr = ((Address<<1)&0x7E);
​
    for(i=8;i>0;i--)
    {
        MOSI = ((ucAddr&0x80)==0x80);
        SCK = 1;
        ucAddr <<= 1;
        SCK = 0;
    }
​
    for(i=8;i>0;i--)
    {
        MOSI = ((value&0x80)==0x80);
        SCK = 1;
        value <<= 1;
        SCK = 0;
    }
    NSS = 1;
    SCK = 1;
}
void SetBitMask(u8 reg,u8 mask)  
{
    char tmp = 0x0;
    tmp = SPI_ReadByte(reg);
    SPI_WriteByte(reg,tmp | mask);  // set bit mask
}
void ClearBitMask(unsigned char reg,unsigned char mask)  
{
    char tmp = 0x0;
    tmp = SPI_ReadByte(reg);
    SPI_WriteByte(reg, tmp & ~mask);  // clear bit mask
}

之后是设置PN512的代码

char PcdReset(void)
{
    RST=1;
    Delay_ms(40);
    RST=0;
    Delay_ms(40);
    RST=1
    Delay_ms(40);
    SPI_WriteByte(CommandReg,PCD_RESETPHASE);
    Delay_ms(40);
    
    SPI_WriteByte(ModeReg,0x3D);            //和Mifare卡通讯,CRC初始值0x6363
    SPI_WriteByte(TReloadRegL,30);           
    SPI_WriteByte(TReloadRegH,0);
    SPI_WriteByte(TModeReg,0x8D);
    SPI_WriteByte(TPrescalerReg,0x3E);
    SPI_WriteByte(TxAutoReg,0x40);     
    return 1;//初始化完成
}
//设置PN512的工作模式
char PcdConfigISOType(unsigned char type)
{
    unsigned char temp=0;
   if(type == 'A')                     //ISO14443_A
   { 
         SPI_WriteByte(Status2Reg,0x08);         
         SPI_WriteByte(ControlReg,0x10);    
         SPI_WriteByte(TxModeReg,0x00);      
         SPI_WriteByte(RxModeReg,0x08);  
         SPI_WriteByte(TxAutoReg,0x40); 
         SPI_WriteByte(DemodReg,0x4D);  
         SPI_WriteByte(GsNOnReg,0xF4);
         //SPI_WriteByte(CWGsPReg,0x3f);
         SPI_WriteByte(RxThresholdReg,0x75);
         SPI_WriteByte(RFCfgReg,0x67);  
         SPI_WriteByte(ModeReg,0x3D);
         SPI_WriteByte(TxControlReg,0x00);
         SPI_WriteByte(CollReg,0x80);
         SPI_WriteByte(TReloadRegL,30);//tmoLength);
       // TReloadVal = 'h6a =tmoLength(dec) 
        SPI_WriteByte(TReloadRegH,0);
        SPI_WriteByte(TModeReg,0x8D);
        SPI_WriteByte(TPrescalerReg,0x3E);
   }
     else if (type == 'B') //ISO14443_B//ID_Card
   {
         SPI_WriteByte(CommandReg,PCD_RESETPHASE);//SoftReset
         SPI_WriteByte(ControlReg, 0x10);
         SPI_WriteByte(ModeReg, 0x3b);
         SPI_WriteByte(TxModeReg, 0x83);
         SPI_WriteByte(RxModeReg, 0x83);//RxModeReg ----select type b
//       SPI_WriteByte(TxControlReg, 0x83);//2016--3--5  add
         SPI_WriteByte(TxAutoReg, 0x00);
         SPI_WriteByte(TxSelReg, 0x10);
         SPI_WriteByte(RxSelReg, 0x86);
         SPI_WriteByte(RxThresholdReg, 0x77);
         SPI_WriteByte(DemodReg, 0x4D);
         SPI_WriteByte(ManualRCVReg, 0x10);
//       SPI_WriteByte(TypeBReg, 0x93); //TypeBReg(1EH)-------AutoTestReg(36H)
//       SPI_WriteByte(TypeBReg, 0x83);
         SPI_WriteByte(TypeBReg, 0x90);
         SPI_WriteByte(GsNOffReg, 0x88);
         SPI_WriteByte(ModWidthReg, 0x26);
         SPI_WriteByte(RFCfgReg, 0x59);
         SPI_WriteByte(GsNOnReg, 0x88);
         SPI_WriteByte(CWGsPReg, 0x20);
         SPI_WriteByte(ModGsPReg, 0x06);
         ///*****************************///
         SPI_WriteByte(TxControlReg,0x00);
   }
     else{ return (char)-1; }
   
   return 1;
}

通过PN512与ISO14443卡通讯,A卡B卡取决于初始设定值。

//          Command[IN]:RC522命令字
//          pInData[IN]:通过RC522发送到卡片的数据
//          InLenByte[IN]:发送数据的字节长度
//          pOutData[OUT]:接收到的卡片返回数据
//          *pOutLenBit[OUT]:返回数据的位长度
char PcdComMF522(u8 Command, 
                 u8 *pInData, 
                 u8 InLenByte,
                 u8 *pOutData, 
                 u16 *pOutLenBit)
{
    char status = MI_ERR;
     u8 irqEn   = 0x00;
     u8 waitFor = 0x00;
     u8 lastBits;
     volatile u8 n;
     volatile u16 i;
    switch (Command)
    {
       case PCD_AUTHENT:
          irqEn   = 0x12;
          waitFor = 0x10;
          break;
       case PCD_TRANSCEIVE:
          irqEn   = 0x77;
          waitFor = 0x30;
          break;
       default:
         break;
    }
   
    SPI_WriteByte(ComIEnReg,irqEn|0x80);
    ClearBitMask(ComIrqReg,0x80);
    SPI_WriteByte(CommandReg,PCD_IDLE);
    SetBitMask(FIFOLevelReg,0x80);
    
    for(i=0; i<InLenByte; i++){ SPI_WriteByte(FIFODataReg, pInData[i]); }
    SPI_WriteByte(CommandReg, Command);
        if(Command == PCD_TRANSCEIVE){ SetBitMask(BitFramingReg,0x80);}
    
//    i = 600;//根据时钟频率调整,操作M1卡最大等待时间25ms
    i = 20000;
    do{
         n = SPI_ReadByte(ComIrqReg);
         i--;
    }while ((i!=0) && !(n&0x01) && !(n&waitFor));
    ClearBitMask(BitFramingReg,0x80); 
    if (i!=0)
    {
         if(!(SPI_ReadByte(ErrorReg)&0x1B))
         {
             status = MI_OK;                     
             if(n & irqEn & 0x01)
             {   status = MI_NOTAGERR; }
             if(Command == PCD_TRANSCEIVE)
             {
                n = SPI_ReadByte(FIFOLevelReg);
                lastBits = SPI_ReadByte(ControlReg) & 0x07;
                if (lastBits)
                {   *pOutLenBit = (n-1)*8 + lastBits;   }
                else
                {   *pOutLenBit = n*8;   }
                if (n == 0)
                {   n = 1;    }
                if (n > MAXRLEN)
                {   n = MAXRLEN;   }
                for (i=0; i<n; i++)
                {   pOutData[i] = SPI_ReadByte(FIFODataReg);    }
            }
         }
         else
         {   status = MI_ERR;   }
        
   }
   SetBitMask(ControlReg,0x80);           // stop timer now
   SPI_WriteByte(CommandReg,PCD_IDLE); 
   return status;
}
void PcdAntennaOn()//开启天线
{
    unsigned char i;
    i = ReadRawRC(TxControlReg);
    if (!(i & 0x03))
    {
        SetBitMask(TxControlReg, 0x03);
    }
}
void PcdAntennaOff()//关闭天线
{
    ClearBitMask(TxControlReg, 0x03);
}
//PN512的初始化代码
void PN512_Init(unsigned char choose_mode)
{
    PcdReset(); /*Reset PN512*/
    PcdAntennaOff();
    delay_ms(2);//每次开启或者关闭天线必须经过1ms的延时
    PcdAntennaOn();          /*open wireless*/
    if(choose_mode==0)
    PcdConfigISOType('A'); /* Configure PN512 Work at TYPEA Mode*/
    else
    PcdConfigISOType('B'); /* Configure PN512 Work at TYPEA Mode*/
    PcdAntennaOff();
    delay_ms(2);
    PcdAntennaOn();          /*open wireless*/
//  PcdHalt();
}

 

参考资料

今天的文章PN512使用调研分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/24304.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注