[2024-feb-29] Sad news: Eric Layton aka Nocturnal Slacker aka vtel57 passed away on Feb 26th, shortly after hospitalization. He was one of our Wiki's most prominent admins. He will be missed.

Welcome to the Slackware Documentation Project

This is an old revision of the document!


Interfacing I2C Devices To Your System

Inter-Integrated Circuit (I²C or more often written as I2C) is a multimaster serial single-ended computer bus invented by the Philips semiconductor division (see the wikipedia article for more information on I2C) and commonly used in many modern electronic devices including PC.

I²C uses only two bidirectional open-drain lines, Serial Data Line (SDA) and Serial Clock Line (SCL), pulled to logic level 1 by pullup resistors. Typical voltages used are +5 V or +3.3 V although systems with other voltages are permitted and are often encountered. This has an interesting implication: logic level 1 is achieved by doing nothing whilst logic level 0 needs to be pulled down to ground. Although there are bidirectional I2C voltage level shifters (like the PCA9306) it may be feasible to experiment simpler solutions if on the I2C bus you're not going to have many devices connected. See more on this in the “Voltage Level Shifting” chapter.

Preface

Most modern PC have several internal components that communicate vital information, like internal temperatures of critical components, over I2C bus. This type of stuff is factory wired into your PC and can be dealt with lm_senosts … what we want to do here is use an I2C bus on your computer to connect some external I2C sensor like an accelerometer. I'm tagging this in the ARM hardware section because I think that, excluding the lm-sensors stuff, most people will be doing this sort of thing on embedded ARM systems … but the concepts are applicable to any linux capable system with an I2C bus.

Preparing Your Host System

Before you start you might want to make sure that the OS has all that's required to manage the I2C bus you will be using. The first thing to do is make sure you have the correct kernel driver for whatever physical layer implementation is on your system. You will have to research on the datasheets of your system's hardware … my Pi has bcm2708 so in my case it was a matter of loading the bcm2708_i2c module. It might also be necessary to load i2c-dev module too depending on your setup.

Once you have the drivers right you might like to have a user-space tool to help you detect buses, present devices and communicate with the I2C devices each bus. I use i2ctools for this but it's not packaged amongst the Slackware packages and I was unable to find ant third party providing an ARM Slackware package so I downloaded the sources and compiled it for myself. Sources can be gotten from here: I2CTools.

Connecting A Device on the I2C Bus

Provided you've sorted out the voltage level issues (see the “Voltage Level Shifting” chapter) adding a new device in the bus is really simple. The bus is multimaster meaning that you can have many devices (upto 101) on the same bus so all you have to do is make 4 connections: Power, Ground, SDA and SCL. If it's the first device you're connecting on the bus it may be necessary to install the pullups between Power-SDA and Power-SCL. It's as simple as that and if the system is ready with the appropriate drivers and user-land utilities you are ready to access the newly connected device.

Detecting Connected Devices

There are probably many ways to determine what's connected to an I2C bus, I chose to use stuff out of the i2ctools project. I was unable to find a Slackware ARM package for i2ctools so I compiled and installed it on my system.

First thing you want to know is what I2C busses are present on your system as there may be more then one and looking in the wrong bus may be frustrating:

root@pi:~# i2cdetect  -l
i2c-0   i2c             bcm2708_i2c.0                           I2C adapter
i2c-1   i2c             bcm2708_i2c.1                           I2C adapter
root@pi:~#

If you're not sure which bus you connected your stuff on you might want to do this on on all the busses:

root@pi:~# i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 1e -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- 69 -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- 77                         
root@pi:~#

Communicating With An I2C Device

Communication with I2C devices id done by reading and writing to it's registers. Each device has it's own register list and is something you need to look for in the device's datasheet. Some devices even need some preliminary calibration to be done before you can read any sensible data out of them so before you start using i2cget to read out some registers you should at least have an idea of what registers you are interested in and fave figured out if your device needs calibration prior to reading any sensible data. Registers can be set by using i2cset but do this only if you have read the datasheet.

Even once you know if your device needs calibration and the registers involved, the content of the registers may not be in a convenient format for ready use. There are generally various scripts in perl or python that address calibrating and managing data from specific I2C devices so that human readable information can be produced.

At this point it's impossible to show any further info about I1C communication without going into some detail about a specific device so I'll pick one ot the devices on my IMU pcb. While detecting previously on my PI I found 4 devices with the flowing hex addresses: 1e,40,69 and 77. Generally each I2C device has a range of addresses than can set. We shall concentrate on the device with 0x69 address. By doing some cross reference search on the IMU pcb datasheet and on the single datasheets of each device present on my IMU it's revealed that 0x69 should be the address of the ITG3200 (gyro + temp sensor) and indeed the ITG3200 asserts that it can have either 0x68 or 0x69 address selectable by logic level on pin 9.

Voltage Level Shifting

You may end up with heterogeneous voltage level devices and if you have many devices the correct way to work around this problem is by using bidirectional I2C voltage level shifters like the PCA9306, but if you only have a few devices all grouped up in a neat PCB like the 10DOF IMU unit you might want to give a simpler system a try. This is how I connected my 5v IMU pcb to a 3.3v I2C bus on my RaspberryPI: I took as educated guess that 4.4v (5v with a 4148 diode in series) would still be a tolerable power supply voltage for the whole IMU pcb, this would most likely allow all the I2C devices on the IMU pcb to recognize a minimum of 3.08v (4.4 * 0.7) as the lowest reliable logic level 1 tension allowing it to inter-operate with the PI's 3.3v logig levels. I was not able to find if the PI has internal pullups on the I2C bus or if they have to be externally placed so in doubt I put in 10k pullups between the 4.4v power line and the 2 data lines. I was the able to correctly detect the sensors on the IMU pcb.

Sources

 howtos:hardware:arm:interfacing_i2c_devices ()