I2C management

Hello I am developing a system in which multiple tasks of different priorities communicate over an I2C bus. FreeRTOS is running on the master device. I would like to know if there is a standard for the management of the bus. I currently have a single “I2C manager” task that application tasks send messages to on a queue. Without going into too many specifics, the application tasks receive data they are requesting over I2C by passing references to buffers with the messages. There is a bunch of flag-setting and memory-protecting that goes with this method. While this method has worked well so far I feel it is overly complex and also doesn’t allow messages with different priorities (this could be added along with more complexity). The other option I know of is to just have global I2C read/write functions that are protected with semaphores and are called by tasks. This inherently gives a message the priority of its task but presents the possibility of priority inversion slowing everything down. Are one of these methods generally preferred or am I missing something?

I2C management

Both ways are valid. Personally I just go for the simplest, which is to have global read/write functions with a semaphore. I like simple. It might be that for your system it is also the most efficient, but that depends on the number of tasks accessing the port and the number of bytes being sent.

I2C management

I personally tend to have a set of global driver functions that use a (recursive) mutex to control access. If an operation needs to be atomic for longer than what is provided by the driver (from just before sending the start bit until after the stop bit), then the task needs to explicitly use the mutex. I personally tend to have a set of global driver functions that use a (recursive) mutex to control access. If an operation needs to be atomic for longer than what is provided by the driver (from just before sending the start bit until after the stop bit), then the task needs to explicitly use the mutex. Note that Free RTOS WILL handle the case of a low priority task having the I2C bus, when a higher priority task wants it, as part of the basic Mutex code (you need to use a Mutex, not a semaphore here for that to work). Note, if another master can send a message to us, I would have a task sitting waiting for such a message and handling/distributing it as needed. The one case where I would use a separate task for sending i2c messages would be if I needed to make sure that high priority tasks can’t starve (or at least make it harder to starve) the low priority task. Note that Free RTOS WILL handle the case of a low priority task having the I2C bus, when a higher priority task wants it, as part of the basic Mutex code (you need to use a Mutex, not a semaphore here for that to work). Note, if another master can send a message to us, I would have a task sitting waiting for such a message and handling/distributing it as needed. The one case where I would use a separate task for sending i2c messages would be if I needed to make sure that high priority tasks can’t starve (or at least make it harder to starve) the low priority task.

I2C management

Ok cool thanks this helped. Im still a bit worried about priority inversion but I might experiment with priority inheritance a bit to put my mind at ease.

I2C management

A simple illustration of Prioriy Inheritance would be: Low priority task is running and gets control of the I2C device. Middle priority task starts up, blocking out the low priority task. High priority task starts, which wants the I2C device. When it blocks on the Mutex, the mutex code will note that the Low Priority task has the mutex, and temporarily raise the task the the priority level of the High Priority task, this will keep the middle priority task from blocking it from running. When the low priority task finishes with the device and release the Mutex, it will go back down to its old low priority, and the high priority task will get the device and run. Note that the low and high priority tasks do need to be written with the fact they are sharing a resource. The low priority task needs to avoid doing extra stuff while it has the Mutex, so that it can give it back as quick as possible, not not do an operation that takes too long for the high priority task to wait to be completed, and the High proirity task might need to defend against excesive latency (if the low priority task might do too long of a cycle) and perhaps skip the operation if needed.