Reading i2c data sheets and implementing them on the Raspberry Pi

Josh Cole
5 min readAug 3, 2016

Learning the i2c protocol using a raspberry pi can actually be a challenging task. The pi typically comes fully loaded with a complete Linux distro and as a result, we become far removed from the old days of pure bit banging. The mere notion of hardware level programming is typically accessible only through operating-system level functionality that simply doesn’t exist in the fun beginner languages like nodejs or even java. In this article, I will explain one way of communicating over i2c using simple tools and demonstrate how to read a data sheet and then write a driver for the MCP23008. You can access all of my code by visiting my github repository.

My raspberry pi hooked up to many i2c components. An EEPROM chip, a magnometer, and a GPIO expander.

i2c protocol

A lot of cool integrated circuits you find these days communicate over i2c and the protocol is actually pretty nifty. Essentially you can hook up a lot of different chips to same two pins (SDA/SCL) and then transmit commands. For a chip to communicate over i2c, it has to be assigned a unique identifier from some central authority. In that way, each chip is guaranteed to have a non-conflicting address. Communicating over the protocol is essentially done by broadcasting commands that are prefixed with a known address which corresponds to some chip. All the chips just sit there, listening for commands. But they only act on commands that are addressed to them.

That’s really the protocol in a nutshell. All of this overhead is abstracted for us through operating system level functions and/or command line tools.

The command line tools

Debian Linux (and probably other distros) have a simple to use package that can be installed called i2c-tools. You can run sudo apt-get install -y i2c-tools if you’d like to install the command line stuff. There is a bit of pre-work that must be done in order to enable the raspberry pi to communicate over i2c, but that is outside the scope of this tutorial. The good folks at sparkfun have an awesome guide you can follow if you’d like to get setup.

Reading data sheets and using the i2c-tools CLI

What is a data sheet? It’s a technical document used to convey complex information about how to use whatever it is the data sheet was written for. Any i2c chip you buy online should have an accompanying data sheet available for download and in this example, let’s use the MCP23008 data sheet. That’s a chip which you can buy from http://adafruit.com and use to add an additional eight GPIO pins which can be managed solely through the i2c pins on your raspberry pi. Zero cool!

Data sheets can be daunting to look at, but just take it piece by piece. The most important bit of information we need to find first is what address the chip listens for. This data sheet is not straightforward, but never fear. If you look at page 6 you will see this:

Image from the data sheet showing how to form the “control byte” and which bits represent the slave address.

A2, A1, and A0 are pins on the chip. In my setup, they’re all wired to ground. That would give us the address of 0100000 = 0x20. Wonderful! That’s really the hardest part here. If we read the data sheet more, we can find more command addresses and what they’re used for. Another important one to jot down is the address: 0x00. That will let us set which pins are inputs and which are outputs. The last bit of information we’ll need to find is what command to send in order to control specific gpio pins.

Page 22 solves that problem for us. You can see in the bolded part GPIO — General Purpose Input/Output Registers (ADDR: 0x09). Most i2c devices will give you a specific “command” address and then break down the bits in the the packet you can send to that “command address” in order to achieve some kind of functionality. Not all data sheets are that straightforward and if you encounter a more advanced data sheet, your best bet is to read it thoroughly and try to understand how the memory addressing works.

To alter pins on the MCP23008, simply form a byte where each bit (from right to left) represents the HIGH/LOW state for the respective pin. Pretty simple, huh? Let’s take a look at the command line

Putting it all together

All this boils down to two shell commands:

i2c-set -y 1 0x20 0x00 0x00

i2c-set -y 1 0x20 0x09 0x01

The signature for this command line tool is i2c-set -y 1 [device address] [command] [value]

The -y 1 part is special to the raspberry pi B and above. It has to do with which device driver linux is using for communication over i2c. On newer raspberry pi models this will be 1. On older models it’s zero. All the other parameters are just plug and play values based on the data we mined above. Pretty cool huh?

If you take a look at my github repo you’ll see an example of how to wrap these command line tools in a simple function call. And from there, skies the limit. But what if you want to read values from registers? Never fear, it’s almost identical.

i2c-get -y 1 0x20 0x09

For the MCP23008, this will return a number which represents all the bits that are pulled HIGH. Any bit with a zero is inherently assumed to be pulled LOW.

Conclusion

I hope this helps you understand the core mechanics behind i2c-tools and the underlying i2c protocol. Up next, I’ll be writing an advanced guide on i2c communication using c++. But if you just want to play around with java or some other not-c-based language, try shelling out to the command line and going from there. Good luck!

--

--

Josh Cole

I am a self-taught software engineer, hacker, baremetal enthusiast.