Fun With RFID and NFC
See how you can set up your own RFID reader, among other projects, as Erich Styger guides you through the hardware, code, and components needed.
Join the DZone community and get the full member experience.
Join For FreePlaying with RFID and NFC is definitely fun, and they are everywhere! For a research project, I’m exploring different RFID tags and solutions. I kept several types around for a long time, but I never found the time to actually work on them. Last night, I thought I would give it a try, and I have it working with GNU ARM and Eclipse, powered by the NXP FRDM-K64F board.
I had the NXP kit (PN7120 NFC Controller SBC Kit, OM5577/PN7120S) available, which came with adapter boards for BeagleBone and Raspberry Pi:
There is another version of that kit for Arduino (see https://community.nxp.com/docs/DOC-331907), but I was not able to order that one. To use the BeagleBone and Raspberry would be something the future.
There were several articles and tutorials available, but they didn't all work out-of-the-box for a variety of reasons. So, I ended up doing my special tutorial, with the added benefits that I learned a lot about how to use that board, and I was able to fix issues in the NXP application note and demo code.
Materials
In this tutorial, I’m using the following:
- Eclipse with GNU tools (Kinetis Design Studio V3.2.0, but any other IDE or toolchain could be used): See NXP Kinetis Design Studio v3.2.0.
- Kinetis SDK V2.0: see Kinetis SDK V2.0.
- Processor Expert with McuOnEclipse components (see “McuOnEclipse Releases on SourceForge“).
- NXP FRDM-K64F board (other boards could be used).
- NXP PN7120 Controller board (PN7120 NFC Controller SBC Kit).
- RFID tags for testing (there are two RFID tags included in the above kit).
- Wires and cables.
The projects and sources are posted on GitHub:
NXP PN7120
The NXP PN7120 is an integrated IC with an ARM Cortex-M0, which handles all RF communication and NFC related protocols:
It simplifies my design as the ARM Cortex-M0 on the PN7120 does all the low-level RF stuff — and I can talk to the M0 from another microcontroller over I²C. NXP delivers the library to talk to the PN7120 in the full source code as ‘NfcLibrary’. This is what I’m going to use in my application.
Hardware Connections
I²C is used as the communications interface with the NFC controller board. The board has the following pins available on each side:
So all I need is power (3.3V and GND), an I²C connection (SCL and SDA), an interrupt input pin (IRQ), and a GPIO output pin (VEN). The pins are available on both sides of the module:
I’m using a header, which I put into the side socket as a connector so I don’t need to solder anything:
The image below shows my wiring to the FRDM-K64F. I’m using the following pins:
- 3.3V ==> 3.3V.
- SCL ==> PTE24 (I2C0).
- SDA ==> PTE23 (I2C0).
- IRQ ==> PTC4.
- VEN ==> PTC3.
- GND ==> GND.
Note that I’m using an RevA/Sch-RevC FRDM-K64F board. Newer boards have SDA/SCL swapped!
Software
I have downloaded a ZIP file from http://cache.nxp.com/documents/software/SW3735.zip.
It has the NFC Library with two Kinetis SDK projects. But the projects do not compile because they are not standalone and have incorrect settings. I gave up trying to fix the issues. Instead, I created a new project from scratch.
Creating a New Project
In Kinetis Design Studio V3.2.0, create a new project with File > New > Kinetis SDK V2.0 Project:
Create a project with all the drivers and with FreeRTOS enabled:
NFC Library
From the SW3735 ZIP file KDSK2.0 project, copy the following folders into the project:
- NfcLibrary: The stack communicating with the PN7120.
- TML: The Transport Mapping Layer, it implements the low-level interface, e.g. interrupt handling and I²C communication.
- Tool: has a delay() function.
- Main.c: This file runs the demo application.
I’m placing them into the project so it builds the same directory structure. The file main.c gets replaced with the version from the zip file:
Include Paths
Because I have added folders with header files, I need to tell the compiler where to find them. I add the following paths to the compiler include search paths of the project properties:
../source/TML
../source/tool
../NfcLibrary/inc
../NfcLibrary/NdefLibrary/inc
../NfcLibrary/NxpNci/inc
Preprocessor Defines
The source files are using several #defines, so I add the following to the project settings:
RW_SUPPORT
P2P_SUPPORT
CARDEMU_SUPPORT
The following defines are supported (see https://community.nxp.com/docs/DOC-331907):
- RW_SUPPORT: With this mode, the host can access a remote contactless tag/card via the NFC Controller.
- P2P_SUPPORT: The host MCU can establish two-way communication accessing to or sending data to an external Reader/Writer.
- CARDEMU_SUPPORT: The NFC controller host (MCU) can emulate a contactless card, which can be accessed by an external Reader/Writer.
- NCI-DEBUG: If defined, all information transferred between the host MCU and the NFC Controller Interface (commands, responses, notifications, data) is echoed to console for debug purposes.
Disabling Interrupts
We are going to use FreeRTOS, and when we start the scheduler, interrupts get enabled. We have to make sure that interrupts are disabled until that point. The default startup code, however, enables interrupts right before jumping to main(). To keep the interrupts disabled, I edit a line in startup/startup_MK64F12.S and comment that line with “cpsie i”:
Pins
In board\board.h, I added the following defines for the pins used:
/* NXPNCI NFC related declaration */
#define NXPNCI_I2C_INSTANCE I2C0
#define NXPNCI_I2C_BAUDRATE (100000)
#define NXPNCI_I2C_ADDR_7BIT (0x28)
#if 1 /* use PTC4 instead of PTC12, because PTC12 might be on different pins depending on the FRDM-K64F board revision */
#define NXPNCI_IRQ_PORTIRQn PORTC_IRQn
#define NXPNCI_IRQ_GPIO (GPIOC)
#define NXPNCI_IRQ_PORT (PORTC)
#define NXPNCI_IRQ_PIN (4U)
#else /* original example uses PTC12 */
#define NXPNCI_IRQ_PORTIRQn PORTC_IRQn
#define NXPNCI_IRQ_GPIO (GPIOC)
#define NXPNCI_IRQ_PORT (PORTC)
#define NXPNCI_IRQ_PIN (12U)
#endif
#define NXPNCI_VEN_GPIO (GPIOC)
#define NXPNCI_VEN_PORT (PORTC)
#define NXPNCI_VEN_PIN (3U)
The original demo was using PTC12, which is mapped to different pins on the FRDM-K64F board, depending on the board revision. That’s why I’m using PTC4 instead.
In board\pinmux.c, add the following to the includes:
#include "board.h"
Inside BOARD_InitPins(), add:
/* Declare and initialize for pull up configuration */
port_pin_config_t pinConfig = {0};
#if 0 /* internal pull-up on I2C as workaround: do *not* use this for real! */
pinConfig.pullSelect = kPORT_PullUp;
#if defined(FSL_FEATURE_PORT_HAS_OPEN_DRAIN) && FSL_FEATURE_PORT_HAS_OPEN_DRAIN
pinConfig.openDrainEnable = kPORT_OpenDrainEnable;
#endif /* FSL_FEATURE_PORT_HAS_OPEN_DRAIN */
#endif
The original demo code had enabled the internal pull-up resistors for the I²C lines as a workaround — if the bus does not have pull-ups installed. The internal pull-ups are usually weak ones, so that doesn't work in most cases anyway, so be careful when relying on this. As the FRDM-K64F has pull-ups on the board for the I2C0 lines, I don’t need that workaround.
To configure the pins, I have added the following code inside BOARD_InitPins():
/* Initialize I2C0 pins below */
/* Ungate the port clock */
CLOCK_EnableClock(kCLOCK_PortE);
/* I2C0 pull up resistor setting */
PORT_SetPinConfig(PORTE, 24U, &pinConfig);
PORT_SetPinConfig(PORTE, 25U, &pinConfig);
/* I2C0 PIN_MUX Configuration */
PORT_SetPinMux(PORTE, 24U, kPORT_MuxAlt5);
PORT_SetPinMux(PORTE, 25U, kPORT_MuxAlt5);
/* Initialize NXPNCI GPIO pins below */
/* Ungate the port clock */
CLOCK_EnableClock(kCLOCK_PortC);
/* IRQ and VEN PIN_MUX Configuration */
PORT_SetPinMux(NXPNCI_IRQ_PORT, NXPNCI_IRQ_PIN, kPORT_MuxAsGpio);
PORT_SetPinMux(NXPNCI_VEN_PORT, NXPNCI_VEN_PIN, kPORT_MuxAsGpio);
/* IRQ interrupt Configuration */
NVIC_SetPriority(NXPNCI_IRQ_PORTIRQn, 5);
EnableIRQ(NXPNCI_IRQ_PORTIRQn);
PORT_SetPinInterruptConfig(NXPNCI_IRQ_PORT, NXPNCI_IRQ_PIN, kPORT_InterruptRisingEdge);
The above enables the clock gates, muxes the pins for I²C, IRQ and GPIO, and configures the interrupt priority.
Have a read of a special series starting with “ARM Cortex-M, Interrupts and FreeRTOS: Part 1” about interrupt priorities and FreeRTOS on ARM Cortex-M.
Running It…
With this, save all your files, build it, and debug it.
Open a terminal/console with 115200 baud to the OpenSDA UART port of the board, and you should see something like this:
Using the sticker tag, which was on the PN7120 box:
It gives this output:
I also have a few 1K Mifare cards from Adafruit, and here, I can read and write the cards:
I’m able to read and write RFID cards now.
The ability of reading/writing RFID cards depends on the security protocol. Mifare cards use a protocol that has been compromised, see https://en.wikipedia.org/wiki/MIFARE.
Processor Expert
Using the Kinetis SDK with the PN7120 is possible, but a lot of rather complicated steps are necessary. Using Processor Expert not only makes it easier and simpler, it is portable that way, too. Plus ,not all Kinetis devices are supported by the Kinetis SDK.
I have published the Processor Expert project on GitHub here.
The steps are very similar to the SDK project:
- Create a project for the board with Processor Expert enabled.
- Add the NfcLibrary, TML, and tool folders to the project.
- Add the same preprocessor defines as for the SDK project.
- Add the same include paths to the compiler settings as for the SDK project.
- Add FreeRTOS.
- Add the Shell component with a communication device (e.g. serial or USB).
- Add BitIO (output pin) for VEN.
- Add ExtInt for the interrupt line from the module, triggering on a raising edge.
- Add the GenericI2C component for the I²C communication.
With this, I have the basic project (I have added there extra components for the board RGB LED):
Because the original demo is using printf() (ouch, see “Why I don’t like printf()“), in order to keep the original demo code, I have added a wrapper to printf() (see my code on GitHub).
In the TML code, I have replaced the interrupt handler and the I²C read/write routines with the Processor Expert driver calls:
void PORTC_IRQHandler(void)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(IrqSem, &xHigherPriorityTaskWoken);
}
static Status I2C_WRITE(uint8_t *pBuff, uint16_t buffLen)
{
uint8_t res;
GI2C1_SelectSlave(NXPNCI_I2C_ADDR_7BIT);
res = GI2C1_WriteBlock(pBuff, buffLen, GI2C1_SEND_STOP);
GI2C1_UnselectSlave();
if (res==ERR_OK) {
return SUCCESS;
}
return ERROR;
}
static Status I2C_READ(uint8_t *pBuff, uint16_t buffLen)
{
uint8_t res;
GI2C1_SelectSlave(NXPNCI_I2C_ADDR_7BIT);
res = GI2C1_ReadBlock(pBuff, buffLen, GI2C1_SEND_STOP);
GI2C1_UnselectSlave();
if (res==ERR_OK) {
return SUCCESS;
}
return ERROR;
}
That’s it! Much easier and simpler, in my view. I was able to strip out all the code for muxing and pin configuration because Processor Expert takes care of it.
Comparison
Using Processor Expert was not only much faster and simpler. The code and data comparison of the two projects doing the same is interesting (no compiler optimizations turned on).
Kinetis SDK V2.0 version:
arm-none-eabi-size --format=berkeley "FRDM-K64F_PN7120_SDK_v2.0.elf"
text data bss dec hex filename
65012 340 15652 81004 13c6c FRDM-K64F_PN7120_SDK_v2.0.elf
Processor Expert version:
arm-none-eabi-size --format=berkeley "FRDM-K64F_PN7120_PEx.elf"
text data bss dec hex filename
36152 364 9108 45624 b238 FRDM-K64F_PN7120_PEx.elf
The RAM (bss) size needed by the Processor Expert version is much smaller, mostly because the SDK version is using an excessive amount of heap and task stack. But the code (text) for the of the SDK drivers is nearly twice as much as needed by the Processor Expert version!
Summary
I have now the ability to read and write (! ) different RFID cards and tags. For example, I can read my SwissPass card or my university badge with this project:
While the SDK V2.0 version works fine, it needs nearly double the amount of FLASH and RAM compared using Processor Expert, mostly because the SDK driver code seems to use a lot of layers, and the example uses an excessive amount of RAM (I think mostly because of printf()?).
The Kinetis SDK project is available on GitHub.
The Processor Expert project is on GitHub here.
Happy RFIDing!
Published at DZone with permission of Erich Styger, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments