Experiment: Queued Approach to I2C Device Driver

A Queued I2C Driver?

There has been a large push at the PC & Server level lately for breaking software into small, container-based independent parcels. But container infrastructure for embedded computing is still in its infancy. So how can we improve software design methods for embedded computing using the tools we have available?

I've always liked the simplicity and separation provided by message queues. One of the key benefits, to me, is the inherent synchronization - basing a software design around queues relieves a lot of concurrency problems and typically single-threads your access to critical resources.

So I'm considering the idea of basing an I2C interface around a queue mechanism. An I2C part needing a software driver, say a real-time-clock, would implement the driver to do a register read by sending a queue message and waiting for a response. The firmware would initialize command and response queues, and start an I2C operations thread.

Pros and Cons

Before we discuss how this would be helpful, and how it would be implemented, let's look at the drawbacks.

  • We are adding latency to all I2C operations, and
  • We are adding a layer of complication to the firmware. 

In particular, my concern is how much latency will we be adding, and how much is acceptable? In most of my projects recently, the I2C has been a secondary bus for reading temperature and synchronizing the clock, and adding even a hundred milliseconds of latency would not be a major issue. But if you are using I2C to read an accelerometer at 500 Hz, even an additional 1 ms is probably too much. But a key point is that we need to have an accurate measure of the latency we are adding if we want to try this approach.

On the positive side we should see some benefits:

  • Once the initial I2C driver is debugged, it can be easily re-used,
  • Porting the higher-level drivers to different MCUs is just a matter of creating a new low-level I2C driver,
  • The queue approach prevents bus contention,
  • We could abstract the higher-level driver for unit testing without hardware present, and
  • If we need to secure the bus across multiple transactions, like if we are using an I2C mux, we can add a lock/unlock function easily, and
  • We can implement common device drivers in libraries without including a lot of messy HAL include files.

Measurement

I see two features that we could pretty easily measure while developing the firmware to support this. First, we could look at the true latency - the time from when we call an API function to perform a register read until the start of traffic on the I2C bus. We could add a GPIO write at the start of the API function, and use an oscilloscope to trigger on the GPIO and monitor the I2C bus.

The second option is simpler and doesn't require the scope - we just measure the round-trip time of the API call, and compare with the round-trip of the direct I2C transfer call. This should show the latency of both the command queue and the response queue, and the actual transmit/receive time on the I2C bus will be subtracted out.

Next Steps

To continue, we need to:
  • Write the low-level driver,
  • Write the higher level driver,
  • Write the measurement code, and
  • Tie it all together in a firmware project.

Comments

Popular posts from this blog

FFT Analysis using CMSIS DSP Library

Basic Scan-Mode A/D DMA (STM32H743)

Double-Buffered DMA from ADC