Friday, May 16, 2014

I2C with the Beaglebone Black

Recently, a colleague asked if I could help with a project that required an I2C bus. This technology is nothing new to me, but his request came with a twist: it needed to be done on a Beaglebone. Well, I couldn't deny this opportunity because I too had a similar project that requested very much the same setup.

I quick bit of surfing surprisingly turned up dry results - most of which were examples in Python. (Why not Ruby?!) Anyway, after several hours of research I finally managed to uncover some valuable hits...

First things first! In order to use I2C one must start up it's engine(s). This is done by entering the following shell command: (There may be two more capes for the other buses, however this is the only one I could find in /lib/firmware; For distros such as Angstrom, I2C should already be loaded)
echo BB-I2C1 > /sys/devices/bone_capemgr.*/slots

REGISTERS
To access i2c registers, the interface clock [ICLK] first needs to be un-gated. This can be done through the Power, Reset, and Clock Management [PRCM] module registers: CM_PER_L4LS_CLKSTCTRL & CM_PER_I2C_CLKCTRL. For this demonstration, the code from the git repository BoneMemory is used - load the shell script with "source script.sh" command.

# Enable I2C2 ICLK and FCLK - otherwise 'Bus Error'
writem 0x44E00000  0 0 # CM_PER_L4LS_CLKSTCTRL.MODULCTRL = 0
writem 0x44E00000 50 2 # CM_PER_I2C2_CLKCTRL = 2

# Prepare bus data and begin

writem 0x4819C000 98 1 # I2C_CNT  = 1
writem 0x4819C000 9C F # I2C_DATA = 0x0F
writem 0x4819C000 A4 8C03 # EN/MASTER/TX/START/STOP

See Pg 3715 of the Technical Reference Manual for a detailed approach to reading and writing via registers.


BASH
(I2C devices are assigned according to the order they were initialized so $Bus=1 may be the I2C-2 bus. Confirm you are using the correct bus by listing them:
                                 "ls -l /sys/bus/i2c/devices"
I2C0 = 0x44E0B000, I2C1 = 0x4802A000, I2C2 = 0x4819C000)

Next is the handy dandy tool that will automagically bit-bang for you to find all connected devices on the given bus: (UU is a reserved address)
i2cdetect -y -r $Bus

To view all the bit values stored in a slave's register file (EE/PROM), one can call:
i2cdump -y $Bus $Addr

To get a particular byte value from the slave's register file, use:
i2cget -y $Bus $Addr $Offset
# Example: i2cget -y 1 0x77 0xB2 w - read a word from 0x77B2

And to write to the register (if permitted by the slave), use:
i2cset -y $Bus $Addr $Offset $Value
# Example: i2cset -y 0 0x52 0x02 0xFF - write 0xFF to 0x5202

These are all script values, but at the same time my priority was set on doing all this in C/C++ and I really didn't want to use the 'system()' function. I knew from my experience with other elements including PWM and GPIO, that there are files one can access in a lower level language such as C that would allow me this I2C functionality, and, after a 45 min presentation (v=8C2zk6B-eLU), I was correct!

The files are located in /dev: i2c-0, i2c-1, & i2c-2

- - - - - - - - - - - - - - - - -
I've tested both the registry and bash approaches with great success!