Issue Using NFC Pins (P0.02/P0.03) as I2C Slave on nRF5340

Hello Nordic Support Team,

I’m working with an nRF5340 using nRF Connect SDK 3.0.2.

I am trying to use the NFC pins (P0.02 and P0.03) as an I2C slave interface. However, when I use these pins, the I2C master never receives a valid NACK from my slave device. If I change to other pins, the communication works correctly.

My I2C TWIS driver source code, .conf file, and .overlay file are attached to this ticket.

How can I resolve this issue? My current hardware is routed to use the NFC pins.

Thanks in advance.

Best regards.

3324.app.conf

5481.appl.overlay

/**
 * @file i2cDrv.c
 * @author Luis Maciel ([email protected])
 * @brief
 * @version x.x.x
 * @date 2025-02-13
 *
 * @copyright Copyright LogPyx S/A (c) 2025
 *
 * 	 _
 *	| |    ___   __ _ _ __  _   ___  __
 *	| |   / _ \ / _` | '_ \| | | \ \/ /
 *	| |__| (_) | (_| | |_) | |_| |>  <
 *	|_____\___/ \__, | .__/ \__, /_/\_\
 *                |__|_|    |___/
 *
 *              Logpyx S/A
 */

#include <zephyr/kernel.h>
#include <zephyr/types.h>
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
#include <zephyr/devicetree/pinctrl.h>

#include <nrfx_twis.h>

#include "i2cDrv.h"

LOG_MODULE_REGISTER(i2cDrv, LOG_LEVEL_INF);

#define I2C_SLAVE 1

#if I2C_SLAVE
#define TWI_INSTANCE_ID 2
#define I2C2_NODE DT_NODELABEL(i2c2)
#define I2C2_ADDRESS 0x11

static const nrfx_twis_t s_twi = NRFX_TWIS_INSTANCE(TWI_INSTANCE_ID);

static i2cIrqHandler rxDone;

static void twis_event_handler(nrfx_twis_evt_t const *const p_event);
#else
static const struct i2c_dt_spec dev_i2c; // = I2C_DT_SPEC_GET(DT_ALIAS(myesp));
#endif

uint8_t rx_buf[TWI_RX_BUF_LEN];

void i2cDrvIn(uint8_t *txBuffer, uint16_t size)
{
#if I2C_SLAVE
    nrfx_twis_tx_prepare(&s_twi, txBuffer, size);
#else
    i2c_write_dt(&dev_i2c, txBuffer, size);
#endif
}

void i2cDrvInOut(uint8_t *txBuffer, uint16_t txSize, uint8_t *rxBuffer, uint16_t rxSize)
{
#if !I2C_SLAVE
    i2c_write_read_dt(&dev_i2c, txBuffer, txSize, rxBuffer, rxSize);
#endif
}

void i2cDrvOut(uint8_t *rxBuffer, uint16_t size)
{
    ARG_UNUSED(rxBuffer);
    ARG_UNUSED(size);
}

bool i2cDrvIsWaiting4Tx(void)
{
#if I2C_SLAVE
    return nrfx_twis_is_waiting_tx_buff(&s_twi);
#endif
    return false;
}

void i2cDrvInit(i2cIrqHandler cb)
{
#if I2C_SLAVE
    IRQ_CONNECT(DT_IRQN(I2C2_NODE),
                DT_IRQ(I2C2_NODE, priority),
                nrfx_isr,
                nrfx_twis_2_irq_handler,
                0);

    nrfx_twis_config_t twis_config = NRFX_TWIS_DEFAULT_CONFIG(30, 3, I2C2_ADDRESS);
    nrfx_twis_init(&s_twi, &twis_config, twis_event_handler);
    nrfx_twis_enable(&s_twi);
    rxDone = cb;
#else
    while (!i2c_is_ready_dt(&dev_i2c))
    {
        k_msleep(1);
    }
#endif
}

#if I2C_SLAVE

