Wednesday, August 15, 2012

Raspberry pi and i2c gpios

Intro

As Pi arrived I started playing with it.  I know I have plenty of GPIO pins on it's connector, however I was interested in using either SPI or I2C interface to connect "something". I quickly hacked old, lying around, board with pcf8574 i2c expander (connected to 8 LEDs) and connected it to Pi I2C lines (and ground and +3.3V). And the fun started... I had no previous experience in I2C and GPIOs on linux. 

Making I2C work

First thing I've learned is that kernel needs to be replaced, as wheezy one does not support I2C. I used Chris Boot's one: http://www.bootc.net/projects/raspberry-pi-kernel/
(procedure is there requires firmware update, is rather easy and automated).
After booting to new kernel I expected to have /dev/i2c-0 and /dev/i2c-1 devices (the BCM chip raspberry bases upon has two I2C interfaces). Well... no, devices were not created, creating them by hand (mknod /dev/i2c-0 c 89 0) did not help much - which means there is no driver in kernel is to service those. After digging and reading parts of kernel source I finally knew what is going on.
to have i2c devices visible in userspace (/dev/*) you need the following:

  • hardware I2C module (i2c_bcm2708, this exposes hardware in a standard way to kernel internals)
  • i2c-dev module (exposig /dev/i2c-x entries)

So after doing:
modprobe i2c_bcm2708
modprobe i2c-dev
I had desired device entries in /dev/

Using I2C directly

As I already had pcf8574 connected I wanted to try it, the key to success was i2c-tools package that contains (among others) i2cdetect and i2cset programs.
As I was too lazy to check what address is set on pcf and what datasheet says about it's bus address so I run i2cdetect (warning - this tool reads (and writes?) whole i2c bus which can harm some hardware (older thinkpads for example...), see man) as follows:

root@raspberrypi:~# i2cdetect -a 0
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-0.
I will probe address range 0x00-0x7f.
Continue? [Y/n] y
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- 27 -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
It was clearly visible that my pcf8574 had address of 0x27. From now on I was able to write it like that:
i2cset -f -y 0 0x27 1 <value>
So to have blinking LEDs I used the following one-liner:
while : ; do i2cset -f -y 0 0x27 1 0xaa && sleep 0.3 &&  i2cset -f -y 0 0x27 1 0x55 && sleep 0.3; done
(as you can see raspberry wheezy has sleep with fraction support!)

Dedicated module

So far so good, but this is still direct I2C data banging with i2cset (which is sort of acceptable), but we have a nice module gpio_pcf857x for pcf8574 expanders - as they are quite popular. 
Loading the module directly did not work as expected - you need to provide bus address of your pcf8574 to the module, easiest way I found is:
echo pcf8574 0x27 > /sys/class/i2c-adapter/i2c-0/new_device
which tells I2C kernel subsystem that there is a pfc8574 @ 0x27. This causes auto-loading of gpio_pfc857x module into kernel. 

/sys/class/gpio

Loading this module also creates entry in /sys/class/gpio:
root@raspberrypi:/sys/class/gpio# ls -l
total 0
--w------- 1 root root 4096 Aug 15 18:19 export
lrwxrwxrwx 1 root root    0 Aug 15 17:54 gpiochip0 -> ../../devices/virtual/gpio/gpiochip0
lrwxrwxrwx 1 root root    0 Aug 15 18:09 gpiochip248 -> ../../devices/platform/bcm2708_i2c.0/i2c-0/0-0027/gpio/gpiochip248
--w------- 1 root root 4096 Aug 15 18:01 unexport
It's clear that gpiochip248 is our expander, the 248 is the base (also can be read from base file from within gpiochip248 directory. 
And that's pretty it - now we can make specific gpios visible in /sys/class/gpio, to do this use export and unexport entries. 8 gpios on pcf have numbers: 248...255 (base+8 to base+7)
to export a gpio do:
echo 248 > /sys/class/gpio/export
this causes :
lrwxrwxrwx 1 root root    0 Aug 15 18:18 gpio248 -> ../../devices/platform/bcm2708_i2c.0/i2c-0/0-0027/gpio/gpio248
to appear
inside we find:
root@raspberrypi:/sys/class/gpio/gpio248# ls -l
total 0
-rw-r--r-- 1 root root 4096 Aug 15 19:21 active_low
lrwxrwxrwx 1 root root    0 Aug 15 19:21 device -> ../../../0-0027
-rw-r--r-- 1 root root 4096 Aug 15 18:19 direction
-rw-r--r-- 1 root root 4096 Aug 15 19:21 edge
drwxr-xr-x 2 root root    0 Aug 15 19:21 power
lrwxrwxrwx 1 root root    0 Aug 15 18:18 subsystem -> ../../../../../../../class/gpio
-rw-r--r-- 1 root root 4096 Aug 15 18:18 uevent
-rw-r--r-- 1 root root 4096 Aug 15 18:19 value
in this very simple example (pcf8574 is quite simple) we are interested in direction and value files. Direction controls, well, direction of a pin, and value file is used to read or write value (to write gpio needs to be an output...). So to write a 1 to pin do the following:
echo "out" > /sys/class/gpio/gpio248/direction
echo 1 > /sys/class/gpio/gpio248/value
then to set it to 0 do:
echo 0 > /sys/class/gpio/gpio248/value

More info on gpio on linux can be found here: http://www.kernel.org/doc/Documentation/gpio.txt

No comments:

Post a Comment