Saturday, March 2, 2013

Programming I2C

Although you can perform simple i2c reads and writes using the command line tools i2cget and i2cset, for a more integrated approach you can use a programming language to talk to the bus.

The are dozens of languages which make claims about ease of use and learning etc. and I am sure you can program i2c from them.

What I will demonstrate here is the simple way to do it from c. Although I don't aim to teach how to program in c, I will try and explain what the code is doing so you can follow along even if you are new to c.

This will use some basic i2c read and writes as described at  http://www.kernel.org/doc/Documentation/i2c/dev-interface
We will also need to perform some IO Control (ioctl) which are i2c specific.

First we need some code to get us started. The #include basicly make certain function calls and constants available to the rest of our program.

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>


All of our code will live in the main function for now. main() is where all c programs start.

We need some variables which must be declared at the start of the function.
int main( int argc, char **argv )
{
        int i;
        int r;
        int fd;
        unsigned char command[2];
        unsigned char value[4];
        useconds_t delay = 2000;

        char *dev = "/dev/i2c-1";
        int addr = 0x48;


The [ ] syntax means an array so char command[2] is actually a variable which can hold 2 char values.

Some of our variables have been initialised to specific values so they are ready to use. The ones which have not been initialised will contain random values so we must assign a value to them before they can be used.

0x48 means hexadecimal 48 (which is decimal 72).

Next we print out a banner to show that the program is running
printf("PCF8591 Test\n");

The we get down to business and open the i2c device
        fd = open(dev, O_RDWR );
        if(fd < 0)
        {
                perror("Opening i2c device node\n");
                return 1;
        }


and select our slave device
        r = ioctl(fd, I2C_SLAVE, addr);
        if(r < 0)
        {
                perror("Selecting i2c device\n");
        }


Now we have an infinite loop
         while(1)
        {

There will be no way to end the program except by pressing Control C.

Next we have another loop which will run four times
                for(i = 0; i < 4; i++)
                {

Then we build a command for the pcf8591. The value of this is specified in the data sheet http://doc.chipfind.ru/pdf/philips/pca8591.pdf

In the first 8 bits of the command we will enable the analog output bit (0x40) and select which of the 4 inputs to read ((i + 1) & 0x03). We do a bitwise or to combine these values together with the | symbol.
command[0] = 0x40 | ((i + 1) & 0x03); // output enable | read input i

The // is the start of a comment so you can explain you code to the reader.

In the next 8 bits we increment the value for the analog output
command[1]++;

Now we are ready to send the command to the i2c bus
r = write(fd, &command, 2);

It is not clear why, but we need to wait for the command to be processed
usleep(delay);

Now we are ready to read a value. Remembering that the read is always one value behind the selected input (hence the +1 we used above).
r = read(fd, &value[i], 1);
if(r != 1)
{
        perror("reading i2c device\n");
}
usleep(delay);


Then we end the loop
        }
and now we can print out our results
        printf("0x%02x 0x%02x 0x%02x 0x%02x\n", value[0], value[1], value[2], value[3]);

end our infinite loop
    }

and although we may never reach here, we will clean up and quit.
  close(fd);
  return(0);
}


