Non-blocking I2C Zephyr example with callbacks

Is there a Zephyr example somewhere for using non-blocking I2C writes?

I was able to get a basic I2C read/write command working using the blocking function i2c_write_read(). However, this is a blocking function, and I want to free up the CPU to do other tasks while the I2C transaction is being clocked out.

I am trying to implement the asynchronous version i2c_write_read_cb() by following along with the Zephyr documentation here. My code builds, but I am getting errors at runtime reporting -22 = invalid argument. Having a working example of non-blocking I2C reads, including the callback function implementation, would be a big help to setting this up.

Does such an example exist? If not, could someone help identify what I need to do to get this function working?

Note that I have my I2C interface running in its own thread. The thread runs fine, so I'm only including the code for this thread. I only intend to run my init_tas2563 function once, but I put it into the thread's while loop on a 5 second timer to help debug in the short term. 

init_tas2563 runs every 5 seconds, and it calls i2c_write_read_cb(). I want to observe the I2C traffic on my oscilloscope, and see my callback function process_temp_reading_cb() run and print out the value that is read over I2C.

/*
 * Based on the echo I2S driver sample in the Nordic SDK.
 * Found at: ncs/v2.5.0/zephyr/samples/drivers/i2s/echo/
 */

#include <zephyr/kernel.h>
#include "flare_codec.h"
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>


#if DT_ON_BUS(TAS2563_NODE, i2c)

#define TAS2563_I2C_NODE DT_BUS(TAS2563_NODE)
#define TAS2563_I2C_ADDR DT_REG_ADDR(TAS2563_NODE)

#define FLARE_AUDIO_TASK_PRIORITY CONFIG_SIDEWALK_THREAD_PRIORITY

LOG_MODULE_REGISTER(audio_task, CONFIG_SIDEWALK_LOG_LEVEL);

#define AUDIO_TASK_STACK_SIZE (2048)
#define AUDIO_MSG_THREAD_QUEUE_SIZE 10

K_THREAD_STACK_DEFINE(flare_audio_task_stack, AUDIO_TASK_STACK_SIZE);

K_MSGQ_DEFINE(audio_task_msgq, sizeof(enum audio_event_type), AUDIO_MSG_THREAD_QUEUE_SIZE, 4);

static struct k_thread flare_audio_task;

/* Function Prototypes */
bool init_tas2563(const struct device *const dev);

/* Callback Prototypes */
void process_temp_reading_cb();



static void audio_task(void *dummy0, void *dummy1, void *dummy2)
{
	// Stuff to do once, when the thread initializes
	LOG_INF("Starting %s ...", __FUNCTION__);
    const struct device *const tas2563_dev = DEVICE_DT_GET(TAS2563_I2C_NODE);

    if (!device_is_ready(tas2563_dev)) {
		LOG_ERR("%s is not ready.\n", tas2563_dev->name);
		return false;
	}

    if(!init_tas2563(tas2563_dev)) {
        LOG_ERR("%s failed during I2C initialization.\n", tas2563_dev->name);
        return false;
    }
	else {
		LOG_INF("TAS2563 completed I2C initialization.\n");
	}

	while(1)
	{
		// Stuff to do every time the thread runs
		LOG_INF("In %s ...", __FUNCTION__);

		init_tas2563(tas2563_dev);

		k_msleep(5000);
	}
}

void flare_audio_task_start(void)
{
	(void)k_thread_create(&flare_audio_task, flare_audio_task_stack,
			      K_THREAD_STACK_SIZEOF(flare_audio_task_stack), audio_task,
			      NULL, NULL, NULL, FLARE_AUDIO_TASK_PRIORITY, 0,
			      K_NO_WAIT);

	k_thread_name_set(&flare_audio_task, "flare_audio_task");
}

void add_audio_event_to_queue(enum audio_event_type audio_event_to_add)
{
	while (k_msgq_put(&audio_task_msgq, &audio_event_to_add, K_NO_WAIT)) {
		LOG_WRN("The audio_task_msgq queue is full, purge old data");
		k_msgq_purge(&audio_task_msgq);
	}
}

bool init_tas2563(const struct device *const dev)
{
    LOG_INF("Running init on %s.\n", dev->name);

	uint8_t my_write_buff = 0x02;	// I2C address to read
	uint8_t my_read_buff = 0xA5;	// data from I2C read should end up here, and overwrite 0xA5 with 0x0E
	uint8_t cb_user_data = 0x00;	// this is the data that is passed to the callback - I think we probably want to copy the read_buff here to use the read value in the callback?

	struct i2c_msg * my_msgs;

	i2c_callback_t my_callback = &process_temp_reading_cb;

	int ret;

	ret = i2c_write_read_cb(dev, my_msgs, 2, TAS2563_I2C_ADDR, &my_write_buff, sizeof(my_write_buff), &my_read_buff, sizeof(my_read_buff), my_callback, &cb_user_data);

	if(ret == 0){
		LOG_INF("i2c_write_read_cb returned success");
	}
	else{
		LOG_INF("i2c_write_read_cb failed with %d", ret);
	}

    return true;
}



void process_temp_reading_cb(i2c_callback_t cb_data){
	LOG_INF("in temp_reading_cb! cb_data = %d \n", cb_data);
}




#endif /* DT_ON_BUS(TAS2563_NODE, i2c) */

Related