Tag Archives: linux

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.

Advertisements

Angstrom and Linux

This post is regarding some things I picked up while playing around with the Beaglebone but is not documented so clearly or easily in the Beaglebone setup. And I suggest using linux as you don’t need any drivers at all. And as I haven’t even tried playing with it in Windows, I won’t be covering it here but I believe you can use telnet or a basic serial terminal like putty.

As soon as you plug the brand-new beaglebone into the USB, it powers on and it will mount a partition on your computer as a mass storage device. This contains some pdf documentation including the datasheet and other files. It also has the documentation for the cloud9 IDE. It is a built-in IDE for the Beaglebone based on Javascript which aims to provide an interface to the Beaglebone similar to arduino. This is not yet complete. It is easy to do simple GPIO and LED blinking but in my opinion, I would much rather use the more powerful Linux backend since it’s already there and Cloud9 is merely running on the Linux backend. In any case, if you want to use Cloud9, copy the documentation to your computer and eject the mass storage device. It will then automatically configure a network interface between your PC and your board through USB and you can access cloud9 through any web browser. Just follow the documentation. The network that is automatically setup maps my computer to 192.168.7.1 and the Bone to 192.168.7.2

Note that this connection is in addition to your WiFi/Ethernet connection. I believe that it may be possible to use this interface to connect the bone to the internet through USB by using NAT but I have not yet accomplished this.

Since you now have a network interface to the Bone, that means you can ssh into it or use a serial terminal… Or both. The serial terminal is more fundamental as it shows all the boot up and shutdown process while the ssh of course doesn’t.

Before starting, I recommend that you add your user account to the linux group dialout by doing:

sudo adduser <username> dialout

or

su -c ‘adduser <username> dialout’

where <username> is replaced with your username

Log out and log back in. This allows you to run the serial terminal without having root access.

Now make sure you have screen installed by doing:

sudo apt-get install screen

or

sudo yum install screen

You can use minicom or putty but I started using screen first so it has been a force of habit. Generally, the beaglebone maps to /dev/ttyUSB0 and /dev/ttyUSB1. Just check ls /dev/ttyUSB* before and after plugging in the device.

You need to connect to the second one. Usually, it is /dev/ttyUSB1 and you need to specify the baud rate which is 115200. Connect by executing:

screen /dev/ttyUSB1 115200

If it is still booting, you will see the boot sequence. Otherwise, it will ask for the login. Login as root. There is no password so just hit enter when it asks for it. And ta da! You’re in!

Just remember that the network is only setup after you eject the USB storage device. You need to do this each time. SSH is installed and started on the beaglebone by default and you can access it either through a terminal or through nautilus, the GNOME file browser. For shell access, just ssh://root@192.168.7.2. If you want to access the folder structure, go to the location bar in the file browser (Ctrl-L for short) and type in: sftp://192.168.7.2 Enter root as username and leave the password blank.

If you want to set a root password, type is passwd after login though I don’t really see a point. If someone wanted access, they can just mount the sdcard and copy the data or put a new version of Angstrom on it.

And speaking of new versions, you can get the latest version of Angstrom from http://downloads.angstrom-distribution.org/demo/beaglebone/

They’ve given the directions to load up the new image. WARNING: It will format all existing data so take backups. You can back up the entire sdcard including the original distro by executing the following command (Assuming the sdcard is at /dev/sdb):

mkdir beaglebone

cd beaglebone

sudo dd if=/dev/sdb of=unmarked_sd

tar zcvf unmarked_sd.tar.gz

sudo rm unmarked_sd

Make sure you’re using an sdcard reader for all this backup and restore. To restore, use an SD card of the same size or larger and execute:

sudo su

tar Ozxf unmarked_sd.tar.gz | dd of=/dev/sdb

The backup and restore portion has been adapted (Which is a subtle way of saying copying 😉 from http://taylanayken.wordpress.com/2012/03/27/getting-started-with-beaglebone/

Do take a look there. He shows how to compile a new kernel for the beaglebone. Pretty cool stuff.

That’s it for now. I’ll be adding more about this as I learn more about it. Don’t forget adding yourself to the dialout group! That’s what made me write this post in the first place because of its usefulness.

Ozxf unmarked_sd.tar.gz | dd of=/dev/sdb