I2c in userspace on linux (eg, raspberry pi)

From HeepyWiki
Jump to: navigation, search

There are any number of wrappers and libraries, some try to make it look like Arduino's Wiring, others have their own model, but underneath, they all use the linux i2c-dev interface, which isn't too hard to use itself, for i2c master mode, at least. Basically the model is you open the file, use an ioctl on it to set the desired slave address, then use write() and read() on it to converse. This is quite limited as you don't have direct control over the i2c bus state (ie, start, rep_start, etc) but you can do most stuff. Most of the wrapper libraries ONLY implement reading or writing registers with one-byte addresses, which prevents you from using devices that act differently. Here's an example for accessing the ADS1110 i2c 16 bit a/d converter, which doesn't have registers. You configure it by just sending one byte, and read it by just reading three bytes.

// linux userspace driver for ADS1110 i2c a/d converter. 
// eric volpe 4/15/2013
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define I2CDEV "/dev/i2c-1"
#define I2CADDR 0x4D

int main(void) {
  int file;
  int data;
  float volts;
  char buf[10] = {0};

  if ((file = open(I2CDEV,O_RDWR)) < 0) {
    printf("Failed to open the bus.");
    exit(1);
  }

  if (ioctl(file,I2C_SLAVE,I2CADDR) < 0) {
        printf("Failed to acquire bus access and/or talk to slave.\n");
        exit(1);
  }

  while(1) {
    // send the command to configure and start conversions
    buf[0] = 0x8c;
    if (write(file,buf,1) != 1) {
      printf("Failed to write to the i2c bus.\n");
    }
    // keep reading until the "ready" bit shows up in the config byte
    // which is the third byte read. (there are no registers)
    do {
      if (read(file,buf,3) != 3) {
        printf("Failed to read from the i2c bus.\n");
      }
    } while (buf[2] & (1<<7));

    data = (buf[0]<<8)|buf[1];
    if (data > 32767)
      data = -(65535-data);
    volts = ((float)data/32767)*2.048;
    printf("data: %d volts: %.4f\n",data, volts);
  }
}