Skip to main content
Graduate
September 19, 2025
Solved

RFAL ST25R - library integration (I2C)

  • September 19, 2025
  • 3 replies
  • 1350 views

 

Hello everyone!
This is my first time trying to use the ST25R chip. After struggling with the datasheet, I was referred to the RFAL library and the UM2890 manual.
However, I couldn't find clear step-by-step instructions.

:backhand_index_pointing_right: Therefore, I'd like to ask questions about the integration process in this thread.
Perhaps this will also be useful to someone else in the future.

Now I have issues with steps 4 and 5. Maybe someone could give me some advice on this?

My setup:

  • MCU: STM32C031C6 (NUCLEO-C031C6).

  • NFC: ST25R3916B on a custom PCB via I2C.
    Notes:
    VDD_RF and VDD_DR connected together. 100nF to GND.
    VDD_AM has 2.2uF to GND.
    VDD_A and VDD_D have their own 10nF to GND.
    AAT_A and AAT_B are not connected.
    EXT_LM is not connected.
    TAD1, TAD2 are not connected.
    I2C_EN to VDD_D.

  • Antenna: single-ended, onboard, connected directly to RF0 and RF1.

 

Step 1 – Connections

UM2890 - 4.1:
Pin definitions
• ST25R_SS_PIN / ST25R_SS_PORT - Not used because I have I2C.
• ST25R_INT_PIN / ST25R_INT_PORT - Yes
• ST25R_RESET_PIN / ST25R_RESET_PORT - Not used
• PLATFORM_LED_RX_PIN / PLATFORM_LED_RX_PORT - Yes
• PLATFORM_LED_FIELD_PIN / PLATFORM_LED_FIELD_PORT - Yes

I started with the following wiring:

 

ST25R3916B <--> STM32C031C6
--------------------------------
 SDA -> PA10
 SCL -> PA9
 IRQ -> PB0 (ST25R_IRQ) (as GPIO_EXTI0)

STM32C031C6:
PC13 -> B1
PA5 -> LED_RX_Pin
PA6 -> LED_FIELD_Pin
USART2 - for log

 

Step 2 – Project creation

I started with STM32CubeIDE:

  • File → New → STM32 Project

  • Selected my NUCLEO-C031C6 board

  • Created a new project:

vlad_04_5-1758196639246.png


MCU configuration:

  • Connectivity → I²C → I²C mode with standard parameters:

    vlad_04_2-1758194223075.png
  • GPIO settings for I²C: Enabled Pull-up resistors

    vlad_04_3-1758194284945.png

     

    vlad_04_0-1758271087190.png
     

     

Step 3 – RFAL library import

I downloaded the STSW-ST25RFAL002 package.

Then I:

  • Created a new subfolder in my project: Rfal_library

  • Copied the include and source directories into it

  • Updated the project settings:
    Properties → C/C++ General → Paths and Symbols → Includes
    Rfal_library/source
    Rfal_library/source/st25r3916
    Rfal_library/include

  • According to UM2890, I created a new file: Rfal_library/include/rfal_platform.h
    Into this file, I copied the content from the example:

    ST25NFC_Embedded_Lib_ST25R3916(B)_1.8.0\Projects\STM32L476RG-Nucleo\Applications\X-NUCLEO-NFC08A1\polling\Inc
/**
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2018-2025 STMicroelectronics.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
/*! \file
 *
 * \author
 *
 * \brief Platform header file. Defining platform independent functionality.
 *
 */

#ifndef RFAL_PLATFORM_H
#define RFAL_PLATFORM_H

