One of the useful interfaces on the Raspberry Pi is the I2C bus. Originally invented by Philips in the 1970s for controlling functions inside consumer electronics, especially TVs, it’s still very handy for connecting up lowish-speed peripherals to computers. Some of the most popular are real time clocks like the MCP7940, amongst many others, and general purpose input-output chips like the venerable PCF8574. One of its convenient features is that it only involves two wires: SCL (clock) and SDA (data).
The Raspberry Pi originally exposed one I2C bus on its GPIO connector, P1. It had another I2C bus dedicated to the camera connector, S5. However, with revision 2 of the Raspberry Pi, another connector was added. This was P5, squeezed in next to P1, and it also carried the second I2C bus, making it easier to get at and use. However, for some reason the two I2C buses got swapped over between revision 1 and revision 2. And to add a further layer of complication, the camera connector and P5 are wired to different GPIO pins on the processor, even though they are both logically the same bus. I’ve tried to summarise the situation in the table below.
Revision 1 | Revision 2 | |||
GPIO0 | SDA0 | P1 pin 3 | SDA0 | S5 pin 13 |
GPIO1 | SCL0 | P1 pin 5 | SCL0 | S5 pin 14 |
GPIO2 | SDA1 | S5 pin 13 | SDA1 | P5 pin 3 |
GPIO3 | SCL1 | S5 pin 14 | SCL1 | P5 pin 4 |
GPIO28 | SDA0 | P1 pin 3 | ||
GPIO29 | SCL0 | P1 pin 5 |
Working on the CUED MDP project recently, I had a need to get the both I2C buses working on a revision 2 Raspberry Pi. There was no problem with the one on P5: typing
i2cdetect -y 1
showed the devices I had connected to it. But
i2cdetect -y 0
showed nothing at all, and examining pins 3 and 5 of P1 showed no activity. Disappointing. After a bit of digging, it seemed to me that the standard Raspberry Pi Linux kernel configures the processor to use GPIO 0 and 1 as I2C bus 0, and GPIO 2 and 3 as I2C bus 1. I wanted the bus on P1 to work, so I needed GPIO 28 and 29 to be my bus 0.
The BCM2835 processor on the Raspberry Pi, like most modern integrated processors, can have its pins programmed to do various different functions. In this case I needed to disable I2C on GPIO 0 and 1 and enable it on GPIO 28 and 29. To do this I enlisted the help of Mike McCauley’s BCM2835 library. It makes reprogramming the GPIOs fairly straightforward.
To install the library on your Raspberry Pi, make sure it’s connected to the internet, then:
wget http://www.airspayce.com/mikem/bcm2835/bcm2835-1.26.tar.gz tar xzvf bcm2835-1.26.tar.gz cd bcm2835-1.26 ./configure make sudo make check sudo make install
By the time you read this, there might be a new version of the library, so check on Mike’s site to see what you’re getting.
The C code to set up the bus looks like this:
#include <bcm2835.h> #define BCM2835_GPIO_FSEL_INPT 0 #define BCM2835_GPIO_FSEL_ALT0 4 main() { bcm2835_init(); bcm2835_gpio_fsel(0, BCM2835_GPIO_FSEL_INPT); bcm2835_gpio_fsel(1, BCM2835_GPIO_FSEL_INPT); bcm2835_gpio_fsel(28, BCM2835_GPIO_FSEL_INPT); bcm2835_gpio_fsel(29, BCM2835_GPIO_FSEL_INPT); bcm2835_gpio_fsel(28, BCM2835_GPIO_FSEL_ALT0); bcm2835_gpio_set_pud(28, BCM2835_GPIO_PUD_UP); bcm2835_gpio_fsel(29, BCM2835_GPIO_FSEL_ALT0); bcm2835_gpio_set_pud(29, BCM2835_GPIO_PUD_UP); }
It initialises the library, sets GPIO 0 and 1 as normal inputs (thus disabling their I2C function) and enables GPIO 28 and 29 as alternate function 0 (I2C bus), with pullup enabled. To build it, cut and paste the code into a file. I called it i2c0.c. Then compile it:
cc i2c0.c -o i2c0 -lbcm2835
and run it:
sudo ./i2c0
Now I2C bus 0 will be active on P1. Well, it worked for me. Once the code is compiled, of course, you can just run ‘i2c0’ after booting the Pi.
I realise that in future this will probably be made obsolete by changing the device tree sent to the Linux kernel at boot time, but for now it’s useful!
Thanks for the post, I thought I was going crazy buzzing out connections assuming i2c0 would automatically work. Following the guide worked perfectly at run-time.
Once I restarted, I found I needed to recompile with the code “cc i2c0.c -o i2c0 -lbcm2835” and then run the “i2c0” file before i2cdetect would show the new device – did you see this?
Andy
I take it all, back – changing the name to “i2c0” instead of a name I made for the binary (2i2c) worked.. (Why did I decide to name it differently? No idea..)
Thanks again!
Doesn’t work for me (try to use ver 1.26 and 1.38 of bcm2835 library ). After execution I can’t see i2c0 after command i2cdetect -l
I have also tried to use HiPi with same result. HiPi-i2c return 1 (error) for command hipi-i2c e 0 1
Any suggestions how to switch on i2c0 bus?
I have Raspberry Pi ver B.
Strange – maybe your kernel is set up differently. What kernel and Linux distribution are you running?
Pingback: Reliable I2C with a Raspberry Pi and Arduino | martinjonestechnology