Last modified on 5 November 2011, at 12:05

I2C on Alix Pcengine

Nico found this post on the PCEngines forum: I2C-bus examples or a howto

Working example

The above helps, but it's not all. Looking at kernel Documentation/i2c/dev-interface helps some more, but not quite. Here's the smallest compiling example to use I²C in userspace linux ; it took me a while to get it together.

example from from Documentation/i2c/dev-interface

So let's say you want to access an i2c adapter from a C program. The first thing to do is "#include <linux/i2c-dev.h>". Please note that there are two files named "i2c-dev.h" out there, one is distributed with the Linux kernel and is meant to be included from kernel driver code, the other one is distributed with i2c-tools and is meant to be included from user-space programs. You obviously want the second one here.

Now, you have to decide which adapter you want to access. You should inspect /sys/class/i2c-dev/ or run "i2cdetect -l" to decide this. Adapter numbers are assigned somewhat dynamically, so you can not assume much about them. They can even change from one boot to the next.

The example values are taken from PCA9551 8 bit GPIO / LED Driver

Next thing, open the device file, as follows:

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

int main(int argc, char *argv[])
{
   int file;
   int adapter_nr = 0; /* probably dynamically determined */
   char filename[20];

   snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
   file = open(filename, O_RDWR);
   if (file < 0) {
           /* ERROR HANDLING; you can check errno to see what went wrong */
           exit(1);
   }

When you have opened the device, you must specify with what device address you want to communicate:

  int addr = 0x67; /* The I2C address of PCA9551 */

  if (ioctl(file, I2C_SLAVE, addr) < 0) {
    /* ERROR HANDLING; you can check errno to see what went wrong */
    printf("error: ioctl on file failed\n");
    exit(1);
  }

Well, you are all set up now. You can now use SMBus commands or plain I2C to communicate with your device. SMBus commands are preferred if the device supports them. Both are illustrated below.

  /* Device register to access */
  __u8 reg;
  __s32 res;
  __u8 value;
  char buf[10];

  /* read a byte using SMBus commands */
  // INPUT (read pin state)
  reg = 0x00;
  res = i2c_smbus_read_byte_data(file, reg);
  if (res < 0) {
    /* ERROR HANDLING: i2c transaction failed */
    printf("error: i2c read failed\n");
  } else {
    /* res contains the read word */
    printf("LED status: %x\n",res);
  }

  /* write a byte using SMBus commands */
  // LEDs 4-5 on PWM0, LEDs 6-7 on PWM1
  reg = 0x06;  // register we want to write to
  value = 0xfa;
  res = i2c_smbus_write_byte_data(file, reg, value);
  if (res < 0) {
    /* ERROR HANDLING: i2c transaction failed */
    printf("error: i2c write failed\n");
  }
}

Let the LEDs blink!