#ifdef __cplusplus
extern "C" {
#endif

/*
******************************************************************************
* INCLUDES
******************************************************************************
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <limits.h>
#include <math.h>

#include "main.h"
#ifdef RFAL_USE_I2C
#include "i2c.h"
#else
#include "spi.h"
#endif
#include "timer.h"
#include "logger.h"


/*
******************************************************************************
* GLOBAL DEFINES
******************************************************************************
*/
#define ST25R_SS_PIN SPI1_CS_Pin /*!< GPIO pin used for ST25R SPI SS */
#define ST25R_SS_PORT SPI1_CS_GPIO_Port /*!< GPIO port used for ST25R SPI SS port */

#define ST25R_INT_PIN ST25R_IRQ_Pin /*!< GPIO pin used for ST25R External Interrupt */
#define ST25R_INT_PORT ST25R_IRQ_GPIO_Port /*!< GPIO port used for ST25R External Interrupt */

#ifdef LED_FIELD_Pin
#define PLATFORM_LED_FIELD_PIN LED_FIELD_Pin /*!< GPIO pin used as field LED */
#endif

#ifdef LED_FIELD_GPIO_Port
#define PLATFORM_LED_FIELD_PORT LED_FIELD_GPIO_Port /*!< GPIO port used as field LED */
#endif


#define PLATFORM_LED_A_PIN LED_A_Pin /*!< GPIO pin used for LED A */
#define PLATFORM_LED_A_PORT LED_A_GPIO_Port /*!< GPIO port used for LED A */
#define PLATFORM_LED_B_PIN LED_B_Pin /*!< GPIO pin used for LED B */
#define PLATFORM_LED_B_PORT LED_B_GPIO_Port /*!< GPIO port used for LED B */
#define PLATFORM_LED_F_PIN LED_F_Pin /*!< GPIO pin used for LED F */
#define PLATFORM_LED_F_PORT LED_F_GPIO_Port /*!< GPIO port used for LED F */
#define PLATFORM_LED_V_PIN LED_V_Pin /*!< GPIO pin used for LED V */
#define PLATFORM_LED_V_PORT LED_V_GPIO_Port /*!< GPIO port used for LED V */
#define PLATFORM_LED_AP2P_PIN LED_AP2P_Pin /*!< GPIO pin used for LED AP2P */
#define PLATFORM_LED_AP2P_PORT LED_AP2P_GPIO_Port /*!< GPIO port used for LED AP2P*/

#define PLATFORM_USER_BUTTON_PIN B1_Pin /*!< GPIO pin user button */
#define PLATFORM_USER_BUTTON_PORT B1_GPIO_Port /*!< GPIO port user button */


/*
******************************************************************************
* GLOBAL MACROS
******************************************************************************
*/
#define platformProtectST25RComm() do{ globalCommProtectCnt++; __DSB();NVIC_DisableIRQ(ST25R_IRQ_EXTI_IRQn);__DSB();__ISB();}while(0) /*!< Protect unique access to ST25R communication channel - IRQ disable on single thread environment (MCU) ; Mutex lock on a multi thread environment */
#define platformUnprotectST25RComm() do{ if (--globalCommProtectCnt==0) {NVIC_EnableIRQ(ST25R_IRQ_EXTI_IRQn);} }while(0) /*!< Unprotect unique access to ST25R communication channel - IRQ enable on a single thread environment (MCU) ; Mutex unlock on a multi thread environment */

#define platformProtectST25RIrqStatus() platformProtectST25RComm() /*!< Protect unique access to IRQ status var - IRQ disable on single thread environment (MCU) ; Mutex lock on a multi thread environment */
#define platformUnprotectST25RIrqStatus() platformUnprotectST25RComm() /*!< Unprotect the IRQ status var - IRQ enable on a single thread environment (MCU) ; Mutex unlock on a multi thread environment */

#define platformLedOff( port, pin ) platformGpioClear(port, pin) /*!< Turns the given LED Off */
#define platformLedOn( port, pin ) platformGpioSet(port, pin) /*!< Turns the given LED On */
#define platformLedToggle( port, pin ) platformGpioToggle(port, pin) /*!< Toggles the given LED */

#define platformGpioSet( port, pin ) HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET) /*!< Turns the given GPIO High */
#define platformGpioClear( port, pin ) HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET) /*!< Turns the given GPIO Low */
#define platformGpioToggle( port, pin ) HAL_GPIO_TogglePin(port, pin) /*!< Toggles the given GPIO */
#define platformGpioIsHigh( port, pin ) (HAL_GPIO_ReadPin(port, pin) == GPIO_PIN_SET) /*!< Checks if the given LED is High */
#define platformGpioIsLow( port, pin ) (!platformGpioIsHigh(port, pin)) /*!< Checks if the given LED is Low */

#define platformTimerCreate( t ) timerCalculateTimer(t) /*!< Create a timer with the given time (ms) */
#define platformTimerIsExpired( timer ) timerIsExpired(timer) /*!< Checks if the given timer is expired */
#define platformTimerGetRemaining( timer ) timerGetRemaining(timer) /*!< Gets the remaining time until expiration */
#define platformDelay( t ) HAL_Delay(t) /*!< Performs a delay for the given time (ms) */

#define platformGetSysTick() HAL_GetTick() /*!< Get System Tick ( 1 tick = 1 ms) */

#define platformAssert( exp ) assert_param( exp ) /*!< Asserts whether the given expression is true*/
#define platformErrorHandle() Error_Handler() /*!< Global error handle\trap */


#ifdef RFAL_USE_I2C

#define platformI2CTx( txBuf, len, last, txOnly ) i2cSequentialTx((uint16_t)0xA0, (uint8_t *)(txBuf), (len), last, txOnly ) /*!< I2C Transmit */
#define platformI2CRx( txBuf, len ) i2cSequentialRx((uint16_t)0xA0, rxBuf, len ) /*!< I2C Receive */
#define platformI2CStart() /*!< I2C Start condition */
#define platformI2CStop() /*!< I2C Stop condition */
#define platformI2CRepeatStart() /*!< I2C Repeat Start */
#define platformI2CSlaveAddrWR(add) /*!< I2C Slave address for Write operation */
#define platformI2CSlaveAddrRD(add) /*!< I2C Slave address for Read operation */

#else /* RFAL_USE_I2C */

#define platformSpiSelect() platformGpioClear(ST25R_SS_PORT, ST25R_SS_PIN)/*!< SPI SS\CS: Chip|Slave Select */
#define platformSpiDeselect() platformGpioSet(ST25R_SS_PORT, ST25R_SS_PIN) /*!< SPI SS\CS: Chip|Slave Deselect */
#define platformSpiTxRx( txBuf, rxBuf, len ) spiTxRx(txBuf, rxBuf, len) /*!< SPI transceive */

#endif /* RFAL_USE_I2C */

#define platformLog(...) logUsart(__VA_ARGS__) /*!< Log method */

/*
******************************************************************************
* GLOBAL VARIABLES
******************************************************************************
*/
extern uint8_t globalCommProtectCnt; /* Global Protection Counter provided per platform - instantiated in main.c */

/*
******************************************************************************
* USER SPECIFIC RFAL CONFIGURATION
******************************************************************************
*/

#define RFAL_FEATURE_LISTEN_MODE true /*!< Enable/Disable RFAL support for Listen Mode */
#define RFAL_FEATURE_WAKEUP_MODE true /*!< Enable/Disable RFAL support for the Wake-Up mode */
#define RFAL_FEATURE_LOWPOWER_MODE false /*!< Enable/Disable RFAL support for the Low Power mode */
#define RFAL_FEATURE_NFCA true /*!< Enable/Disable RFAL support for NFC-A (ISO14443A) */
#define RFAL_FEATURE_NFCB true /*!< Enable/Disable RFAL support for NFC-B (ISO14443B) */
#define RFAL_FEATURE_NFCF true /*!< Enable/Disable RFAL support for NFC-F (FeliCa) */
#define RFAL_FEATURE_NFCV true /*!< Enable/Disable RFAL support for NFC-V (ISO15693) */
#define RFAL_FEATURE_T1T true /*!< Enable/Disable RFAL support for T1T (Topaz) */
#define RFAL_FEATURE_T2T true /*!< Enable/Disable RFAL support for T2T */
#define RFAL_FEATURE_T4T true /*!< Enable/Disable RFAL support for T4T */
#define RFAL_FEATURE_ST25TB true /*!< Enable/Disable RFAL support for ST25TB */
#define RFAL_FEATURE_ST25xV true /*!< Enable/Disable RFAL support for ST25TV/ST25DV */
#define RFAL_FEATURE_DYNAMIC_ANALOG_CONFIG false /*!< Enable/Disable Analog Configs to be dynamically updated (RAM) */
#define RFAL_FEATURE_DPO false /*!< Enable/Disable RFAL Dynamic Power Output support */
#define RFAL_FEATURE_ISO_DEP true /*!< Enable/Disable RFAL support for ISO-DEP (ISO14443-4) */
#define RFAL_FEATURE_ISO_DEP_POLL true /*!< Enable/Disable RFAL support for Poller mode (PCD) ISO-DEP (ISO14443-4) */
#define RFAL_FEATURE_ISO_DEP_LISTEN true /*!< Enable/Disable RFAL support for Listen mode (PICC) ISO-DEP (ISO14443-4) */
#define RFAL_FEATURE_NFC_DEP true /*!< Enable/Disable RFAL support for NFC-DEP (NFCIP1/P2P) */

#define RFAL_FEATURE_ISO_DEP_IBLOCK_MAX_LEN 256U /*!< ISO-DEP I-Block max length. Please use values as defined by rfalIsoDepFSx */
#define RFAL_FEATURE_NFC_DEP_BLOCK_MAX_LEN 254U /*!< NFC-DEP Block/Payload length. Allowed values: 64, 128, 192, 254 */
#define RFAL_FEATURE_NFC_RF_BUF_LEN 258U /*!< RF buffer length used by RFAL NFC layer */

#define RFAL_FEATURE_ISO_DEP_APDU_MAX_LEN 512U /*!< ISO-DEP APDU max length. Please use multiples of I-Block max length */
#define RFAL_FEATURE_NFC_DEP_PDU_MAX_LEN 512U /*!< NFC-DEP PDU max length. */

/*
******************************************************************************
* RFAL CUSTOM SETTINGS
******************************************************************************
 Custom analog configs are used to cope with Automatic Antenna Tuning (AAT)
 that are optimized differently for each board.
*/
#define RFAL_ANALOG_CONFIG_CUSTOM /*!< Use Custom Analog Configs when defined */

/*
******************************************************************************
* DEFAULT RFAL CONFIGURATION
******************************************************************************
*/
#include "rfal_defConfig.h"

#ifdef __cplusplus
}
#endif

