Tag Archives: i2c

I2C on the Beaglebone

I got I2C working well a couple of weeks back and got both the modules I am using working well with the beaglebone. I later modified the code to allow API calls to read the values from the two modules. Now I have a proper working directory for the complete project and I’ve decided to test out other things separately and then import them into this single working directory.

This post is to describe how to get the I2C working on the Beaglebone. I got some help from https://github.com/quick2wire/beaglebone. The only problem was that I planned on using C/C++, not python, for better support with OpenCV. I’ll explain the protocol along with the code together. This post assumes you know a bit of electronics. Make sure you don’t connect any 5V supply to any of the beaglebone pins and don’t draw too much power. This means you should only use modules which are working on 3.3V logic and not on 5V logic! If they are 5V logic, you’ll need to some some sort of voltage divider or so. I will not delve into this on this post. If you’re doubtful about something, please feel free to ask me because a wrong connection could be the death of your board (As I’ve read on some other posts).

What needs to be is this:

Include these header files:

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

or the corresponding cpp header files. The i2c-dev.h gives you access to the i2c protocols.

What you need to understand about I2C is that it is a bus. You can connect multiple devices to the same two wires on the Beaglebone. There are three such buses on the Beaglebone and the third is the most accessible. I2C has just two pins required, the serial data and the serial clock. Bus 3, from the datasheet, is located on P9 at pins 19 and 20.

Pin 19 – I2C – 3, SCL (Serial Clock)
Pin 20 – I2C – 3, SDA (Serial Data)

Since we are working with Linux, we don’t need to deal with the low level protocol. We directly connect these two pins with the corresponding pins on the module. Of course, you’ll need to power up the module. In my case, it need a five volt power supply and 3.3 logic voltage. DO NOT POWER THE MODULE USING THE BEAGLEBONE! Some ports may be able to do so but I’m not sure exactly which ones and even then, the power limits are fairly low so I suggest using a separate power supply. (I used an arduino’s 5V supply). The logic supply can be obtained from pins 3 and 4 on P9 on the beaglebone. Make sure you common the ground with the beaglebone’s GND pins at pins 1 and 2 on P9.

After connecting your module and powering it on, try this command in the bash terminal:

i2cdetect -y -r 3

i2cdetect is a part of the package i2ctools. This command will read all the devices on your I2C bus 3 and show you the addresses. My output looks like this:

I’ll post my output the next time I hook it up

Note the addresses of you devices and check with the datasheet to see if they match. Each I2C bus has a maximum of 128 address (And thus 128 devices can be connected to it). Most devices have a unique unchangeable address. Some have a register which can change one or two bits in the address so that you can connect two or more of the same modules. You can directly set the registers with i2cset and read the registers using i2cget from the command line. The syntax is follows:

i2cset <i2cbus> <chip address> <register address> <value>
e.g. i2cset 3 0x19 0x07 0x97

In my example, I write to i2cbus 3. The device/chip hardware address is 0x19. I choose to write the value 0x97 to the register at 0x07 on the chip.

i2cget <i2cbus> <chip address> <register address>
e.g. i2cget 3 0x19 0x07

I now read the value I just store on the device at 0x19 in the register 0x07. Simple enough. So now you’re asking how to do this from a C code, right? Well, I hope so 😉

Basically, to write, you need to specify four things, the bus, the chip address, the register address and the value. To read, you specify the same things except the value.

To do the first, you open the file “/dev/i2c-3” as O_RDWR with the following command:

int g_i2cFile;
g_i2cFile = open(“dev/i2c-3”, O_RDWR);

This opens the i2cbus 3. To handle possible errors, do something like this:

int g_i2cFile;
if((g_i2cFile = open(“dev/i2c-3”, O_RDWR)) < 0){
// ERROR HANDLING
perror(“Failed to open the i2c bus”);
return -1;
}

g_i2cFile is the i2c file handle.
You then need to set the device address on the bus. You can do that like so:

int address = 0x19;
if (ioctl(g_i2cFile, I2C_SLAVE, address) < 0) {
perror(“i2cSetAddress”);
exit(1);
}

I2C_SLAVE is a I2C variable defined in the i2c-dev.h header. address is the address of the device which you found in i2cdetect or from your datasheet. You need to do this each time you want to change the device. I made this into a function with an argument for the address.

Now you just need to read or write the data. I made functions for each. Let’s analyze the commands. This is for write:

unsigned char I2C_WR_Buf[MAX_BUFFER_SIZE];
I2C_WR_Buf[0] = Reg_ADDR;
I2C_WR_Buf[1] = Data;
if(write(g_i2cFile, I2C_WR_Buf,2) != 2) {
perror(“Write Error”);
}

n is the number of bytes you want to send. The first byte should be the register address and in this case, the second byte is the data to be written. Multiple bytes can be sent if the register is to hold multiple bytes but I haven’t yet seen such a case so I am not too sure about that. To read is just slightly more complex. You first write out the register address you want to read from and then accept incoming data.

unsigned char I2C_WR_Buf[MAX_BUFFER_SIZE];
unsigned char I2C_RD_Buf[MAX_BUFFER_SIZE];
I2C_WR_Buf[0] = Reg_ADDR;
i2cSetAddress(DEVICE_ADDR);
if(write(g_i2cFile, I2C_WR_Buf, 1) != 1) {
perror(“Write Error”);
}
i2cSetAddress(DEVICE_ADDR);                              // This address might not need to be set again but this works for me
if(read(g_i2cFile, I2C_RD_Buf, n) !=n){
perror(“Read Error”);
}

Change n to the number of bytes you expect to read. Generally it is one but some devices allow you to send back multiple bytes by oring the register address with 0x80 (In my case, at least… Check your datasheet for this). The output will be at I2C_RD_Buf. And you’re done. Except one thing. Make sure you close the i2c handle like so:

close(g_i2cFile);

I’ve uploaded the class file and the header file for the i2c protocol on the beaglebone at https://github.com/ajaykumarkannan/Beaglebone-I2C

You can use it. You need to compile i2c.cpp with -c flag, include the i2c.h file in you main and then compile. A makefile would be the simplest to do this. I’ve included an example. It is just arbitrary so do modify it for your purposes.