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? 

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

  • 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

Related