#endif /* RFAL_PLATFORM_H */
​
  • The next step was to adapt the rfal_platform.h file according to my hardware connections.
    1. I commented out "spi.h" because I'm using I2C.
#include "main.h"
#ifdef RFAL_USE_I2C
#include "i2c.h"
#else
//#include "spi.h" // <---------here
#endif
#include "timer.h"
#include "logger.h"​
//#define platformSpiSelect() platformGpioClear(ST25R_SS_PORT, ST25R_SS_PIN)/*!< SPI SS\CS: Chip|Slave Select */
//#define platformSpiDeselect() platformGpioSet(ST25R_SS_PORT, ST25R_SS_PIN) /*!< SPI SS\CS: Chip|Slave Deselect */
//#define platformSpiTxRx( txBuf, rxBuf, len ) spiTxRx(txBuf, rxBuf, len) /*!< SPI transceive 


2. Created the missing files .h in the Rfal_library/include, .c in the Rfal_library/source:

// i2c.h
#ifndef I2C_H
#define I2C_H

#include "stm32c0xx_hal.h"

HAL_StatusTypeDef i2cSequentialTx(uint16_t DevAddress, uint8_t *pData, uint16_t Size, bool last, bool txOnly);
HAL_StatusTypeDef i2cSequentialRx(uint16_t DevAddress, uint8_t *pData, uint16_t Size);

