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   

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

  • i tried with your suggestion and face this issue. where data is not receiving by nrf5340 i2c target
    log

    *** Booting nRF Connect SDK v3.2.1-d8887f6f32df ***
    *** Using Zephyr OS v4.2.99-ec78104f1569 ***
    i2c custom target sample
    ***** USAGE FAULT *****
      Illegal use of the EPSR
    r0/a1:  0x200019e4  r1/a2:  0x20000cbb  r2/a3:  0x00000001
    r3/a4:  0x00000000 r12/ip:  0xc5c9c6ed r14/lr:  0x00002e19
     xpsr:  0x60000019
    Faulting instruction address (r15/pc): 0x00000000
     

    logic analyzer 




    overlay file

    / {
        aliases {
            i2c-target = &i2c1;
        };
    };
    
    &i2c1 {
        compatible = "nordic,nrf-twis";
        status = "okay";
        clock-frequency = <I2C_BITRATE_STANDARD>;
        pinctrl-0 = <&i2c1_default>;
        pinctrl-1 = <&i2c1_sleep>;
        pinctrl-names = "default", "sleep";
    
    };
    
    &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;
            };
        };
    };


    main.c

    #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;
    
    int sample_target_write_requested_cb(struct i2c_target_config *config)
    {
    	printk("sample target write requested\n");
    	return 0;
    }
    
    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;
    }
    
    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;
    }
    
    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;
    }
    
    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 = 0x55,
    		.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;
    	}
    
    	while (1)
    	{
    	}
    	return 0;
    }







  • Hi,

    I've used the "custom_target" sample as a base for my small sample.

    I first encountered the same issue you had with the hard fault. However, this is because the sample used is not configured to use the config "CONFIG_I2C_TARGET_BUFFER_MODE" which is enabled by default. The result is that when the i2c receive a request, it tries to call a callback which is not implemented. Thus provoke a hard fault in the MCU.

    To fix that, we either need to deactivate that configuration or to implement these callbacks. I choose to implement the callbacks as it is preferable to use the "buffer mode".

    Here is the resulting code (I tried on a nrf54l15 so the nrf5340 overlay hasn't been tested):

    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;
    }
    
    #ifdef CONFIG_I2C_TARGET_BUFFER_MODE
    /*
     * @brief Callback which is called when a write buffer is received from the master.
     * @param config Pointer to the target configuration.
     * @param data Pointer to the buffer received from the master.
     * @param size Size of the buffer received from the master.
     */
    static void sample_target_buf_write_received_cb(struct i2c_target_config *config,
    					     uint8_t *data, uint32_t size)
    {
    	printk("sample target buffer write received, size: %d\n", size);
    	for (uint32_t i = 0; i < size; i++) {
    		printk("byte %d: 0x%02x\n", i, data[i]);
    	}
    }
    
    /*
     * @brief Callback which is called when a read buffer is requested from the master.
     * @param config Pointer to the target configuration.
     * @param data Pointer to the buffer to be sent to the master.
     * @param size Size of the buffer to be sent to the master.
     */
    static int sample_target_buf_read_requested_cb(struct i2c_target_config *config,
    					    uint8_t **data, uint32_t *size)
    {
    	printk("sample target buffer read requested\n");
    	static uint8_t buffer[5] = {0x44, 0x45, 0x46, 0x47, 0x48};
    	*data = buffer;
    	*size = sizeof(buffer);
    	return 0;
    }
    #endif /* CONFIG_I2C_TARGET_BUFFER_MODE */
    
    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,
    #ifdef CONFIG_I2C_TARGET_BUFFER_MODE
    	.buf_write_received = sample_target_buf_write_received_cb,
    	.buf_read_requested = sample_target_buf_read_requested_cb,
    #endif
    };
    
    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;
    	}
    	
    
    	while(1) {
    		k_sleep(K_SECONDS(10));
    	}
    
    	// never reached, but unregister the target before exiting
    	if (i2c_target_unregister(bus, &target_cfg) < 0) {
    		printk("Failed to unregister target\n");
    		return -1;
    	}
    
    	return 0;
    }
    

    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;
    		};
    	};
    };
    

    prj.conf

    CONFIG_I2C=y
    CONFIG_I2C_NRFX=y
    CONFIG_I2C_TARGET=y
    CONFIG_I2C_TARGET_BUFFER_MODE=y

    If you have any more questions / issue with the i2c target driver, feel free to ask !

    Best regards,

    Simon D-M

  • hi,

    still im facing same problem, data writing from master to slave is working fine
    but when master(controller) trying to read data from slave, slave is not sending data on bus.

    during reading from slave,  just address + ack bit is coming. data is missing

    i tried with both method 
    buffer method and non-buffer method. both method giving same result

  • Hi,

    Do you still get the hard-fault ? Can you show me the full logs, to see if they are the exact same?

    Have you updated the main.c, the prj.conf and the nrf5340dk_nrf5340_cpuapp.overlay with the code that I sent you?

    It sounds strange that it still happens, because I tried the reading and writing on this sample, and it worked on my end... Can you maybe also show me the firmware that you use as a master ?

    Best regards,

    Simon D-M

Reply
  • Hi,

    Do you still get the hard-fault ? Can you show me the full logs, to see if they are the exact same?

    Have you updated the main.c, the prj.conf and the nrf5340dk_nrf5340_cpuapp.overlay with the code that I sent you?

    It sounds strange that it still happens, because I tried the reading and writing on this sample, and it worked on my end... Can you maybe also show me the firmware that you use as a master ?

    Best regards,

    Simon D-M

Children
  • Hey Simon D-M,

    Thank you very much for your help.

    I managed to fix the I2C master transmit and receive issue. The problem was actually coming from the code snippet below:

    static int sample_target_buf_read_requested_cb(struct i2c_target_config *config,
                                                   uint8_t **data, uint32_t *size)
    {
        // printk("sample target buffer read requested\n");
        // static uint8_t buffer[1] = {0x44};
        *data = rx_buf;
        *size = sizeof(rx_buf);
        return 0;
    }

    When these two lines were enabled:

    printk("sample target buffer read requested\n");
    static uint8_t buffer[1] = {0x44};

    they introduced a small delay. Since this function is executed inside a callback context, the delay was not acceptable and the I2C read operation was failing.

    After commenting out these lines, the code works perfectly and the I2C master can now transmit and receive data correctly.

    Thanks again for your support!



Related