NFC P2P with TRF7970A and TivaWare

The latest TivaWare release (2.1.0.12573) includes several new examples, one of which is an NFC P2P demo and NFC stack using the TRF79x0A chip set. This is a brief introduction on how to use the NFC stack provided as part of TivaWare 2.1.0.12573  to do NFC P2P between a Tiva C Micro-controller and an NFC Enabled phone/tablet (preferably android). This article will demonstrate how to use NFClib with a Tiva Launchpad and a TRF7970ABD BoosterPack to talk to an NFC enabled Android phone via NFC P2P. NFC Specs are pulled from here: http://www.nfc-forum.org/specs/spec_list/ I highly recommend downloaded and reading the NDEF and Text / URI record specs.

Background

For background on NFC please see my post on NFC NDEF Basics. A decent grasp of the NFC NDEF layer is critical to using the current implementation of nfclib.

Hardware: the boosterpacks (EM and XL headers)

Currently there are two boosterpacks for the TRF7970A chip. For XL headers compatible with the Tiva Evaluation Kits such as the EK-TM4C123gxl (aka the Tiva Launchpad) there is the DLPTRF7970ABD BoosterPack. Note that these headers are not in fact XL headers, though they are compatible with XL headers.

front and back of DLP NFC BoosterPack
front and back of DLP NFC BoosterPack

For EM headers compatible with the Tiva Development Kits such as the DK-TM4C129x and the DK-TM4C123G there is the TRF7970ATB BoosterPack.

trf7970atb front back picture
trf7970atb front back picture

For more details on the BoosterPack standard and the difference between XL, XXL, EM headers and the ilk please see this handy wiki provided by TI. http://processors.wiki.ti.com/index.php/BYOB

Software: Using NFClib

NFClib can be found in the Tivaware/nfclib/ directory. There are several layers to NFClib and its associated stack, for the purposes of this tutorial we will focus on the highest level interface and the structures associated with it.

The NFC Hardware Abstraction Layer:

Each example for NFClib must provide a hardware abstraction layer, a .h file that maps the signals to pins for what ever board you are using. In the nfc_p2p_demo example provided by tivaware this is the trf79x0_hw.h file which is added to every project via the following command. An example file for the configuration can be found in nfclib/trf79x0_hw_example.h .

#include "./trf79x0_hw.h"

Required Software Constructs:

NFClib is not fully standalone, it does require a few things from the system using it.

  • A timer to count down the timeout and set a timeout flag
  • The IRQ pin interrupt handler mapped to TRF79x0IRQPinInterruptHandler()
  • Set the g_eRFDaughterType variable in your code to indicate which boosterpack currently in use.

Initializing the NFC System

To get the TRF7970A up and running you will need to do the following NFC specific things. (Also set the pinout, the system clock, initialize UART, etc… )

  • Set the boosterpack type variable ( g_eRFDaughterType = RF_DAUGHTER_TRF7970ABP )
  • Initialize the transceiver with TRF79x0Init() .
  • Initialize the software stack with NFCP2P_init(eCurrentTRF79x0Mode, FREQ_212_KBPS) or whatever speed you’re using.
  • At this point you should go into a main loop that switches back and forth between passive target and initiator mode (see nfc_p2p_demo.c for an example of this in the main loop)

 Using the NFC System

Scan for valid Packets
NFC P2P operates by switching back and forth between Initiator and Passive Target mode. In the current implementation of nfclib the user has to switch back and forth between Initiator and Passive Target mode manually and check to see if valid data has been received. In the following example there is a while loop that switches between Initiator and Passive Target mode, checks for valid data, and then processes the data according to the type. This basic while loop should be the main loop of any program using nfclib. It can be an interrupt, or the entire program, your choice.

//
// Variable to hold current NFC mode
//
tTRF79x0TRFMode eCurrentTRF79x0Mode = P2P_PASSIVE_TARGET_MODE;

//
// Receive Status Object from Low Level SNEP/NFC Stack
//
sNFCP2PRxStatus         g_sTRFReceiveStatus;

while(1)
{
    //
    // NFC-P2P-Initiator-Statemachine, switch between modes
    //
    if(NFCP2P_proccessStateMachine() == NFC_P2P_PROTOCOL_ACTIVATION)
    {
        //
        // Switche between modes
        //
        if(eCurrentTRF79x0Mode == P2P_INITATIOR_MODE)
        {
            eCurrentTRF79x0Mode = P2P_PASSIVE_TARGET_MODE;
        }
        else if(eCurrentTRF79x0Mode == P2P_PASSIVE_TARGET_MODE)
        {
            eCurrentTRF79x0Mode = P2P_INITATIOR_MODE;
        }

        //
        // Initialize into new mode, 
        // this function actually performs the switch between
        // Initator mode <=> Passive Target mode
        //
        NFCP2P_init(eCurrentTRF79x0Mode,FREQ_212_KBPS);
    }

    //
    // Read the receive status structure - check if there is a received
    // packet from the Target. If the record received is not a fragment
    // process it
    //
    g_sTRFReceiveStatus = NFCP2P_getReceiveState();
    if(g_sTRFReceiveStatus.eDataReceivedStatus != RECEIVED_NO_FRAGMENT)
    {
        //
        // Process the received message
        //
    }
}