#endif // I2C_H
// i2c.c
#include "i2c.h"
extern I2C_HandleTypeDef hi2c1;

HAL_StatusTypeDef i2cSequentialTx(uint16_t DevAddress, uint8_t *pData, uint16_t Size, bool last, bool txOnly) {
 return HAL_I2C_Master_Transmit(&hi2c1, DevAddress, pData, Size, HAL_MAX_DELAY);
}

HAL_StatusTypeDef i2cSequentialRx(uint16_t DevAddress, uint8_t *pData, uint16_t Size) {
 return HAL_I2C_Master_Receive(&hi2c1, DevAddress, pData, Size, HAL_MAX_DELAY);
}
// timer.h
#ifndef TIMER_H
#define TIMER_H

#include <stdint.h>
#include <stdbool.h>

uint32_t timerCalculateTimer(uint32_t time);
bool timerIsExpired(uint32_t timer);
uint32_t timerGetRemaining(uint32_t timer);

#endif // TIMER_H
// timer.c
#include "timer.h"
#include "stm32c0xx_hal.h"

uint32_t timerCalculateTimer(uint32_t time) {
 return HAL_GetTick() + time;
}

bool timerIsExpired(uint32_t timer) {
 return (HAL_GetTick() >= timer);
}

uint32_t timerGetRemaining(uint32_t timer) {
 uint32_t now = HAL_GetTick();
 return (timer > now) ? (timer - now) : 0;
}
// logger.h
#ifndef LOGGER_H
#define LOGGER_H

