I2C repeated starts implemented on the Raspberry Pi

Update: If you just want a kernel module for something close to kernel version 3.10.25, look here.

I’m working on a project that uses the Raspberry Pi and various sensors. One sensor is a MLX90614 IR thermometer; this communicates using SMBus. One of my goals is to avoid making the project specific to the target hardware, the hardware that actually runs my code. I want the code that communicates with the sensors to be Linux specific but not Raspberry Pi specific.

Communication with a MLX90614

Communication with a MLX90614

One of the problems a lot of people have had with the Raspberry Pi is with getting SMBus communication to work. SMBus is built on I2C, a way of managing serial communication between many devices with only two wires: clock and data. I2C is more flexible, while SMBus is made to be more reliable. As part of guaranteeing reliability, mostly in a multi-master setup, SMBus requires the use of I2C’s repeated starts for read operations. The I2C master driver for Linux on the Raspberry Pi does not support this, and I have been unable to find a patch or custom version anywhere that adds support. Most people have solved the problem by using code that directly interfaces with the Raspberry Pi’s hardware, ignoring the Linux kernel support altogether. This solution not only fails to meet my goal of avoiding code specific to the target hardware, it also won’t work well if more than one process on the same host attempts to use the I2c master hardware.

I decided to solve the problem by modifying the Linux kernel’s support for the I2C master of the Raspberry Pi. I succeeded. My implementation may not be the best or bug free (works for me is a weak guarantee), but it does allow for communication with a MLX90614 using the user-space (unprivileged code outside the kernel) SMBus interface provided by Linux. Since examples with SMBus are more difficult to find than I2C, here is my test code. I also tested with a BMP085 sensor using the support for it already in Linux. This sensor uses I2C rather than SMBus, and it also functions correctly. It doesn’t need a repeated start, but it gets one anyway.

To avoid causing extra trouble, I implemented repeated starts for a subset of conditions under which they could be valid. I focused on SMBus communication. With I2C, a slew of messages going back and forth can be requested, all with repeated starts, but this isn’t normally done or required.  Also, the hardware requires that once the first message has begun transmission, the second message be setup in the registers ahead of the stop condition. To make matters worse, there is no interrupt condition for this, so polling in a busy wait is required. I stuck all this inside the bcm2708_i2c_master_xfer() function of i2c-bcm2708.c so it isn’t in the interrupt handler. After the second message is configured, the interrupt handler is used to receive the data. That avoids additional polling at the cost of not allowing additional repeated starts.

If you’d rather see the kernel change in source control, I stuck it on Github. It is a 3.6.11 kernel, but not the latest revision out there. I’m still using it for now. There is no kernel or kernel module for download here because I’m running Gentoo on my Raspberry Pi and I build my own kernels. If I gave you my kernel, you may well find that something doesn’t work anymore.

About these ads

Tags: , , , , , , , , ,

9 Responses to “I2C repeated starts implemented on the Raspberry Pi”

  1. I2C Repeated Starts on Raspberry Pi @Raspberry_Pi #piday #raspberrypi « adafruit industries blog Says:

    […] Jeff Jackowski writes: […]

  2. timb Says:

    Reblogged this on timb.us and commented:
    Here’s a very concise article on adding SMBus support to the Raspberry Pi. I’ve found quite a few very nice Battery Fuel Gauge chips that absolutely refuse to work with I2C because they require repeated start commands. Jeff Jackowski has added this behavior to the i2c-bcm2708 Kernel Module.
    I just put in a couple of sample requests for SMBus devices and will be testing this myself soon, in the mean time take a look at the article and files within.

  3. Community Corner: The Secret Mechanics of Action Figures and other Delights from This Week in Adafruit’s Community « adafruit industries blog Says:

    […] Jeff Jackowski shared a project using the Adafruit Pi Cobbler: “I’m working on a project that uses the Raspberry Pi and various sensors. One sensor is a MLX90614 IR thermometer; this communicates using SMBus. One of my goals is to avoid making the project specific to the target hardware, the hardware that actually runs my code. I want the code that communicates with the sensors to be Linux specific but not Raspberry Pi specific.” (read more) […]

  4. SMBus Support on the Raspberry Pi | timb.us Says:

    […] I2C repeated starts implemented on the Raspberry Pi […]

  5. Replacement I2C kernel module for Raspbian | Jovian Outpost Says:

    […] while ago, I got I2C repeated starts and SMBus support to work from the Linux kernel on a Raspberry Pi. I recently built a new 3.10.25 kernel from the foundation’s kernel fork on Github and […]

  6. Thomas D Says:

    Hey Jeff,

    after i failed to code my own STABLE ReadWord-function for the SMBus-protocol ( I assume its a timing problem between the Acknowledge-Bit and the second Byte from the Slave) i used your kernel-modul with the wiringPI-library from Gordon. It works really stable, perfect for my requirements! I just want to say thank you, cause it was a really pain in the ass.
    Contact me pls per mail, for a donation ;)

    best regards!

    • jjackowski Says:

      Glad to hear to works for you! If you want to make another attempt at SMBus yourself, you may get some help from the kernel’s i2c-gpio source code.

      As for stability, the Raspberry Pi’s I2C master has a race condition with how the hardware handles repeated starts (the polling I mentioned half-way through the second to last paragraph). With the test program I made for the MLX90614, I get two consecutive communication errors (they are always in pairs) about once every other day when reading once per second; I’m pretty sure it is the race condition. If you are using a device that can use PEC (Packet Error Code), you should enable it. I think all SMBus devices have to support PEC, but it is optional for I2C. That should minimize any trouble.

  7. Bart Van Thielen Says:

    tried it on the MLX90614 using python and worked immediately!
    Thanks!

    • jjackowski Says:

      Is there any chance I could get you to try out my test program for the MLX90614 if you haven’t already? Knowing that the very same test, as well as a different test like your Python based one, will give me some greater confidence in the kernel modifications I made. The test program is here:

      http://home.hiwaay.net/~jeffj1/MLX90614.c

      It is C code, and has a comment at the top with the command to build.

      Thanks, and have fun!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

Join 50 other followers

%d bloggers like this: