This post provides guidance on how to port the example projects from the NXP NCI 2.0 NFC library to the MCXW71 Wireless MCU using SPI interface to communicate with PN7160 SPI EVK.
To follow this guide, please use the following environment:
MCUXpresso IDE 25.06.136.
FRDM-MCXW71 SDK v25.12.00 (last available for MCUXpresso IDE).
FRDM-MCXW71.
OM27160B1 (PN7160 SPI EVK).
Hardware Setup.
The MCXW71 complies with the Arduino header standard as the OM27160B1 board, therefore you can connect directly the shield over the FRDM-MCXW71 as the pin connection match among both, allowing an easy connection between the devices. Take into consideration that this setup forces us to use the LPSPI1 instance of the MCXW71, it is possible to use another instance, but we would not be able to connect the boards directly, rather we would need to do the connections with jumpers.
Downloading base projects and adapting for MCXW71 port.
To start with the porting work, download the NXP-NCI example project from this page. This compressed file contains the base project for different boards that will allow us to do the required modifications to add support for the MCXW71. Once downloaded, extract the SW6705 file into a known path (e.g. the Downloads folder) to later import the project to the IDE. The extracted folder should contain a .zip file with the examples that we will later import to the IDE.
Download the FRDM-MCXW71's SDK from the SDK Builder page, make sure to select the 25.12.00 version, as it is the latest SDK available to use along the MCUXpresso IDE.
Now we have to import the NXP-NCI2.0_MCUXpresso_examples.zip file we previously extracted to the IDE's workspace, to do so, click on the Import project(s) from file system and in the Project archive (zip) tab, browse for the extracted file of step 1 (NXP-NCI2.0_MCUXpresso_examples.zip) and click on Next >. NOTE: Don’t worry if the IDE shows an error message for not having the SDKs of the default boards (iMXRT1170, LPC55S6x, LPC82x) or having a different version, close the warning message, we only need these examples to copy the NCI library and example files. Your workspace should now look like the following image:
Import the hello_world example from the FRDM-MCXW71 SDK:
Add the SPI drivers to the imported project by right clicking over the project, hover the cursor over the SDK Management option and select the option Manage SDK Components: Select the driver, click Ok and if you are asked to refresh files accept it, after this you should be able to see the driver in the "drivers" folder of the project.
Copy the contents of the source folder of the iMXRT1170 project, as well as the NfcLibrary folder and paste them into the imported hello_world project: Make sure to delete the hello_world.c and hello_word.mex files as we won't need it again. After this process your project should look like the following image:
To avoid compiling issues, exclude from the build the files nfc_example_P2P.c and nfc_example_RW.c, to do so right click on the file, go to Resource Configurations and select Exclude from Build… and select for all configurations. Do this for each file.
Add the preprocessor macro: BOARD_NXPNCI_INTERFACE_SPI, as this is used by the example to select the interface with the board. To do this, right-click on the project and select Properties, then drop-down the C/C++ Build option and go to the Settings tab. Here, add the macro in the Preprocessor option, click on Apply and accept the index rebuild.
Add the root folder in C/C++ General > Paths and Symbols > Source Location tab, click on Ok and then Apply:
Still in Paths and Symbols, go to the Includes tab and add the source, TML and tool folders from workspace, click Ok and Apply. Make sure to add them one by one.
Now, go to C/C++ Build > Settings > Includes and add the following folders from Workspace. You can select all of them and add them at the same time or also do it one by one. Once done, click on Apply and Apply and Close. If you are asked to rebuild the index, do it. "${workspace_loc:/${ProjName}/source/TML}"
"${workspace_loc:/${ProjName}/source/tool}"
"${workspace_loc:/${ProjName}/NfcLibrary}"
"${workspace_loc:/${ProjName}/NfcLibrary/inc}"
"${workspace_loc:/${ProjName}/NfcLibrary/NdefLibrary}"
"${workspace_loc:/${ProjName}/NfcLibrary/NdefLibrary/inc}"
"${workspace_loc:/${ProjName}/NfcLibrary/NdefLibrary/src}"
"${workspace_loc:/${ProjName}/NfcLibrary/NxpNci20}"
"${workspace_loc:/${ProjName}/NfcLibrary/NxpNci20/inc}"
"${workspace_loc:/${ProjName}/NfcLibrary/NxpNci20/src}"
Source Code Changes.
In the board folder, open the board.h file and add the following definitions to refer to the peripherals and clocks to be used. Please notice that you may change the LPSPI instance, however you would need to connect jumpers instead of connecting directly the shield over the FRDM. #ifdef BOARD_NXPNCI_INTERFACE_SPI
#define BOARD_NXPNCI_SPI_CLOCK (CLOCK_GetIpFreq(kCLOCK_Lpspi1))
#define BOARD_NXPNCI_SPI_INSTANCE (LPSPI1)
#define BOARD_NXPNCI_SPI_BAUDRATE (400000)
#endif
#define BOARD_NXPNCI_IRQ_PORT (GPIOC) // J2.10 - GPIO0 [PN7160] - IRQ -> J2.10 GPIOC0 [MCXW71]
#define BOARD_NXPNCI_VEN_PORT (GPIOA) // J4.1 - GPIO1 [PN7160] - VEN -> J1.8 GPIOA21 [MCXW71]
#define BOARD_NXPNCI_DWL_PORT (GPIOA) // J4.2 - GPIO2 [PN7160] - REQ -> J1.7 GPIOA20 [MCXW71]
#define BOARD_NXPNCI_IRQ_PIN (0U)
#define BOARD_NXPNCI_VEN_PIN (21U)
#define BOARD_NXPNCI_DWL_PIN (20U)
Now we need to add the required clock, peripheral and pin initialization for our board. To do this, go to the hardware_init.c file inside the board folder, and overwrite the BOARD_InitHardware function with the following: void BOARD_InitHardware(void)
{
BOARD_InitPins();
BOARD_BootClockRUN();
BOARD_InitDebugConsole();
CLOCK_SetIpSrc(kCLOCK_Lpspi1, kCLOCK_IpSrcFro192M);
CLOCK_SetIpSrcDiv(kCLOCK_Lpspi1, kSCG_SysClkDivBy16);
}
To add the correct pin multiplexing and configuration for our SPI and GPIO pins, go to the pin_mux.c file (also in the board folder) and overwrite the BOARD_InitPins function with the following: void BOARD_InitPins(void)
{
/* Clock Configuration: Peripheral clocks are enabled; module does not stall low power mode entry */
CLOCK_EnableClock(kCLOCK_GpioA);
CLOCK_EnableClock(kCLOCK_GpioC);
CLOCK_EnableClock(kCLOCK_PortA);
CLOCK_EnableClock(kCLOCK_PortB);
CLOCK_EnableClock(kCLOCK_PortC);
/*IF SHORTING SH11, SH12, SH13, SH14 needed for LPSPI1*/
const port_pin_config_t portb0_pin46_config = {/* Internal pull-up resistor is enabled */
(uint16_t)kPORT_PullUp,
/* Low internal pull resistor value is selected. */
(uint16_t)kPORT_LowPullResistor,
/* Fast slew rate is configured */
(uint16_t)kPORT_FastSlewRate,
/* Passive input filter is disabled */
(uint16_t)kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
(uint16_t)kPORT_OpenDrainDisable,
/* Low drive strength is configured */
(uint16_t)kPORT_LowDriveStrength,
/* Normal drive strength is configured */
(uint16_t)kPORT_NormalDriveStrength,
/* Pin is configured as LPSPI0_PCS0 */
(uint16_t)kPORT_MuxAlt2,
/* Pin Control Register fields [15:0] are not locked */
(uint16_t)kPORT_UnlockRegister};
/* PORTB0 (pin 46) is configured as LPSPI1_PCS0 */
PORT_SetPinConfig(PORTB, 0U, &portb0_pin46_config);
const port_pin_config_t portb1_pin47_config = {/* Internal pull-up resistor is enabled */
(uint16_t)kPORT_PullUp,
/* Low internal pull resistor value is selected. */
(uint16_t)kPORT_LowPullResistor,
/* Fast slew rate is configured */
(uint16_t)kPORT_FastSlewRate,
/* Passive input filter is disabled */
(uint16_t)kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
(uint16_t)kPORT_OpenDrainDisable,
/* Low drive strength is configured */
(uint16_t)kPORT_LowDriveStrength,
/* Normal drive strength is configured */
(uint16_t)kPORT_NormalDriveStrength,
/* Pin is configured as LPSPI0_SIN */
(uint16_t)kPORT_MuxAlt2,
/* Pin Control Register fields [15:0] are not locked */
(uint16_t)kPORT_UnlockRegister};
/* PORTB1 (pin 47) is configured as LPSPI1_SIN */
PORT_SetPinConfig(PORTB, 1U, &portb1_pin47_config);
const port_pin_config_t portb3_pin1_config = {/* Internal pull-up resistor is enabled */
(uint16_t)kPORT_PullUp,
/* Low internal pull resistor value is selected. */
(uint16_t)kPORT_LowPullResistor,
/* Fast slew rate is configured */
(uint16_t)kPORT_FastSlewRate,
/* Passive input filter is disabled */
(uint16_t)kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
(uint16_t)kPORT_OpenDrainDisable,
/* Low drive strength is configured */
(uint16_t)kPORT_LowDriveStrength,
/* Normal drive strength is configured */
(uint16_t)kPORT_NormalDriveStrength,
/* Pin is configured as LPSPI0_SOUT */
(uint16_t)kPORT_MuxAlt2,
/* Pin Control Register fields [15:0] are not locked */
(uint16_t)kPORT_UnlockRegister};
/* PORTB3 (pin 1) is configured as LPSPI1_SOUT */
PORT_SetPinConfig(PORTB, 3U, &portb3_pin1_config);
const port_pin_config_t portb2_pin48_config = {/* Internal pull-up resistor is enabled */
(uint16_t)kPORT_PullUp,
/* Low internal pull resistor value is selected. */
(uint16_t)kPORT_LowPullResistor,
/* Fast slew rate is configured */
(uint16_t)kPORT_FastSlewRate,
/* Passive input filter is disabled */
(uint16_t)kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
(uint16_t)kPORT_OpenDrainDisable,
/* Low drive strength is configured */
(uint16_t)kPORT_LowDriveStrength,
/* Normal drive strength is configured */
(uint16_t)kPORT_NormalDriveStrength,
/* Pin is configured as LPSPI0_SCK */
(uint16_t)kPORT_MuxAlt2,
/* Pin Control Register fields [15:0] are not locked */
(uint16_t)kPORT_UnlockRegister};
/* PORTA19 (pin 14) is configured as LPSPI1_SCK */
PORT_SetPinConfig(PORTB, 2U, &portb2_pin48_config);
/*IF SHORTING SH11, SH12, SH13, SH14 needed for LPSPI1*/
const port_pin_config_t irq_pin = {/* Internal pull-up/down resistor is disabled */
(uint16_t)kPORT_PullUp,
/* Low internal pull resistor value is selected. */
(uint16_t)kPORT_LowPullResistor,
/* Fast slew rate is configured */
(uint16_t)kPORT_FastSlewRate,
/* Passive input filter is disabled */
(uint16_t)kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
(uint16_t)kPORT_OpenDrainDisable,
/* Low drive strength is configured */
(uint16_t)kPORT_LowDriveStrength,
/* Normal drive strength is configured */
(uint16_t)kPORT_NormalDriveStrength,
/* Pin is configured as PTC0 */
(uint16_t)kPORT_MuxAsGpio,
/* Pin Control Register fields [15:0] are not locked */
(uint16_t)kPORT_UnlockRegister};
/* PORTC0 (pin 37) is configured as PTC0 */
PORT_SetPinConfig(PORTC, 0U, &irq_pin);
const port_pin_config_t ven_pin = {/* Internal pull-up/down resistor is disabled */
(uint16_t)kPORT_PullDisable,
/* Low internal pull resistor value is selected. */
(uint16_t)kPORT_LowPullResistor,
/* Fast slew rate is configured */
(uint16_t)kPORT_FastSlewRate,
/* Passive input filter is disabled */
(uint16_t)kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
(uint16_t)kPORT_OpenDrainDisable,
/* Low drive strength is configured */
(uint16_t)kPORT_LowDriveStrength,
/* Normal drive strength is configured */
(uint16_t)kPORT_NormalDriveStrength,
/* Pin is configured as PTA20 */
(uint16_t)kPORT_MuxAsGpio,
/* Pin Control Register fields [15:0] are not locked */
(uint16_t)kPORT_UnlockRegister};
/* PORTA20 (pin 17) is configured as PTA20 */
PORT_SetPinConfig(PORTA, 20U, &ven_pin);
const port_pin_config_t req_pin = {/* Internal pull-up/down resistor is disabled */
(uint16_t)kPORT_PullDisable,
/* Low internal pull resistor value is selected. */
(uint16_t)kPORT_LowPullResistor,
/* Fast slew rate is configured */
(uint16_t)kPORT_FastSlewRate,
/* Passive input filter is disabled */
(uint16_t)kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
(uint16_t)kPORT_OpenDrainDisable,
/* Low drive strength is configured */
(uint16_t)kPORT_LowDriveStrength,
/* Normal drive strength is configured */
(uint16_t)kPORT_NormalDriveStrength,
/* Pin is configured as PTA21 */
(uint16_t)kPORT_MuxAsGpio,
/* Pin Control Register fields [15:0] are not locked */
(uint16_t)kPORT_UnlockRegister};
/* PORTA21 (pin 18) is configured as PTA21 */
PORT_SetPinConfig(PORTA, 21U, &req_pin);
const port_pin_config_t portc2_pin39_config = {/* Internal pull-up/down resistor is disabled */
(uint16_t)kPORT_PullDisable,
/* Low internal pull resistor value is selected. */
(uint16_t)kPORT_LowPullResistor,
/* Fast slew rate is configured */
(uint16_t)kPORT_FastSlewRate,
/* Passive input filter is disabled */
(uint16_t)kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
(uint16_t)kPORT_OpenDrainDisable,
/* Low drive strength is configured */
(uint16_t)kPORT_LowDriveStrength,
/* Normal drive strength is configured */
(uint16_t)kPORT_NormalDriveStrength,
/* Pin is configured as LPUART1_RX */
(uint16_t)kPORT_MuxAlt3,
/* Pin Control Register fields [15:0] are not locked */
(uint16_t)kPORT_UnlockRegister};
/* PORTC2 (pin 39) is configured as LPUART1_RX */
PORT_SetPinConfig(PORTC, 2U, &portc2_pin39_config);
const port_pin_config_t portc3_pin40_config = {/* Internal pull-up/down resistor is disabled */
(uint16_t)kPORT_PullDisable,
/* Low internal pull resistor value is selected. */
(uint16_t)kPORT_LowPullResistor,
/* Fast slew rate is configured */
(uint16_t)kPORT_FastSlewRate,
/* Passive input filter is disabled */
(uint16_t)kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
(uint16_t)kPORT_OpenDrainDisable,
/* Low drive strength is configured */
(uint16_t)kPORT_LowDriveStrength,
/* Normal drive strength is configured */
(uint16_t)kPORT_NormalDriveStrength,
/* Pin is configured as LPUART1_TX */
(uint16_t)kPORT_MuxAlt3,
/* Pin Control Register fields [15:0] are not locked */
(uint16_t)kPORT_UnlockRegister};
/* PORTC3 (pin 40) is configured as LPUART1_TX */
PORT_SetPinConfig(PORTC, 3U, &portc3_pin40_config);
}
To add the required interfacing APIs specific of our chip, we need to modify the tml.c file from the TML folder, in this file overwrite the functions: INTF_INIT, INTF_WRITE and INTF_READ with the following: static void INTF_INIT(void)
{
lpspi_master_config_t userConfig;
uint32_t srcFreq = 0;
/*SPI configuration*/
LPSPI_MasterGetDefaultConfig(&userConfig);
userConfig.baudRate = BOARD_NXPNCI_SPI_BAUDRATE;
srcFreq = BOARD_NXPNCI_SPI_CLOCK;
userConfig.whichPcs = (lpspi_which_pcs_t)kLPSPI_Pcs0;
userConfig.pcsActiveHighOrLow = (lpspi_pcs_polarity_config_t)kLPSPI_PcsActiveLow;
/*Initialize SPI*/
LPSPI_MasterInit(BOARD_NXPNCI_SPI_INSTANCE, &userConfig, srcFreq);
}
static status_t INTF_WRITE(uint8_t *pBuff, uint16_t buffLen)
{
uint8_t temp[1000];
temp[0] = 0x7F;
memcpy(temp+1, pBuff, buffLen);
masterXfer.txData = temp;
masterXfer.rxData = NULL;
masterXfer.dataSize = buffLen+1;
masterXfer.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap;;
return LPSPI_MasterTransferBlocking(BOARD_NXPNCI_SPI_INSTANCE, &masterXfer);
}
static status_t INTF_READ(uint8_t *pBuff, uint16_t buffLen)
{
status_t status;
uint8_t temp[257];
temp[0] = 0xFF;
masterXfer.txData = temp;
masterXfer.rxData = temp;
masterXfer.dataSize = buffLen+1;
masterXfer.configFlags = kLPSPI_MasterPcs0 | kLPSPI_MasterPcsContinuous | kLPSPI_MasterByteSwap;;
status = LPSPI_MasterTransferBlocking(BOARD_NXPNCI_SPI_INSTANCE, &masterXfer);
if(status == kStatus_Success) memcpy(pBuff, temp+1, buffLen);
SDK_DelayAtLeastUs(10, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
return status;
}
#endif
We also need to overwrite the functions: tml_Init, tml_DeInit and tml_Reset to adapt them to use the specific APIs for the GPIOs of our board. static Status tml_Init(void) {
gpio_pin_config_t in_config = {kGPIO_DigitalInput, 0};
gpio_pin_config_t out_config = {kGPIO_DigitalOutput, 0};
GPIO_PinInit(BOARD_NXPNCI_IRQ_PORT, BOARD_NXPNCI_IRQ_PIN, &in_config);
GPIO_PinInit(BOARD_NXPNCI_VEN_PORT, BOARD_NXPNCI_VEN_PIN, &out_config);
GPIO_PinInit(BOARD_NXPNCI_DWL_PORT, BOARD_NXPNCI_DWL_PIN, &out_config);
INTF_INIT();
return SUCCESS;
}
static Status tml_DeInit(void) {
GPIO_PortClear(BOARD_NXPNCI_VEN_PORT, 1U << BOARD_NXPNCI_VEN_PIN);
return SUCCESS;
}
static Status tml_Reset(void) {
/* Set DWL_REQ low for NCI protocol */
GPIO_PortClear(BOARD_NXPNCI_DWL_PORT, 1U << BOARD_NXPNCI_DWL_PIN);
GPIO_PortClear(BOARD_NXPNCI_VEN_PORT, 1U << BOARD_NXPNCI_VEN_PIN);
Sleep(10);
GPIO_PortSet(BOARD_NXPNCI_VEN_PORT, 1U << BOARD_NXPNCI_VEN_PIN);
Sleep(10);
return SUCCESS;
}
Finally, modify the main file so it uses the APIs to initialize our board clocks and pins: #include <stdio.h>
#include <string.h>
#include "app.h"
#include "board.h"
#include "pin_mux.h"
#include "fsl_debug_console.h"
extern void nfc_example (void);
int main(void)
{
BOARD_InitHardware();
#ifdef BOARD_NXPNCI_INTERFACE_I2C
PRINTF("\nRunning the NXP-NCI2.0 example (I2C interface)\n");
#else
PRINTF("\nRunning the NXP-NCI2.0 example (SPI interface)\n");
#endif
nfc_example();
}
Testing the example.
At this point we have everything set to build and flash our example with SPI interface, you may proceed to build and debug/flash the example by pressing the blue beetle button:
Once the example is flashed, open a serial terminal such as Teraterm with the following settings:
Baudrate: 115200.
Data: 8 bits.
Parity: None.
Stop bits: 1 bit.
No flow control.
While running, the example should output the following logs to the terminal:
When a tag is placed near the antenna, the example should print the tag information in the terminal as shown:
View full article