static void twis_event_handler(nrfx_twis_evt_t const *const p_event)
{
    switch (p_event->type)
    {
    case NRFX_TWIS_EVT_READ_REQ:
        if (p_event->data.buf_req)
        {
            // Sending default message
            static uint8_t default_msg[] = {0x01, 0x02, 0x03};
            nrfx_twis_tx_prepare(&s_twi, default_msg, sizeof(default_msg));
            // LOG_INF("Send default value to master");
        }
        break;

    case NRFX_TWIS_EVT_READ_DONE:
        // LOG_INF("Master terminou leitura.");
        break;

    case NRFX_TWIS_EVT_WRITE_REQ:
        if (p_event->data.buf_req)
        {
            // preparar buffer para receber
            // LOG_INF("Request to receive bytes from master", TWI_RX_BUF_LEN);
            nrfx_twis_rx_prepare(&s_twi, rx_buf, TWI_RX_BUF_LEN);
        }
        break;

    case NRFX_TWIS_EVT_WRITE_DONE:
    {
        // terminou de receber dados do master
        // LOG_INF("I2C Slave recebeu %d bytes:", p_event->data.rx_amount);
        // LOG_HEXDUMP_INF(rx_buf, p_event->data.rx_amount, "RX DATA");
        if (rxDone)
        {
            rxDone(rx_buf, p_event->data.rx_amount);
        }
        break;
    }

    case NRFX_TWIS_EVT_READ_ERROR:
    case NRFX_TWIS_EVT_WRITE_ERROR:
    case NRFX_TWIS_EVT_GENERAL_ERROR:
        break;

    default:
        break;
    }
}
#endif

Parents
  • Hello,

    I see you have correctly included the uicr node with the nfct-pins-as-gpios; property in your overlay, so provided the overlay is being applied to the build, I would have expected this to work (you can verify that it's applied by looking at the generated zephyr.dts). Are you testing this on a custom board or the nRF5340 DK?

    Best regards,

    Vidar

  • Thank you for your response, Vidar.

    While I don't have an nRF5340DK available for testing with this specific pin configuration, I can confirm that my I2C slave implementation works correctly on a custom board using the following configuration:

    ```dts
    i2c2_default: i2c2_default {
    group1 {
    psels = <NRF_PSEL(TWIS_SDA, 0, 3)>,
    <NRF_PSEL(TWIS_SCL, 0, 30)>;
    bias-pull-up;
    nordic,drive-mode = <NRF_DRIVE_E0E1>;
    };
    };

    The only modification from my working custom board design is changing the SCL pin to P0.02.

    In my zephyr.dts file, I currently have:

    uicr: uicr@ff8000 {
    compatible = "nordic,nrf-uicr";
    reg = <0xff8000 0x1000>;
    status = "okay";
    nfct-pins-as-gpios;
    };


     

  • P0.03 does not support E drive modes AFAIK. Its also wrong to use a mode that actively drives "high" level with I²C bus lines - that is what the pullup resistors are there for.

    Try omitting the "nordic,drive-mode" line or set it to "H0D1".

Reply Children
  • Thank you for your support, Turbo J.

    Even though I'm using the following configuration:

    ```dts
    i2c2_default: i2c2_default {
    group1 {
    psels = <NRF_PSEL(TWIS_SDA, 0, 3)>,
    <NRF_PSEL(TWIS_SCL, 0, 2)>;
    };
    };

    i2c2_sleep: i2c2_sleep {
    group1 {
    psels = <NRF_PSEL(TWIS_SDA, 0, 3)>,
    <NRF_PSEL(TWIS_SCL, 0, 2)>;
    low-power-enable;
    };
    };

    I'm still experiencing the same behavior.

  • Thanks for confirming. The reason I asked what board you were using was because of on the DK the the NFC pins are not routed to the pin headers by default. In the DT overlay you can omit the drive strength setting ('E' drive is not available either) as Turbo said, but you need to keep the bias pull up property if you want to enable the internal pull ups. 

    Have you confirmed that you are able to drive these pins on your board? For example, by measuring if the pull-up is applied or by using a logic analyser? 

  • Thank you for your response, Vidar.

    Yes, I can confirm that I'm able to successfully toggle the pins between high and low states using the debug code I developed.

    I can also verify that the signals are transmitting correctly. When using the same I2C bus with another slave device (my other custom board that uses P0.30 for SCL), the communication works properly.

  • During testing, I observed the following:

    1. Successful Configuration: The nRF5340 operates correctly as an I2C master when using P0.03 for SDA and P0.02 for SCL on my new board design.

    2. Failed Configuration: On an older board, a previously working configuration (SDA on P0.03, SCL on P0.30) fails when the pins are swapped in the device tree:

      • Working: psels = <NRF_PSEL(TWIS_SDA, 0, 3)>, <NRF_PSEL(TWIS_SCL, 0, 30)>

      • Failing: psels = <NRF_PSEL(TWIS_SDA, 0, 30)>, <NRF_PSEL(TWIS_SCL, 0, 3)>

    The issue suggests a problem on I2C slave driver.

  • So you were able to use the NFC pins now after you switched to new board, or is it still the same board? If you suspect the driver, you may consider reading out the TWIS PSEL registers and GPIO CFG registers to confirm the pins are configured correctly and according to the product specification. You can also add logging for the TWIS error events in the TWIS callback.

Related