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 Reply Children
  • 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

  • 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?

Related