Read Tag
To process the received message there are a series of encoder / decoder functions in nfclib/nfc_p2p.c and data structures defined in nfclib/nfc_p2p.h . To read a tag you must do two things.

  1. First call the message header decoder function (NFCP2P_NDEFMessageDecoder()) to find out what the message type is (the sNDEFMessageData.pui8Type field, ‘T’ for TextRecord, ‘U’ for URI,’Sp’ for SmartPoster … etc).
  2. Second call the decoder function for that type on the payload (NFCP2P_NDEFTextRecordDecoder() for TextRecord, NFCP2P_NDEFURIRecordDecoder() for URIRecord … etc). With the structure returned from the payload field decoder function any data about the payload can be extracted.
//
// This code would go in the 'Process the received message' section
// in the previous example. This code will decode the received message
// and check for its type, then call the appropriate decode function.
//

#include "nfclib/nfc_p2p.h"

//
// Global Structures, used for easy access to the data field.
// see nfclib/nfc_p2p.h for details
//
sNDEFMessageData        g_sMessageHeader;
sNDEFTextRecord         g_sTextRecord;
sNDEFURIRecord          g_sURIRecord;
sNDEFSmartPosterRecord  g_sSPRecord;

//
// Copy Volatile stack buffer into semi-stable buffer for processing.
//
for(x=0;x<g_sTRFReceiveStatus.ui8DataReceivedLength;x++)
{
    g_ui8NFCP2PRawTagBuffer[x]=g_sTRFReceiveStatus.pui8RxDataPtr[x];
}

//
// Decode Message Header from Buffer to Struct
//
NFCP2P_NDEFMessageDecoder(&g_sMessageHeader,
                          g_ui8NFCP2PRawTagBuffer,
                          sizeof(g_ui8NFCP2PRawTagBuffer));

//
// Determine TypeID, this turns the characters into a numeric string
// so the switch statment can be used. 
//
for(x=0,TypeID=0;x<g_sMessageHeader.ui8TypeLength;x++)
{
    TypeID=(TypeID<<8)+g_sMessageHeader.pui8Type[x];
}

//
// Handler for different Message Payload Types
//
switch(TypeID)
{
    case NDEF_TYPE_TEXT: // 'T' = 0x54
    {
        //
        // Decode the Record from g_sMessageHeader Buffer to g_sTextRecord 
        // Struct
        //
        NFCP2P_NDEFTextRecordDecoder(&g_sTextRecord,
                                    g_sMessageHeader.pui8PayloadPtr,
                                    g_sMessageHeader.ui32PayloadLength
                                    );

        //
        // The payload data can be read from g_sTextRecord.pui8Text[]
        //
        break;
    }
    case NDEF_TYPE_URI: // 'U' = 0x55
    {
        //
        // Decode the Record from g_sMessageHeader Buffer to g_sURIRecord 
        // Struct
        //
        NFCP2P_NDEFURIRecordDecoder(&g_sURIRecord,
                                    g_sMessageHeader.pui8PayloadPtr,
                                    g_sMessageHeader.ui32PayloadLength
                                    );

        //
        // The payload data can be read from g_sURIRecord.puiUTF8String[]
        //
        break;
    }
    case NDEF_TYPE_SMARTPOSTER: // 'Sp' = 0x5370
    {
        //
        // Calculate maximum size remaining in buffer
        // The size remaining = Total size - size used by header
        //
        ui16MaxSizeRemaining = (sizeof(g_ui8NFCP2PRawTagBuffer) -
                                (g_sMessageHeader.pui8PayloadPtr -
                                    &g_ui8NFCP2PRawTagBuffer[0]));
        //
        // Decode the Record from g_sMessageHeader Buffer to g_sSPRecord 
        // Struct
        //
        NFCP2P_NDEFSmartPosterRecordDecoder(&g_sSPRecord,
                                    g_sMessageHeader.pui8PayloadPtr,
                                    ui16MaxSizeRemaining,
                                    g_sMessageHeader.ui32PayloadLength
                                    );
        
        //
        // The data can now be read from the various sub structures in 
        // g_sSPRecord. See nfclib/nfc_p2p.h sNDEFSmartPosterRecord for 
        // more info.
        //
        break;
    }
    default: 
    {
        //
        // If you get here the Tag Type isn't recognized / supported
        //
        break;
    }
}

