RFAL ST25R - library integration (I2C)
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:

MCU configuration:
Connectivity → I²C → I²C mode with standard parameters:

GPIO settings for I²C: Enabled Pull-up resistors


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?
