I2C communication between Renesas MCU(master) -> nRF5340(slave).

Hello Nordic Team,

I am using nRF5340 DK as a debugger/programmer to develop firmware for the Fanstel EV-WT02C40C module.
My goal is to do i2c communication between Renesas MCU and  EV-WT02C40C board with nRF Connect SDK.

I have a few i2c communication related questions and would appreciate clarification.

SDK:         nRF Connect SDK v3.2.1
Toolchain: nRF Connect SDK Toolchain v3.2.1

here i'm considering renesas MCU as master and nrf5340 MCU as slave. renesas mcu send data to nrf5340 mcu which i will publish on server with the help of wifi and MQTT

which sample code should i refer for my i2c communication query? 

Parents
  • Hi,

    You can take a look at this guide "TWI/I2C implementation with nRFx TWIS Driver". It shows how to make a TWI/I2C slave. It has been made for the nRF52840 DK, but it shouldn't be too hard to port it to the nRF5340 DK.

    We also have some sample concerning TWI master and slave implementation here [GitHub - nrfx_twim_twis].

    If you need more information about TWI/I2C, feel free to ask it here.

    Best regards,

    Simon D-M

  • hi,

    i tried with my main.c file.
     

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/i2c.h>
    #include <zephyr/logging/log.h>
    #include <string.h>
    
    LOG_MODULE_REGISTER(i2c_slave, LOG_LEVEL_INF);
    
    #define I2C_SLAVE_NODE DT_NODELABEL(i2c1)
    #define I2C_SLAVE_ADDR 0x55
    
    #define RX_BUFFER_SIZE 32
    #define TX_BUFFER_SIZE 32
    
    static uint8_t rx_buffer[RX_BUFFER_SIZE];
    static uint8_t tx_buffer[TX_BUFFER_SIZE];
    
    static size_t rx_index = 0;
    static size_t tx_index = 0;
    static size_t tx_length = 0;
    
    static const struct device *i2c_dev;
    
    /* Data received from Master */
    static struct
    {
        uint8_t temperature;
        uint8_t humidity;
        uint8_t pressure;
    } sensor_data;
    
    /* ================= I2C Callbacks ================= */
    
    static int write_requested_cb(struct i2c_target_config *config)
    {
        rx_index = 0;
        return 0;
    }
    
    static int write_received_cb(struct i2c_target_config *config, uint8_t val)
    {
        if (rx_index < RX_BUFFER_SIZE)
        {
            rx_buffer[rx_index++] = val;
            return 0;
        }
        return -ENOMEM;
    }
    
    static int read_requested_cb(struct i2c_target_config *config, uint8_t *val)
    {
        tx_index = 0;
    
        /* Prepare 3 uint8_t values = 3 bytes */
        tx_buffer[0] = sensor_data.temperature;
        tx_buffer[1] = sensor_data.humidity;
        tx_buffer[2] = sensor_data.pressure;
    
        tx_length = 3;
    
        *val = tx_buffer[tx_index++];
        return 0;
    }
    
    static int read_processed_cb(struct i2c_target_config *config, uint8_t *val)
    {
        if (tx_index < tx_length)
        {
            *val = tx_buffer[tx_index++];
        }
        else
        {
            *val = 0xFF;
        }
    
        return 0;
    }
    
    static int stop_cb(struct i2c_target_config *config)
    {
        /* Expecting 3 uint8_t = 3 bytes */
        if (rx_index == 3)
        {
            memcpy(&sensor_data.temperature, &rx_buffer[0], sizeof(uint8_t));
            memcpy(&sensor_data.humidity, &rx_buffer[1], sizeof(uint8_t));
            memcpy(&sensor_data.pressure, &rx_buffer[2], sizeof(uint8_t));
    
            LOG_INF("Received from Master:");
            LOG_INF("Temp: %d", sensor_data.temperature);
            LOG_INF("Humidity: %d", sensor_data.humidity);
            LOG_INF("Pressure: %d", sensor_data.pressure);
        }
        else if (rx_index > 0)
        {
            LOG_WRN("Unexpected data length: %d", rx_index);
        }
    
        rx_index = 0;
        return 0;
    }
    
    /* Callback structure */
    static struct i2c_target_callbacks callbacks = {
        .write_requested = write_requested_cb,
        .write_received = write_received_cb,
        .read_requested = read_requested_cb,
        .read_processed = read_processed_cb,
        .stop = stop_cb,
    };
    
    static struct i2c_target_config target_config = {
        .address = I2C_SLAVE_ADDR,
        .callbacks = &callbacks,
    };
    
    /* ================= Main ================= */
    
    int main(void)
    {
        int ret;
    
        LOG_INF("nRF5340 I2C Slave Started (Address 0x55)");
    
        i2c_dev = DEVICE_DT_GET(I2C_SLAVE_NODE);
    
        if (!device_is_ready(i2c_dev))
        {
            LOG_ERR("I2C device not ready");
            return -ENODEV;
        }
        LOG_INF("I2C device name: %s", i2c_dev->name);
        ret = i2c_target_register(i2c_dev, &target_config);
        if (ret)
        {
            LOG_ERR("Failed to register I2C target: %d", ret);
            return ret;
        }
    
        LOG_INF("I2C Slave registered successfully");
    
        while (1)
        {
            k_sleep(K_SECONDS(1));
        }
    
        return 0;
    }

    .overlay file

    &pinctrl {
    i2c1_default: i2c1_default {
    group1 {
    psels = <NRF_PSEL(TWIS_SDA, 1, 2)>,
            <NRF_PSEL(TWIS_SCL, 1, 3)>;
            bias-pull-up;
        };
    };
    
    i2c1_sleep: i2c1_sleep {
    group1 {
    psels = <NRF_PSEL(TWIS_SDA, 1, 2)>,
            <NRF_PSEL(TWIS_SCL, 1, 3)>;
            low-power-enable;
            };
        };
    };
    
    &i2c1 {
    compatible = "nordic,nrf-twis";
    pinctrl-0 = <&i2c1_default>;
    pinctrl-1 = <&i2c1_sleep>;
    pinctrl-names = "default", "sleep";
    status = "okay";
    };

    prj.conf file

    #i2c
    CONFIG_I2C=y
    CONFIG_I2C_TARGET=y
    
    # Logging
    CONFIG_LOG=y
    CONFIG_I2C_LOG_LEVEL_DBG=y
    CONFIG_PRINTK=y
    CONFIG_LOG_MODE_DEFERRED=y
    CONFIG_I2C_LOG_LEVEL_INF=y


    transmission from renesas  mcu(master) is working
    but receiving end something missing.

    logs.

    00:00:49.113,372] <err> os: ***** USAGE FAULT *****
    [00:00:49.113,372] <err> os:   Illegal use of the EPSR
    [00:00:49.113,403] <err> os: r0/a1:  0x20000000  r1/a2:  0x2000122d  r2/a3:  0x00000003
    [00:00:49.113,433] <err> os: r3/a4:  0x00000000 r12/ip:  0xd04ae020 r14/lr:  0x00005ff7
    [00:00:49.113,433] <err> os:  xpsr:  0x60000019
    [00:00:49.113,464] <err> os: Faulting instruction address (r15/pc): 0x00000000
    [00:00:49.113,494] <err> os: >>> ZEPHYR FATAL ERROR 35: Unknown error on CPU 0
    [00:00:49.113,494] <err> os: Fault during interrupt handling
    
    [00:00:49.113,525] <err> os: Current thread: 0x20000678 (idle)
    [00:00:49.306,213] <err> os: Halting system

      this is the waveforms   

  • Hi,

    Can you please give more information on when does the crash happen? Can you also give the full serial logs?

    Best regards,

    Simon D-M