Now, if you enter all the code into a file called pcf8591d.c (you can copy the complete code as show below) then you are ready to compile it with this command
gcc -Wall -o pcf8591d pcf8591d.c
This says to compile the .c file and write the output (-o) to pcf8591d (if you don't specify an output file the default of a.out will be used which can be a but confusing). -Wall will make sure all warnings are printed out by the compiler.

Assuming the compile (and link) was successful you are ready to run
./pcf8591d
and you should see output like this:
PCF8591 Test
0x5f 0xd3 0xac 0x80
0xc1 0xd3 0xae 0x80
0xc1 0xd3 0xb0 0x80
0xc1 0xd3 0xb2 0x80
0xc1 0xd3 0xb7 0x80
0xc1 0xd3 0xba 0x80
0xc1 0xd3 0xde 0x80
0xc1 0xd3 0xdc 0x80
0xc1 0xd3 0xe0 0x80

To stop the program press ^c (Control + C).

Here is the complete source code:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>



// Written by John Newbigin jnewbigin@chrysocome.net

// Based on info from http://www.kernel.org/doc/Documentation/i2c/dev-interface
// And http://doc.chipfind.ru/pdf/philips/pca8591.pdf

int main( int argc, char **argv )
{
        int i;
        int r;
        int fd;
        unsigned char command[2];
        unsigned char value[4];
        useconds_t delay = 2000;

        char *dev = "/dev/i2c-1";
        int addr = 0x48;

        printf("PCF8591 Test\n");

        fd = open(dev, O_RDWR );
        if(fd < 0)
        {
                perror("Opening i2c device node\n");
                return 1;
        }

        r = ioctl(fd, I2C_SLAVE, addr);
        if(r < 0)
        {
                perror("Selecting i2c device\n");
        }

        while(1)
        {
                for(i = 0; i < 4; i++)
                {
                        command[0] = 0x40 | ((i + 1) & 0x03); // output enable | read input i
                        command[1]++;
                        r = write(fd, &command, 2);
                        usleep(delay);
                        // the read is always one step behind the selected input
                        r = read(fd, &value[i], 1);
                        if(r != 1)
                        {
                                perror("reading i2c device\n");
                        }
                        usleep(delay);
                }
                printf("0x%02x 0x%02x 0x%02x 0x%02x\n", value[0], value[1], value[2], value[3]);
        }

        close(fd);
        return(0);
}

In the next blog I will improve the output to print a graph of the values so you can see them move up and down.


41 comments:

  1. could you please explain the use of | and & in more detail?

    in particular the purpose of & 0x03

    regards

    ReplyDelete
  2. & and | are the c bitwise operators for AND and OR. This page may be of use http://www.tutorialspoint.com/cprogramming/c_bitwise_operators.htm

    ReplyDelete
  3. I believe this is a good tutorial to start using the /dev/i2c in embedded linux. thanks to writer

    ReplyDelete
    Replies
    1. The are dozens of languages which make claims about ease of use and learning etc. and I am sure you can program i2c from them. drywall texture contractors near me

      Delete
  4. Hi john thanks for your tutorial
    I couldnt open i2c-1 devices
    int fd = open("/dev/i2c-1", O_RDWR); fd returns -1.
    Whats the problem can you help me please

    ReplyDelete
    Replies
    1. The #include basicly make certain function calls and constants available to the rest of our program. mold remediation pittsburgh pa

      Delete
  5. The best way to debug this is call perror like this
    if(fd < 0)
    {
    perror("opening i2c device");
    exit(1);
    }

    That will tell you what is wrong. Likely the device node does not exist or you don't have permissions. Once you know the problem you can try and fix it up. See my post http://blog.chrysocome.net/2012/11/raspberry-pi-i2c.html on how to fix both of these issues.

    ReplyDelete
  6. thanks buddy you made my hard day come to a happy ending,
    was trying to write to the control buffers of the i2c registers of Mpu9150 for 6hrs without any result. But finally saw your post and found the result.

    ReplyDelete
  7. I want to send two sets of data from RPI to arduino via i2c ,used by the RPI c code.

    The two data sets is an integer value between + - 255.

    guide for me please.

    regards

    ReplyDelete
  8. command[1] - not initialized!
    command[0]=command[1]=0; should be

    ReplyDelete
  9. Hi,

    How do I read only the input AIN0 without for loop?

    In the first 8 bits of the command we will enable the analog output bit (0x40) and select which of the 4 inputs to read ((i + 1) & 0x03). We do a bitwise or to combine these values together with the | symbol.
    command[0] = 0x40 | ((i + 1) & 0x03); // output enable | read input i

    The // is the start of a comment so you can explain you code to the reader.

    In the next 8 bits we increment the value for the analog output
    command[1]++;

    ReplyDelete
  10. It is important that the messages were passed well and clearly.Emma

    ReplyDelete
  11. I feel extremely grateful to have discovered your web page and look forward to some more excellent moments reading
    here. Thank you again for everything. cineblog01

    ReplyDelete
  12. Thanks a lot for the post. It has helped me get some nice ideas. I hope I will see some really good result soon.
    hotmail login

    ReplyDelete
  13. I really appreciate the kind of topics you post here.
    facebook baixar

    ReplyDelete
  14. SDA is the data line. The SCL & SDA lines are connected to all devices on the I2C bus.

    1playergames66

    ReplyDelete
  15. Oh my goodness! an amazing article dude. Thank you However I am experiencing issue with ur rss. Don’t know why Unable to subscribe to it. Is there anyone getting identical rss problem? Anyone who knows kindly respond. Thnkx

    Codepen.io
    Information
    Click Here
    Visit Web

    ReplyDelete
  16. It's really nice and meaningful. It's really cool blog. Linking is very useful thing. you have really helped lots of people who visit this blog and provide them useful information. Thanks for sharing and please update some more information here. Get best App Developer Dubai service you visit here site for more info.

    ReplyDelete
  17. Thanks for taking the time to discuss this. I feel strongly about it and love to learning more on this topic. If possible, as you gain expertise, would you mind updating your blog with more information? It is extremely helpful for me. Get best Remote Working Jobs provide good services visit here site for more info from Smart Job

    ReplyDelete
  18. This comment has been removed by the author.

    ReplyDelete
  19. I believe this is a good tutorial to start using the /dev/i2c in embedded linux. thanks to writer awning for patio

    ReplyDelete
  20. The ones which have not been initialised will contain random values so we must assign a value to them before they can be used. Egress Repair

    ReplyDelete
  21. I want to send two sets of data from RPI to arduino via i2c ,used by the RPI c code Waterproofing Services

    ReplyDelete
  22. Excellent explanation. Amarillo Drywall Contractors will use this in their future I2C program.

    ReplyDelete
  23. Now I understand it more. Thanks for explaining. web design

    ReplyDelete
  24. They have an elevated degree of abilities, as we've referenced previously, they are a lot higher in HackerRank rivalry than Chile developers. As per Topcoder, Ukraine engineers are in sixth spot around the world. What's more, designers from Chile didn't get in this 25 rundown. In view of our experience as a Ukrainian IT reevaluating supplier, we at Mobilunity can affirm that abilities and ability of Ukrainian designers are amazing. That is reflected in our clients' criticism: "Subsequent to attempting different far off joint efforts in a few nations, Mobilunity has been remarkable in employing very skilled individuals " - Maxime Haineault, 3e joueur>> Mobilunity

    ReplyDelete
  25. Im considering this suggesiton! our site for my sample.

    ReplyDelete
  26. www.tampa-deckbuilders.com/ appreciates your effort in posting codes like these..

    ReplyDelete
  27. The topic is highly informative. Well done on sharing it.
    Accountants Edmonton

    ReplyDelete
  28. Awesome read you’ve got there, I’ll have to pass it on!
    Accountants Calgary

    ReplyDelete
  29. We are recognized specialists in all home improvement areas, including painting, decorating, fencing, decking, soft landscape Design, patio paving, artificial grass installation, and building concrete bases.

    ReplyDelete
  30. We have the solution with our full-service carpet cleaning in Maidstone, Kent. Our skilled technicians can effectively deep-clean your carpets, upholstery, rug cleaning, and
    mattress, greatly improving the appearance and feel of your home or business.

    ReplyDelete