Pages

Thursday, May 29, 2014

Writing a userland driver for Raspberry PI I2C device.

In my last post I used I2C bus on the raspberry pi header to extend IOs to other devices - may it be an input device (example dip switches) or an output device (array of leds). I did this by connecting the MCP23017 chip which communicates through the I2C bus thats present on the raspberry pi header.

The program assumes that the i2c-dev and i2c_bcm2708 drivers are loaded in the kernel, and that /dev/i2c-1 (Raspberry Pi Model B, Rev. 2) is created by the drivers to interface with userland applications.




Note that /dev/i2c-1 is writable only by the superuser, so running this program would require appropriate permissions. You can either add the user to the i2c group, or run the program as root.

The example C program below illustrates how data can be written to that device (led's) which is located at I2C address of 0x21. 

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

/* This function is borrowed from i2ctools package */

int main(int argc, char * argv[])
{
    int file, counter;
    const char * i2cbus="/dev/i2c-1";
    // Data write contains reg address byte[0] plus
    // actual data in byte [1]
    char buf[2];


    // Open the i2c
    file = open(i2cbus, O_WRONLY);
    if (file < 0)
    {
        printf("Failed to open file: %s\n", i2cbus);
        exit(1);
    }

    // Set the device address via IOCTL
    if (ioctl(file, I2C_SLAVE, 0x21) < 0)
    {
        printf("Failes setting device address\n");
        exit(1);
    }

    // We set the direction registers first
    buf[0] = 0x00; // Port b
    buf[1] = 0x00; // All bits set as output
    if (write(file, buf, 2) != 2)
    {
        printf("Failed to write to i2c device\n");
        exit(1);
    }

    for(counter = 0; counter < 100; counter++)
    {
        // We write to register 0x14 for the leds
        buf[0] = 0x14;
        buf[1] = (char) counter;

        if (write(file, buf, 2) != 2) 
        {
            printf("Failed to write data to device\n");
            exit(1);
        }

        sleep(1);
    }

    close(file);

}

In the above code, I opened the device /dev/i2c-1 just like any other file. To set the device address, I have to use an ioctl call passing in the I2C_SLAVE command and the device address as the value. Writing to the led's require two bytes, the first being the register address of the MCP23017 (0x14 for the led), and the second byte is the actual data written. In this case we are simply writing the counter to the 8 bit register which in turn turns the led on with the value of the counter.


You can get the above code from here.

No comments: