Raspberry Pi I2C

With the Buster version

With the Buster version, as of june 2019, the necessary details for using i2c_smbus_write_byte_data() and siblings, require the following include statements:

#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <i2c/smbus.h>

When linking, the -li2c library reference has to be added, as here is where these functions exist.

Before Buster

To use the i2c_smbus_write_byte_data() and the other similar smbus functions, which might be macros and not functions, in at least some installations, these are defined in linux/i2c-dev.h Here is also the ioctl constant for setting slave address.

Typical includes are thus:

#include <unistd.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>

Configuring i2c in the system

In recent systems, the raspi-config program has menu entries for configuring i2c active on startup. Earlier systems had to have some module code turned off and on.

Prior to February 2015, in order to get the i2c working, we need to have these guys added into /etc/modules:

i2c-dev
i2c-bcm2708

There is also the matter of un-blacklisting the i2c-bcm2708 in the /etc/modprobe.d/raspi-blacklist.conf file. This file is relevant for SPI as well, when the spi-bcm2708 needs to be turned on. Then some of the recent kernels have added as a default some sound-device modules that reserve addresses such as 0x1b or 0x3b, but unless these physical devices are present, these addresses are not being used.

To free them, blacklist the following modules:

0x1bsnd_soc_tas5713
0x3bsnd_soc_wm8804

To see what these might be, look at locations such as /sys/bus/i2c/devices/1-001b/name or /sys/bus/i2c/devices/1-003b/name as appropriate, and find the culprits there.

So in summary, the /etc/modprobe.d/raspi-blacklist.conf file might contain something along the lines of:

# blacklist spi-bcm2708
# blacklist i2c-bcm2708
blacklist snd_soc_wm8804
blacklist snd_soc_tas5713

Raspbian 2015-02-24 and later doesn't use this blacklist file anymore. There are also some changes in how the device is set-up, it is now specified in /boot/config.txt as

dtparam=i2c_arm=on
dtparam=i2c_vc=on

The i2c_arm is the bus number 1, the bus number 0 can be turned on with the i2c_vc statement.

There should also be the entry i2c_dev in /etc/modules so the devices appear. The raspi-config program turns all of this on for the standard i2c buss.

Notice that the i2c-0 buss is reserved, and it is used for ID eeproms on compliant HATs, as well as with the camera and display, if these are included. So it may not be all that straightforward to use this.

There can also be entries present in /boot/config.txt that allows us to use a third i2c-buss, using the bitbanging and very portable, i2c-gpio, on any two pins, for example:

dtoverlay=bus=3,i2c-gpio,i2c_gpio_sda=27,i2c_gpio_scl=17

Defaults for SDA is GPIO23/P16 and for SCL is GPIO24/P18

Also note that you will need to provide pull-up resistors for these alternate pins.

The i2c-bcm2708 and i2c-dev modules can be added initially with modprobe commands as well, but they won't remain past the next reboot.

To get the development and user-space tools for manipulating the busses, there are

apt-get install i2c-tools
apt-get install libi2c-dev 

The 3.3V i2c bus is on P1 pins 1,3,5,6 as follows:

PinFunction
1 3.3V
3 SDA
5 SCL
6 Gnd

It should be an easy matter to connect this to something simple and easy-going as a DS1621, and the see stuff happening:

RP.P1 function DS1621 pin #
P1 3.3V 8
P3 SDA 1
P5 SCL 2
P6 GND 4,5,6,7 (for it to go in on address 0x48)

Then there's the possibility of level-shifting to 5 V with the usual NMOS transistors and pulling 5V from RP.P1 pins 2 or 4, as I did with the Picotuxes or the WD-book. When using the i2c bus on the Raspberry Pi's pins P1.3 and P1.5, there are internal pull-up resistors, so there is no need for additional ones. I still need some on the drains of the FETS, on the 5V side of the level translator (BSN245 NMOS FETs, with G to 3.3V power (P1.1), S to the 3.3V i2c bus side (P1.3/P1.5) and D to the 5V i2c bus side; one transistor each for SDA and SCL)

The direct connection shown above worked with a DS1621 at first try! Once connected, run i2cdetect -y 1 and there it appears at address 0x48

PCF8574

This chip also works as defined right away, with simple calls to i2c_smbus_write_byte() and i2c_smbus_read_byte(). I made a test board long ago, with a DS1621, PCF8574, a number of LEDs on the outputs of the DS1621 and all the bits of the PCF8574, and a 24LC16.

MCP23017

Drop-in easy just like the PCF8574.

PIC16F690

The PIC16F690 can be programmed to run as an I2C slave device. I have had much success with doing this when combining with Picotux, but my old code didn't seem to work well with the R-pi. I could read to some extent, at least single bytes (i2c_smbus_read_byte()) but writing doesn't seem to work. Closer investigations reveals that the start bit is not being flagged, possibly because the device isn't reacting fast enough. To investigate this further I will try hooking the PIC16F690 up to an Arduino and see how that works.

On further investigations, changing the PIC16F690 to run at its maximum of 8 MHz, and ignoring the absence of the start bit when checking, I did get some rather more useful results even with the standard i2c-1 buss on the Raspberry Pi. And even if I hadn't, the bit-banging interface discussed below does work well enough.