Write Tag
To write a tag out you must do 5 things.

  1. Fill out the appropriate payload record data structure (sNDEFURIRecord for a URI tag, sNDEFTextRecord for a Text record, etc).
  2. Encode the payload record data using the appropriate encorder function.(NFCP2P_NDEFURIRecordEncoder, NFCP2P_NDEFTextRecordEncoder, etc)
  3. Fill out the meta data for the message header in a sNDEFMessageData strcture. point the pui8PayloadPtr at the buffer returned by the payload record encoder.
  4. Encode the message header data into a buffer using NFCP2P_NDEFMessageEncoder), which will return a buffer of the fully encoded tag
  5. The buffer returned from the MessageEncoder function then gets passed to NFCP2P_sendPacket() which sends the stream to the transceiver to be broadcast.
//
// This example will write a URI Tag with the address 'www.austinblackstone.com'
//

#include "nfclib/nfc_p2p.h"

//
// Global Structures, used for easy access to the data field.
// see nfclib/nfc_p2p.h for details
//
sNDEFMessageData        g_sMessageHeader;
sNDEFURIRecord          g_sURIRecord;

//
// Buffers for Encoding NFC Messages
//
uint8_t g_pui8MessageBuffer[SNEP_MAX_BUFFER];
uint8_t g_pui8PayloadBuffer[SNEP_MAX_BUFFER];

//
// Variables Specific to this example
//
uint8_t  ui8Webpage[]="austinblackstone.com";
uint32_t ui32length;

//
// Start Setting Header Information
// This says the message is 1 record long, sent in 1 burst, is short,
// has no ID field, and is a well known type of record.
//
g_sMessageHeader.sStatusByte.MB = 1;     // Message Begin
g_sMessageHeader.sStatusByte.ME = 1;     // Message End
g_sMessageHeader.sStatusByte.CF = 0;     // Record is Not Chunked
g_sMessageHeader.sStatusByte.SR = 1;     // Record is Short Record
g_sMessageHeader.sStatusByte.IL = 0;     // ID Length =0 (No ID Field)
g_sMessageHeader.sStatusByte.TNF = TNF_WELLKNOWNTYPE;

//
// Set Type to URI ('U')
// Set Type Lengh to 1 Character
//
g_sMessageHeader.pui8Type[0] = 'U';   // 'U' is the Type for URI's
g_sMessageHeader.ui8TypeLength = 1;   // TypeLengh is 1 char long ('U')
//g_sMessageHeader.ui8IDLength = ;    // not needed, IL=0 so no ID given
//g_sMessageHeader.pui8ID =;          // not needed, IL=0 so no ID given

//
// Set URI Record Info
//
// prepend the URI with 'http://www.' (see nfc_p2p.h for a full list)
// set the webpage as the URI string
// Set the Length of the String
//
g_sURIRecord.eIDCode = http_www;
g_sURIRecord.puiUTF8String = ui8Webpage;
g_sURIRecord.ui32URILength = sizeof(ui8Webpage);

//
// Encode the URI Record into the Payload Buffer, will return the length
// of the buffer written in the ui32length variable
//
NFCP2P_NDEFURIRecordEncoder(g_sURIRecord, 
                            g_pui8PayloadBuffer,
                            sizeof(g_pui8PayloadBuffer),
                            &ui32length);

//
// Finish Setting Header Information
// Set the Length of the Payload and the Payload Pointer in the
// Message Header Struct
//
g_sMessageHeader.ui32PayloadLength = ui32length;
g_sMessageHeader.pui8PayloadPtr = g_pui8PayloadBuffer;

//
// Encode the Header and the Payload into the Message Buffer
//
NFCP2P_NDEFMessageEncoder(g_sMessageHeader, 
                          g_pui8MessageBuffer,
                          sizeof(g_pui8MessageBuffer), 
                          &ui32length);

//
// Send the NFC Message data to the stack for processing.
// This step physically writes the buffer out to the transceiver
//
NFCP2P_sendPacket(g_pui8MessageBuffer, ui32length);

Currently this is the highest level API for nfclib, and the data structures are only in place and tested for NFC Well Known Types, and even then only a limited subset. If a wider subset is desired it should be a simple matter of writing wrapper functions based on the NFC Specification and adding them as sub structures / payloads in nfc_p2p.c and nfc_p2p.h . Now, go forth and have fun with NFC!