Uint8_t david_digikey_coffee_cup_zephyr_i2c_write(uint8_t address, uint8_t value)

In the previous article it was shown how to connect to the TDK ICOM-20948 Accelerometer, Gyroscope Sensor Qwiic Platform Evaluation Expansion Board from Sparkfun with an actual Zephyr OS aplication) to scan the slave I2C addresses of the sensor from the Zephy OS shell. In this article the I2C write function will be developed here at Digikey for the Zephyr OS. Zephyr OS provides a mechanism for many sensors using KConfig and Devicetree tools, but this article provides an alternative for those who want to develop directly in the I2C interface in the Zephyr OS where in some cases is needed. Also the build process application, with the essential files and directory structure will be described for the Raspberry Pico 2. First, open a minicom terminal which will be used later to see the output from the Raspberry Pico 2 that will run the Zephyr OS application.

$ minicom -D /dev/ttyACM0

Welcome to minicom 2.8

OPTIONS: I18n 
Port /dev/ttyACM0, 12:41:43

Press CTRL-A Z for help on special keys

Within the Zephyr directory create a folder called myproject, then inside that folder create the following files.

The CMake file named CMakeLists.txt,

cmake_minimum_required(VERSION 3.25.1)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(i2c_scanner)
target_sources(app PRIVATE src/main.c)

The Zephyr OS configuration file named prj.conf,

CONFIG_I2C=y
CONFIG_RTIO=y
CONFIG_I2C_RTIO=y
CONFIG_I2C_TARGET=y
CONFIG_I2C_TARGET_BUFFER_MODE=y

The Zephyr OS Overlay file in this case named RP2350.overlay that defines the I2C port in this case i2c port 0 in the Raspberry Pico 2,

/ {
    aliases {
        scan-i2c = &i2c0;
    };
};

The main.c program that follows was developed at Digikey and will reside in a new subfolder called src.

/*Digikey Coffee Cup*/

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/i2c/rtio.h>
#include <string.h>

#define CONFIG_I2C_RTIO_LOOPBACK_DATA_WRITE_MAX_SIZE 1
#define CONFIG_I2C_RTIO_LOOPBACK_DATA_READ_MAX_SIZE 1
#define I2C_TARGET_ADDR 0x69

static const struct device *scan_dev = DEVICE_DT_GET(DT_ALIAS(scan_i2c));

static uint8_t sample_write_data[CONFIG_I2C_RTIO_LOOPBACK_DATA_WRITE_MAX_SIZE];
static uint8_t sample_write_buf[sizeof(sample_write_data)];
static uint32_t sample_write_buf_pos;

static uint8_t sample_read_data[CONFIG_I2C_RTIO_LOOPBACK_DATA_READ_MAX_SIZE];
static uint32_t sample_read_data_pos;
static uint8_t sample_read_buf[sizeof(sample_read_data)];


uint8_t david_digikey_coffee_cup_zephyr_i2c_write(uint8_t address, uint8_t value)
{
    
    uint8_t error = 0u;
    struct i2c_msg msgs[2];
    msgs[0].buf = &address;
    msgs[0].len = 1U;
    msgs[0].flags = I2C_MSG_WRITE;
    msgs[1].buf = &value;
    msgs[1].len = sizeof(value);
    msgs[1].flags = I2C_MSG_WRITE | I2C_MSG_STOP;
    
    error = i2c_transfer(scan_dev, msgs, 2, I2C_TARGET_ADDR);
    if (error == 0) {
            printk("I2C Bus Digikey Coffee Cup Write Complete\n");

    }
    else {
            printk("I2C Bus Digikey Coffee Cup Error\n");
    }

    return  error;
    
}

int main(void) 
{
    k_sleep(K_SECONDS(1));
    if (!scan_dev) {
        printk("I2C: Device driver not found.\n");
        return -1;
    }

    printk("*** David Digikey Coffee Cup Zephyr I2C ***\n");
    printk("Board:           %s\n", CONFIG_BOARD);
    printk("I2C device:      %s\n", scan_dev->name);
    
    
    uint8_t address = 0x00;
    uint8_t theregister = 0x00;
    uint8_t value = 0x00;
    
    while(1)
    {
        printk("david_digikey_coffee_cup_zephyr_i2c_write(address, value)\n");
        address = 0x05;
        value = 0x00;
        david_digikey_coffee_cup_zephyr_i2c_write(address, value);

        k_sleep(K_MSEC(500));
    }
    
	return 0;
    
}

The previous program shows how to write to the sensor every 500 mS in the I2C bus using the Zephyr OS Raspberry Pico 2.

The complete folder myproject will look like this,

|-- CMakeLists.txt
|-- prj.conf
|-- RP2350.overlay
`-- src
    `-- main.c

Now at this point build the Zephyr OS application for the Raspberry Pico 2 using the previous RP2350.overlay file as follows,

(venv) $ west build -p always -b rpi_pico2/rp2350a/m33 -S cdc-acm-console -- -DCONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=y -DEXTRA_DTC_OVERLAY_FILE=RP2350.overlay

The Sparkfun 8-channel USB Logic Analyzer previously described in this Techforum article was setup as shown in the next picture using these Qwiic cables to monitor the I2C bus using the proper voltage source, PIN6 (SDA), PIN7 (SCL) (I2C Port 0) and PIN 38 (GND) on the Raspberry Pico 2 as shown below,

and the actual setup in evaluation bread board from Digikey,

The Sparkfun 8-channel USB Logic Analyzer was connected to the SCL, SDA (I2C bus signals) and the GND (Ground) as shown above (Not the optimum way of connecting it due to long cable runs, increased first order lumped RC time constant, etc, perturbation of the I2C bus). Now proceed to flash the Zephyr OS application into the Raspberry Pico 2

(venv) $ west flash --runner uf2

In the minicom terminal the output will display from the Raspberry Pico 2 the following,

david_digikey_coffee_cup_zephyr_i2c_write(address, value)
I2C Bus Digikey Coffee Cup Write Complete
david_digikey_coffee_cup_zephyr_i2c_write(address, value)
I2C Bus Digikey Coffee Cup Write Complete
david_digikey_coffee_cup_zephyr_i2c_write(address, value)
I2C Bus Digikey Coffee Cup Write Complete
david_digikey_coffee_cup_zephyr_i2c_write(address, value)
I2C Bus Digikey Coffee Cup Write Complete
david_digikey_coffee_cup_zephyr_i2c_write(address, value)
I2C Bus Digikey Coffee Cup Write Complete
david_digikey_coffee_cup_zephyr_i2c_write(address, value)
I2C Bus Digikey Coffee Cup Write Complete
david_digikey_coffee_cup_zephyr_i2c_write(address, value)
I2C Bus Digikey Coffee Cup Write Complete
david_digikey_coffee_cup_zephyr_i2c_write(address, value)
........

The Sparkfun 8-channel USB Logic Analyzer confirms that this Zephyr C program running inside the Raspberry Pico 2 using only the I2C definition in the Zephyr OS overlay works. The proper sequence I2C interface I2C_MSG_WRITE, I2C_MSG_WRITE, I2C_MSG_STOP defined in the main.c developed here at Digikey for the internal finite state machine is followed.

This I2C write transaction confirmed by the logic analyzer on the I2C bus, changes the low power configuration mode to remove duty cycled mode of the MEMS sensors. Have a nice day!

This article is also available in spanish language here.

Este artículo esta disponible en idioma español aquí.

2 Likes