PIC12F1822

This one also talks i2c, and the start-bit is handled the way it should be. I made the code along the same lines as the one for the 16F690. For many standard uses, I have made and used this code for the 12F1822 and the matching code for the Raspberry Pi.

Bit-banging i2c mode

Apparently, more i2c busses can be created using the bit-banging protocol, which is what is used in the Picotux and WD books, and which works reliably with the PIC16F690 as well, required until I figured out how to make the PIC16F690 work right with the standard i2c buses.

As of 2015 the kernel already has the CONFIG_I2C_GPIO=m setting, indicating that this is turned on as a module, so there is no need to change the configuration for this anymore.

In fact, it is now quite straightforward to add more i2c-busses on any pair of GPIOs not otherwise allocated, via entries in /boot/config.txt. As an example, the following will create three busses, number 3, 4, and 5, thus /dev/i2c-3, /dev/i2c-4, /dev/i2c-5, with SDA on various GPIOs:

dtoverlay=i2c-gpio,bus=3,i2c_gpio_delay_us=1,i2c_gpio_sda=17,i2c_gpio_scl=22
dtoverlay=i2c-gpio,bus=4,i2c_gpio_delay_us=1,i2c_gpio_sda=23,i2c_gpio_scl=24
dtoverlay=i2c-gpio,bus=5,i2c_gpio_delay_us=1,i2c_gpio_sda=5,i2c_gpio_scl=6

The defaults are SDA on GPIO 23 (P16) SCL on GPIO24 (P18)

The new device will appear after next reboot.

Looking at /sys/kernel/debug/gpio after reboot with the above in /boot/config.txt, shows:

gpiochip0: GPIOs 0-53, parent: platform/3f200000.gpio, pinctrl-bcm2835:
 gpio-5   (                    |sda                 ) in  hi    
 gpio-6   (                    |scl                 ) in  hi    
 gpio-17  (                    |sda                 ) in  lo    
 gpio-22  (                    |scl                 ) in  lo    
 gpio-23  (                    |sda                 ) in  lo    
 gpio-24  (                    |scl                 ) in  lo    

And i2cdetect -l tells us:

i2c-3	i2c       	i2c@5                           	I2C adapter
i2c-1	i2c       	bcm2835 I2C adapter             	I2C adapter
i2c-4	i2c       	i2c@4                           	I2C adapter
i2c-5	i2c       	i2c@3                           	I2C adapter

ls -l /dev/i2c* shows:

crw-rw---- 1 root i2c 89, 1 2018-12-25 17:26 /dev/i2c-1
crw-rw---- 1 root i2c 89, 3 2018-12-25 17:26 /dev/i2c-3
crw-rw---- 1 root i2c 89, 4 2018-12-25 17:26 /dev/i2c-4
crw-rw---- 1 root i2c 89, 5 2018-12-25 17:26 /dev/i2c-5

Older notes

As it turns out, others have been working on this, and Kadamski had made a kernel-module, called i2c-gpio-param in order to make it easy to manipulate several simultaneous bit-banging i2c interfaces. These themselves are in the standard module i2c-gpio.ko, which needs to be included in the kernel. So the first thing to do is to recompile the kernel, similar to what I've done before when wanting to make my own modules, then run make menuconfig to turn i2c-gpio on. I set it to be a module, (CONFIG_I2C_GPIO=m) then went through the standard compilation steps

Some notes on this: having made the kernel, there is the zImage file and the Image files. The page on compilation refers to the file Image, but it seems to be the zImage that is to be copied into /boot as the kernel.img.

I named this file kernel_new.img and referenced it in /boot/config.txt as the kernel=kernel_new.img, so that the old, working, kernel image and its associated modules (which live under /lib/modules/3.6.11+, while the new ones came up as being just plain 3.6.11 without the plus sign) can be recovered.

Then I compiled the i2c-gpio-param.ko, and loaded it with:

insmod i2c-gpio-param.ko busid=2 sda=27 scl=17

since I had hooked my SCL line to GPIO17, pin 11 on the header, and SDA to GPIO27, pin 13 on the header. (Incidentially the same allocations of pins 11 and 13 is used on the PIC16F690.) For the first test, I have connected a DS1621 to these two GPIO lines.

And lo and behold, i2cdetect now tells me:

root@stn81:/home/pi/xfer# i2cdetect -l
i2c-0   i2c             bcm2708_i2c.0                           I2C adapter
i2c-1   i2c             bcm2708_i2c.1                           I2C adapter
i2c-2   i2c             i2c-gpio2                               I2C adapter
root@stn81:/home/pi/xfer# i2cdetect -y 2
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
root@stn81:/home/pi/xfer#

And there is the DS1621 showing up at address 0x48 just where it is expected to be! Attempting to communicate with it is also successful; it tells me that the ambient temperature is 23.75 degrees Celsius.

And indeed, communication with a PIC16F690 has also become reliable.

To make this load on startup, I put the i2c-gpio-param.ko file into /lib/modules/3.6.11/kernel/drivers/i2c, then ran depmod, then it can be inserted using

modprobe i2c-gpio-param busid=2 scl=17 sda=27

in /etc/rc.local


Valid XHTML 1.0 Transitional