Reply Children
  • based on you recommendation  i change devicetree, prj.conf and main.c  file which i will attaching.

    1. main.c

    /*
     * nRF5340 I2C Slave - Simple 3-byte Array
     * Compatible with RA6M5 Master
     * Based on Nordic nrfx example
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/irq.h>
    #include <zephyr/devicetree.h>
    #include <nrfx_twis.h>
    
    LOG_MODULE_REGISTER(i2c_slave, LOG_LEVEL_INF);
    
    /* Configuration */
    #define SLAVE_ADDR 0x55
    #define DATA_SIZE 3
    
    /* Pin definitions (CORRECTED) */
    #define SCL_PIN 35 /* P1.03 */
    #define SDA_PIN 34 /* P1.02 */
    
    /* TWIS instance */
    // static nrfx_twis_t m_twis = NRFX_TWIS_INSTANCE(1);
    static nrfx_twis_t m_twis_inst = NRFX_TWIS_INSTANCE(0);
    
    /* Data buffers */
    static uint8_t m_tx_buffer[DATA_SIZE] = {25, 50, 100}; /* Data to send to master */
    static uint8_t m_rx_buffer[DATA_SIZE];                 /* Data received from master */
    
    /* ========== TWIS Event Handler ========== */
    static void twis_handler(nrfx_twis_event_t const *p_event)
    {
        int status;
        (void)status;
    
        switch (p_event->type)
        {
        case NRFX_TWIS_EVT_WRITE_REQ:
            /* Master wants to write - prepare to receive */
            status = nrfx_twis_rx_prepare(&m_twis_inst, m_rx_buffer, DATA_SIZE);
            NRFX_ASSERT(status == 0);
            break;
    
        case NRFX_TWIS_EVT_WRITE_DONE:
            /* Data received from master */
            m_tx_buffer[0] = 0X11;
            m_tx_buffer[1] = 0X12;
            m_tx_buffer[2] = 0X13;
            break;
    
        case NRFX_TWIS_EVT_READ_REQ:
            /* Master wants to read - send TX buffer */
            status = nrfx_twis_tx_prepare(&m_twis_inst, m_tx_buffer, DATA_SIZE);
            NRFX_ASSERT(status == 0);
            break;
    
        case NRFX_TWIS_EVT_READ_DONE:
            break;
    
        default:
            break;
        }
    }
    /* ========== IRQ Handler Wrapper ========== */
    static void twis_irq_wrapper(const void *param)
    {
        ARG_UNUSED(param);
        nrfx_twis_0_irq_handler();
    }
    /* ========== Main ========== */
    int main(void)
    {
        int status;
        /*
         *  irq_p = IRQ line number.
         *  priority_p = Interrupt priority.
         *  isr_p = Address of interrupt service routine.
         *  isr_param_p =  Parameter passed to interrupt service routine.
         *  flags_p = Architecture-specific IRQ configuration flags..
         * */
        /* Connect IRQ (CRITICAL for Zephyr) */
        IRQ_CONNECT(DT_IRQN(DT_NODELABEL(i2c0)),
                    DT_IRQ(DT_NODELABEL(i2c0), priority),
                    nrfx_isr,
                    nrfx_twis_0_irq_handler,
                    0);
    
        irq_enable(DT_IRQN(DT_NODELABEL(i2c0)));
    
        /* Configure TWIS - CORRECTED PIN ORDER */
        nrfx_twis_config_t config = NRFX_TWIS_DEFAULT_CONFIG(
            SCL_PIN, /* P1.03 = SCL âś… */
            SDA_PIN, /* P1.02 = SDA âś… */
            SLAVE_ADDR);
    
        /* Initialize TWIS */
        status = nrfx_twis_init(&m_twis_inst, &config, twis_handler);
        NRFX_ASSERT(status == NRFX_SUCCESS);
    
        /* Enable TWIS */
        nrfx_twis_enable(&m_twis_inst);
    
        while (1)
        {
            k_sleep(K_SECONDS(1));
        }
    
        return 0;
    }
     

    IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_TWIS_INST_GET(TWIS_INST_IDX)), IRQ_PRIO_LOWESTnrfx_twis_irq_handler, &m_twis_inst, 0); 

    note :above  line is form twim_twia_txrx sample code.
    i'm facing challenges here. 

    2.devicetree .overlay file

    &i2c1 {
        status = "disabled";
    };


    i only mention i2c1 disabled because of I'm configuring  twis for nordic driver.
    if you think i should configure through devicetree(.overlay file) let me know what should i write and what should i ignore 

    my requirement  is Renesas is my hot mcu which is i2c master and i have EV-WT02C40C fanstle board where nrf5340 mcu is twis (as i2c slave ) 
    currently is using i2c1 and pin port is (SCL = 1.03 & SDA =1.02).

    3.prj.conf 

    #i2c
    CONFIG_NRFX_TWIS=y

    common.conf
    CONFIG_LOG=y
    CONFIG_BOOT_BANNER=n
    CONFIG_NCS_BOOT_BANNER=n
    CONFIG_ASSERT=y
    CONFIG_LOG_PROCESS_THREAD=n

    4.and in log im facing this 
    [00:00:00.253,234] <err> os: ***** MPU FAULT *****
    [00:00:00.253,234] <err> os:   Data Access Violation
    [00:00:00.253,265] <err> os:   MMFAR Address: 0x501
    [00:00:00.253,265] <err> os: r0/a1:  0x20000000  r1/a2:  0x20001e98  r2/a3:  0x00000165
    [00:00:00.253,295] <err> os: r3/a4:  0x00000000 r12/ip:  0x00000000 r14/lr:  0x00000209
    [00:00:00.253,295] <err> os:  xpsr:  0x29000000
    [00:00:00.253,295] <err> os: Faulting instruction address (r15/pc): 0x00005c3e
    [00:00:00.253,326] <err> os: >>> ZEPHYR FATAL ERROR 19: Unknown error on CPU 0
    [00:00:00.253,356] <err> os: Current thread: 0x20000718 (main)
    [00:00:00.315,582] <err> os: Halting system
    






  • Hi,

    Sorry, I looked a bit more into details into the samples that I sent you before, and I don't really like how they are implementing things...

    However, I found this sample called custom_target. This uses the default Zephyr i2c library (which will use the nrfx library under the hood for Nordic chips), and I like much more how this was implemented. It uses devicetree and has a higher level of abstraction.

    By default, the nRF5340 DK is not supported, but the port to the nRF5340 DK is not hard. Here is how to do it:

    • Create a devicetree overlay file that contains the i2c node and the corresponding pinctrls.
      nrf5340dk_nrf5340_cpuapp.overlay :
      / {
      	aliases {
      		i2c-target = &i2c1;
      	};
      };
      
      &i2c1 {
      	compatible = "nordic,nrf-twis";
      	pinctrl-0 = <&i2c1_default>;
      	pinctrl-1 = <&i2c1_sleep>;
      	pinctrl-names = "default", "sleep";
      	status = "okay";
      };
      
      &pinctrl {
          i2c1_default: i2c1_default {
      		group1 {
      			psels = <NRF_PSEL(TWIS_SDA, 1, 2)>,
      				<NRF_PSEL(TWIS_SCL, 1, 3)>;
      			bias-pull-up;
      		};
      	};
      
      	i2c1_sleep: i2c1_sleep {
      		group1 {
      			psels = <NRF_PSEL(TWIS_SDA, 1, 2)>,
      				<NRF_PSEL(TWIS_SCL, 1, 3)>;
      			low-power-enable;
      		};
      	};
      };
      
    • Change the main.c to use the newly created node.
      main.c :
      /*
       * Copyright (c) 2024 Open Pixel Systems
       *
       * SPDX-License-Identifier: Apache-2.0
       */
      
      #include <zephyr/kernel.h>
      #include <zephyr/sys/printk.h>
      #include <zephyr/drivers/i2c.h>
      
      static const struct device *bus = DEVICE_DT_GET(DT_ALIAS(i2c_target));
      static char last_byte;
      
      /*
       * @brief Callback which is called when a write request is received from the master.
       * @param config Pointer to the target configuration.
       */
      int sample_target_write_requested_cb(struct i2c_target_config *config)
      {
      	printk("sample target write requested\n");
      	return 0;
      }
      
      /*
       * @brief Callback which is called when a write is received from the master.
       * @param config Pointer to the target configuration.
       * @param val The byte received from the master.
       */
      int sample_target_write_received_cb(struct i2c_target_config *config, uint8_t val)
      {
      	printk("sample target write received: 0x%02x\n", val);
      	last_byte = val;
      	return 0;
      }
      
      /*
       * @brief Callback which is called when a read request is received from the master.
       * @param config Pointer to the target configuration.
       * @param val Pointer to the byte to be sent to the master.
       */
      int sample_target_read_requested_cb(struct i2c_target_config *config, uint8_t *val)
      {
      	printk("sample target read request: 0x%02x\n", *val);
      	*val = 0x42;
      	return 0;
      }
      
      /*
       * @brief Callback which is called when a read is processed from the master.
       * @param config Pointer to the target configuration.
       * @param val Pointer to the next byte to be sent to the master.
       */
      int sample_target_read_processed_cb(struct i2c_target_config *config, uint8_t *val)
      {
      	printk("sample target read processed: 0x%02x\n", *val);
      	*val = 0x43;
      	return 0;
      }
      
      /*
       * @brief Callback which is called when the master sends a stop condition.
       * @param config Pointer to the target configuration.
       */
      int sample_target_stop_cb(struct i2c_target_config *config)
      {
      	printk("sample target stop callback\n");
      	return 0;
      }
      
      static struct i2c_target_callbacks sample_target_callbacks = {
      	.write_requested = sample_target_write_requested_cb,
      	.write_received = sample_target_write_received_cb,
      	.read_requested = sample_target_read_requested_cb,
      	.read_processed = sample_target_read_processed_cb,
      	.stop = sample_target_stop_cb,
      };
      
      int main(void)
      {
      	struct i2c_target_config target_cfg = {
      		.address = 0x60,
      		.callbacks = &sample_target_callbacks,
      	};
      
      	printk("i2c custom target sample\n");
      
      	if (i2c_target_register(bus, &target_cfg) < 0) {
      		printk("Failed to register target\n");
      		return -1;
      	}
      
      	k_msleep(5000);
      
      	if (i2c_target_unregister(bus, &target_cfg) < 0) {
      		printk("Failed to unregister target\n");
      		return -1;
      	}
      
      	return 0;
      }
      

    After applying these changes you should be able to run the sample on a nRF5340 DK (It builds, flashes, and runs, but I didn't try to actually use it).

    From that sample you should be able to implement a cleaner application.

    If you need more help on how to implement the logic inside the slave, feel free to ask. I can try to make a small example.

    Best regards,

    Simon D-M

  • Hi,

    I want to use nRF5340 as an I2C Slave and Renesas MCU as I2C Master.

    My requirement is:

    • Renesas MCU will act as I2C Master

    • nRF5340 will act as I2C Slave (using TWIS0)

    • Renesas will send data to nRF5340

    • nRF5340 should receive that data and process it

      My question is:

      If Renesas sends data using I2C write so is this your given code will act as slave and receive data?
      i means nrf5340 will work as slave ? i seen in nordic community discussion zephyr os driver is not able to work with  Nordic slave hardware is it true?

  • Hi,

    The naming "master" and "slave" is getting slowly removed from all the documentation. It refers to slavery which is not very nice. It is being replaced by other nomenclature. The one used in this sample is "controller" (master) and "target" (slave).

    So this sample should be the right one for your needs.

    Embedded developer0 said:
    i seen in nordic community discussion zephyr os driver is not able to work with  Nordic slave hardware is it true?

    I've never tried using our drivers for an i2c target, but I don't see why it shouldn't work. If you want, I can try to make a small sample using zephyr's driver. However, I can't make it before Monday (CET time). 

    Best regards,

    Simon D-M

  • yeah please if you can make small smaple using zephyr's driver for nrf5340 i2c target. please do it.

Related