Sunday, October 05, 2014

Raspberry Pi and (lack of) I2C Repeated Starts

Just spent several hours digging into a communication bug between my Raspberry Pi and a SparkFun MPR121 breakout board. I found two core problems: the MPR121 requiring Repeated Starts in its I2C communication, and the RPi's BCM2835 not implementing them.

MPR121 Requires Repeated Starts


The MPR121 uses I2C to communicate with its host. There are over a hundred registers in the MPR121 that can be read. From a functional standpoint, it would look something like:
uint8 value = read_register(uint7 addr, uint8 which_reg)
On the I2C bus, the bits/frames look something like:
| Start | addr/W | which | R-Start | addr/R | data | Stop |
The Repeated Start allows the host to hold the bus during this "write which register we want, now read it" transaction. If a normal Stop-then-Start sequence was performed, then you have a time where the bus was released, allowing another I2C master to take control. That race condition could allow the other master to change the register-to-read. The original I2C master would then get bad data.

The requirement for a Repeated Start is eminently sensible in a multi-master environment, although the MPR121 documentation does not call out this requirement. My own experimentation and a bit of Google action confirms this. Sensible, yes, but poorly documented. (Though I have to say: the MPR121 doc and application notes are overall very outstanding! Usually, I use their I2C timing diagrams rather than the formal I2C specification)

It is also important to note that most environments are single-master, so a Repeated Start wouldn't be necessary, and the requirement is a potential burden upon the I2C master (compared to a standard write/read pair of operations).

BCM2835 Lack of Repeated Starts


My test code was using the Python "smbus" module to speak I2C to the MPR121 breakout. Everything was quite straight forward, and there are several tutorials and pages on the web to show you how to set up I2C on an RPi, and to use Python to control it.

But when I tried to read the "touched" results for the 10 pads (two bytes), I kept getting the same byte values. The first byte (in register 0x00) has the first eight pads, and the second byte (at 0x01) has the next four. But that second byte was always the same, and changed right along with the first byte as I touched various pads.

After thorough digging through code, trying alternative approaches, oscilloscope review of the I2C bus, etc, it became apparent that upon receiving a Stop condition, the MPR121 would reset the which-register value to 0x00. Thus, I'd tell the MPR121 "read from 0x01. Stop. give me the byte.", and it would always return the value from register 0x00.

After further investigation and reading: the BCM2835 does not implement Repeated Starts. There is no way for it to read from arbitrary registers of the MPR121. Some people have attempted gimmicks around the BCM2835's 10-bit addressing feature, but I'll avoid that.

However, you can read all the sensor data by reading two bytes, starting at register 0x00. I2C supports a block data transfer, so this is quite straightforward. In fact, the host doesn't ever have to say "start reading from 0x00" since that is the default. It can just issue a read for two bytes.

In order for an RPi to read arbitrary registers, it would be necessary to use "bit-banging" on the GPIO pins to manually run through the I2C protocol. Code out there exists, if this feature is needed.

Summary


In my research, I found there are a number of I2C peripherals that require a Repeated Start. Presumably for transaction purposes (as noted above). Most work just fine with a Stop/Start pair, which will work in the typical single master environment.

In my home automation scenario, the host will (always?) be a PIC16F688 microcontroller running my own I2C master code. Needless to say, it will incorporate the Repeated Start capability.

Hopefully, my research will help your own use of an MPR121, or the I2C bus on a Raspberry Pi.

3 comments:

idreamincode said...

Do you know if the Raspberry Pi 2 has this same issue with the I2C Repeated Starts?

Unknown said...

Using:
Raspberry Pi 2 Model B
Python 2.7
python-smbus
i2c-tools

I was having problems with a device that required combined (repeated start) mode operation to work. After flailing around for a couple of days (getting smarter about I2C, Python, R-Pi and Linux), I found the information on this site:
http://ciaduck.blogspot.com/
Lastly, the MPL3115A2 requires a proper repeated start command in it's I2C communication. Raspberry Pi doesn't do this out of the box, but there is a kernel module that can be enabled to make it perform repeated start correctly. Run the following commands to enable repeated start on the Pi:

sudo su -
echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined
exit

More details about the repeated start problem can be found here:
http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=15840&start=25

After setting the contents of the file from “N” to “Y”, my device started working, with the scope showing that the Stop command was no longer being issued in the middle of the read_word_data(addr, cmd) instruction.

Greg said...

Thanks for the awesome research, Carl!! Very helpful, and much appreciated for sharing your tips.

I hope this helps @idreamincode, too.