#include <stdio.h>
#include <stdarg.h>

void logUsart(const char *format, ...);

#endif // LOGGER_H
// logger.c
#include "logger.h"
#include "stm32c0xx_hal.h"
#include "main.h"

extern UART_HandleTypeDef huart1;

void logUsart(const char *format, ...) {
 char buffer[256];
 va_list args;
 va_start(args, format);
 vsnprintf(buffer, sizeof(buffer), format, args);
 va_end(args);
 HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY);
}
  • 3. The next step was to customize the pins to match my:
/*
******************************************************************************
* GLOBAL DEFINES
******************************************************************************
*/
//#define ST25R_SS_PIN SPI1_CS_Pin /*!< GPIO pin used for ST25R SPI SS */
//#define ST25R_SS_PORT SPI1_CS_GPIO_Port /*!< GPIO port used for ST25R SPI SS port */

#define ST25R_INT_PIN ST25R_IRQ_Pin /*!< GPIO pin used for ST25R External Interrupt */
#define ST25R_INT_PORT ST25R_IRQ_GPIO_Port /*!< GPIO port used for ST25R External Interrupt */

#ifdef LED_FIELD_Pin
#define PLATFORM_LED_FIELD_PIN LED_FIELD_Pin /*!< GPIO pin used as field LED */
#endif

#ifdef LED_FIELD_GPIO_Port
#define PLATFORM_LED_FIELD_PORT LED_FIELD_GPIO_Port /*!< GPIO port used as field LED */
#endif


#define PLATFORM_LED_A_PIN LED_RX_Pin /*!< GPIO pin used for LED A */
#define PLATFORM_LED_A_PORT LED_RX_GPIO_Port /*!< GPIO port used for LED A */
#define PLATFORM_LED_B_PIN LED_RX_Pin /*!< GPIO pin used for LED B */
#define PLATFORM_LED_B_PORT LED_RX_GPIO_Port /*!< GPIO port used for LED B */
#define PLATFORM_LED_F_PIN LED_RX_Pin /*!< GPIO pin used for LED F */
#define PLATFORM_LED_F_PORT LED_RX_GPIO_Port /*!< GPIO port used for LED F */
#define PLATFORM_LED_V_PIN LED_RX_Pin /*!< GPIO pin used for LED V */
#define PLATFORM_LED_V_PORT LED_RX_GPIO_Port /*!< GPIO port used for LED V */
#define PLATFORM_LED_AP2P_PIN LED_RX_Pin /*!< GPIO pin used for LED AP2P */
#define PLATFORM_LED_AP2P_PORT LED_RX_GPIO_Port /*!< GPIO port used for LED AP2P*/

#define PLATFORM_USER_BUTTON_PIN B1_Pin /*!< GPIO pin user button */
#define PLATFORM_USER_BUTTON_PORT B1_GPIO_Port /*!< GPIO port user button */​

Step 4 – Configuration of the operating mode

Where should I do this? Specify the antenna type and so on?

Step 5 – Initialization and work with ST25R

What's next?

 

 

 

    This topic has been closed for replies.
    Best answer by Brian TIDAL

    Hi,

    could you confirm that your I2C issue is now solved and that the interrupt line in stable?

    Regarding the antenna, I suggest to have a look on rfalChipSetAntennaMode API. 

    The following documentations are available:

    Rgds

    BT

    3 replies

    Technical Moderator
    September 19, 2025

    Hi,

    an STM32CubeMX pack is available for the ST25R3916(B): this is the X-CUBE-NFC6 pack.

    BrianTIDAL_0-1758281101981.png

    Inside the documentation folder of this pack (in <yourhomedir>\STM32Cube\Repository\Packs\STMicroelectronics), the user manual provides step-by-step instructions to configure the MCU, set up the RFAL middleware, and select the appropriate communication interface. STM32CubeMX then generates a ready-to-use demo (polling demo or NDEF read demo).

    It is recommended to base your development on the application generated by STM32CubeMX rather than manually integrating the RFAL from the standalone delivery. The standalone delivery is more suitable for non-ST MCUs, whereas the X-CUBE-NFC6 package is specifically designed for STM32 MCUs.

    Rgds

    BT

    vlad_04Author
    Graduate
    September 19, 2025

    I have a custom board with the ST25R3916B chip.
    And I don't understand whether I should choose "NFC_Applications" or "NFC_Boardface"?

    Technical Moderator
    September 22, 2025

    Hi,

    I suggest the following:

    BrianTIDAL_0-1758525967450.png

    You will probably have to fake the LEDs that are used on the X-NUCLEO-NFC08A1 as your application uses it's own custom board with only RX and field leds.

    Rgds

    BT

     

     

    vlad_04Author
    Graduate
    September 25, 2025

    Many thank you for the reply, I did it and my project compiled fine instead one thing:
    I saw an error for the line "USR_INT_LINE.PendingCallback = st25r3916Isr;" in the file "app_x-cube-nfcx.c". So I replaced it with "USR_INT_LINE.FallingCallback = st25r3916Isr;"

    When running the project, I noticed that after enabling the oscillator command, the I2C line was disabled, which is very strange, and the project no longer works.
    Maybe anyone understand why this is happening?
    P.S. An oscilloscope shows that oscillation is turned on at the moment the command is sent.

    vlad_04_0-1758803652033.png

     

    Technical Moderator
    September 25, 2025

    Hi,

    possibly a concurrency problem of I2C functions being called from main level and GPIO interrupt - please check platformProtectST25RComm()/platformUnprotectST25RComm().

    At second thought likely a interrupt priority issue: ISR of GPIO triggering I2C transfers which in turn requires active systick interrupts. Recommendation:

    Prio(Systick) > Prio(I2C) > Prio(ST25R_ISR), e.g. 0 - 2- 5 (the smaller the number the higher the priority)

    Weird that the INT pin is not stable at high: Is your VDD_IO stable?

    BR, Ulysses

    Technical Moderator
    September 26, 2025

    Hi,

    could you confirm that your I2C issue is now solved and that the interrupt line in stable?

    Regarding the antenna, I suggest to have a look on rfalChipSetAntennaMode API. 

    The following documentations are available:

    Rgds

    BT