LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-22-2014, 05:44 AM   #1
louigi600
Member
 
Registered: Dec 2013
Location: Italy
Distribution: Slackware
Posts: 635
Blog Entries: 20

Rep: Reputation: 81
reading data via i2c-dev


Following example from Documentation/i2c/dev-interface I wrote very simple code to read out raw data from ITG3200 ... but the odd thing is that I hadto skip data from other devices in an unexpected way.
What am I doing wrong ?

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

#define I2C_DEVICE "/dev/i2c-1"
#define I2C_DEV_ADDR 0x69
#define I2C_DEV_SELF 0x0
#define I2C_DEV_INT 0x1a
#define I2C_DEV_REG_START_ADDR 0x1b
#define I2C_DEV_REG_END_ADDR 0x22

main ()
{ int file,buffer;
  int i,j,k;
  int raw_data[4],data[4];
  char buf[256] = {0};

  if ((file = open(I2C_DEVICE, O_RDWR)) < 0)
  { /* ERROR HANDLING: you can check errno to see what went wrong */
    perror("Failed to open the i2c bus");
    exit(1);
  }

  if (ioctl(file, I2C_SLAVE, I2C_DEV_ADDR) < 0)
  { printf("Failed to acquire bus access and/or talk to slave.\n");
    /* ERROR HANDLING; you can check errno to see what went wrong */
    exit(1);
  }

/*For some unexpected reason I'm getting responses from the other I2C devices
  on the same bus so I'm ignoring data that does not match the WHO AM I reg
  and also data that has not set the interrupt register (meaning that data is
  not really avalible)
*/
  while (buf[0] != 0x69 || buf[26] == 0)
  { if (read(file,buf,sizeof(buf)) != sizeof(buf))
    { /* ERROR HANDLING: i2c transaction failed */
      printf("Failed to read from the i2c bus.\n");
      exit(1);
    }
  }

  j=0;
  for(i=I2C_DEV_REG_START_ADDR;i<=I2C_DEV_REG_END_ADDR;i++)
  { k=0;
    k= (buf[i] << 8) + buf[i+1];
    i++;
    raw_data[j]=k;
    printf("%4x \n",raw_data[j]);
    j++;
  }

  close(file);
}
 
Old 03-27-2014, 02:58 PM   #2
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,883
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
I2C is a common wire bus, the other devices may be communicating with other software for other purposes. Yes, you'll "see" those interactions; hence why you need to select only the data which is relevant to your communications. This is not privatized point-to-point. The messaging to/from all devices is visible at all devices; hence why you need the start sequence to grab the line and alert all devices that you wish to use the bus. There are rules for whether or not you can take the line; I forget what you're supposed to do, listen-try-listen again or something else. But that's why you're seeing data not relevant to your specific device, because you're seeing all data on that particular I2C bus.
 
Old 03-27-2014, 03:27 PM   #3
louigi600
Member
 
Registered: Dec 2013
Location: Italy
Distribution: Slackware
Posts: 635

Original Poster
Blog Entries: 20

Rep: Reputation: 81
Interesting ... this is happening on a PI running Slackware ARM and I don't know of anything else using the I2C bus. On that bus I can detect only the devices on the 10DOF that I wired up ... what else would be talking to the devices on the IMU board ?

ioctl(file, I2C_SLAVE, I2C_DEV_ADDR) should be asserting to what device I want to communicate with (ioctl(3, 0x703, 0x69))

I had a closer look at the data that I was discarding and it looked very much like the data was being rotated around full circle ... like as if I was reading 256 bytes from something that was a few bytes longer then 256 and each consecutive 256 byte read would get padded at the front with the leftover of the previous reading.
 
Old 03-28-2014, 07:03 AM   #4
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,883
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
OK, well your first post implied that you did have other devices hooked up, or at least didn't clarify that you feel nothing else is hooked up.

The software way to attack this is to add some debug to the I2C code, or write your own I2C code to bitbang the interface and diagnose more closely what is happening.

The better (IMHO) way to attack this is to hook a scope up to that interface and look at the signals.
 
Old 04-01-2014, 04:25 PM   #5
louigi600
Member
 
Registered: Dec 2013
Location: Italy
Distribution: Slackware
Posts: 635

Original Poster
Blog Entries: 20

Rep: Reputation: 81
What exactly do you mean by "The software way to attack this is to add some debug to the I2C code, or write your own I2C code to bitbang the interface and diagnose more closely what is happening."

I'm using i2c-dev kernel module to read data out of the bus and apart from the example code in the linux Documentation section the code fragment is all my poorly written code: are you suggesting that I should fiddle with i2c-deb module code ? (I think that's too much for me poor C programming skills) or should I be using a totally different approach for reading data out of the IMU ?

What if I try reading 512 or 1024 bytes at a time to see exactly how much data there is before pattern start over again ?
On the gyro the first byte is the "who am I id" it should be easy to recognize the beginning of a data pattern.
 
Old 04-02-2014, 08:24 AM   #6
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,883
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
I meant either to replace the driver with your own or modify the existing driver to add some debug to it. OK, not an option.

Your original issue was that you had to examine the data by way of testing that the address matched the register or device you intended to read.

Actually in looking, you need to do more to the device to read registers, you need to send the register address you wish to read. And you can't just bulk read from the start of registers to the end of registers, in fact you can't bulk read unless the manufacturer states that you can. For instance, there is a gap after the WHO_AM_I register, therefore saying you wish to read more than one byte from that address means nothing, you'll either get an error, or you'll get that one byte and the rest will be invalid.

Datasheet for the 3200. If you give no address information, it will assume the register you intend to read is 0x00 which happens to be the WHO_AM_I register. And you probably should read the TEMP and GYRO registers in bulk.

Take a look at the attached code. It is an example program for tickling the I2C port and specifically check out the get_i2c_register() and how it's used. There's also an example to set (write) a register. Much thanks to Sean Cross, the cited author.

What you'll see is that the author is using an i2c_rdwr_ioctl_data structure as well as an i2c_msg structure array, because you need to send two messages to read or write. You first need to indicate the address of the register you wish to read or write, and then you need to send a second message to execute the read or write.

I haven't specifically run this code; however looking at it, it appears to be the correct way.

Code:
/*
 This software uses a BSD license.

Copyright (c) 2010, Sean Cross / chumby industries
All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 * Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the
   distribution.  
 * Neither the name of Sean Cross / chumby industries nor the names
   of its contributors may be used to endorse or promote products
   derived from this software without specific prior written
   permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 
 */

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


#define I2C_FILE_NAME "/dev/i2c-0"
#define USAGE_MESSAGE \
    "Usage:\n" \
    "  %s r [addr] [register]   " \
        "to read value from [register]\n" \
    "  %s w [addr] [register] [value]   " \
        "to write a value [value] to register [register]\n" \
    ""

static int set_i2c_register(int file,
                            unsigned char addr,
                            unsigned char reg,
                            unsigned char value) {

    unsigned char outbuf[2];
    struct i2c_rdwr_ioctl_data packets;
    struct i2c_msg messages[1];

    messages[0].addr  = addr;
    messages[0].flags = 0;
    messages[0].len   = sizeof(outbuf);
    messages[0].buf   = outbuf;

    /* The first byte indicates which register we'll write */
    outbuf[0] = reg;

    /* 
     * The second byte indicates the value to write.  Note that for many
     * devices, we can write multiple, sequential registers at once by
     * simply making outbuf bigger.
     */
    outbuf[1] = value;

    /* Transfer the i2c packets to the kernel and verify it worked */
    packets.msgs  = messages;
    packets.nmsgs = 1;
    if(ioctl(file, I2C_RDWR, &packets) < 0) {
        perror("Unable to send data");
        return 1;
    }

    return 0;
}


static int get_i2c_register(int file,
                            unsigned char addr,
                            unsigned char reg,
                            unsigned char *val) {
    unsigned char inbuf, outbuf;
    struct i2c_rdwr_ioctl_data packets;
    struct i2c_msg messages[2];

    /*
     * In order to read a register, we first do a "dummy write" by writing
     * 0 bytes to the register we want to read from.  This is similar to
     * the packet in set_i2c_register, except it's 1 byte rather than 2.
     */
    outbuf = reg;
    messages[0].addr  = addr;
    messages[0].flags = 0;
    messages[0].len   = sizeof(outbuf);
    messages[0].buf   = &outbuf;

    /* The data will get returned in this structure */
    messages[1].addr  = addr;
    messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
    messages[1].len   = sizeof(inbuf);
    messages[1].buf   = &inbuf;

    /* Send the request to the kernel and get the result back */
    packets.msgs      = messages;
    packets.nmsgs     = 2;
    if(ioctl(file, I2C_RDWR, &packets) < 0) {
        perror("Unable to send data");
        return 1;
    }
    *val = inbuf;

    return 0;
}


int main(int argc, char **argv) {
    int i2c_file;

    // Open a connection to the I2C userspace control file.
    if ((i2c_file = open(I2C_FILE_NAME, O_RDWR)) < 0) {
        perror("Unable to open i2c control file");
        exit(1);
    }


    if(argc > 3 && !strcmp(argv[1], "r")) {
        int addr = strtol(argv[2], NULL, 0);
        int reg = strtol(argv[3], NULL, 0);
        unsigned char value;
        if(get_i2c_register(i2c_file, addr, reg, &value)) {
            printf("Unable to get register!\n");
        }
        else {
            printf("Register %d: %d (%x)\n", reg, (int)value, (int)value);
        }
    }
    else if(argc > 4 && !strcmp(argv[1], "w")) {
        int addr = strtol(argv[2], NULL, 0);
        int reg = strtol(argv[3], NULL, 0);
        int value = strtol(argv[4], NULL, 0);
        if(set_i2c_register(i2c_file, addr, reg, value)) {
            printf("Unable to get register!\n");
        }
        else {
            printf("Set register %x: %d (%x)\n", reg, value, value);
        }
    }
    else {
        fprintf(stderr, USAGE_MESSAGE, argv[0], argv[0]);
    }


    close(i2c_file);


    return 0;
}
 
2 members found this post helpful.
Old 04-03-2014, 10:37 AM   #7
louigi600
Member
 
Registered: Dec 2013
Location: Italy
Distribution: Slackware
Posts: 635

Original Poster
Blog Entries: 20

Rep: Reputation: 81
Ok that gives me some very good hints.

I was doing the read that way because it did appear that i2cget (from i2ctools) was going more or less the same thing.
Here's s the relevant part of the strace I got while reading a ITG3200 register range:
Code:
open("/dev/i2c-1", O_RDWR)              = 3
ioctl(3, 0x705, 0xbee842f0)             = 0
ioctl(3, 0x703, 0x69)                   = 0
ioctl(3, 0x720, 0xbee842f4)             = 0
close(3)                                = 0
According to i2c-dev.h this is what those ioctls do:
Code:
ioctl(3, 0x705, 0xbee842f0)= 0 -> I2C_FUNCS Get the adapter functionality mask
ioctl(3, 0x703, 0x69)      = 0 -> I2C_SLAVE Use this slave address
ioctl(3, 0x720, 0xbee842f4)= 0 -> I2C_SMBUS SMBus transfer
So they just seem to check out adapter functionality and then just bulk transfer a register range. I got put off into thinking that the register range could be as wide as the whole register set ... I had a look at what i2cdump does and it's much more complicated.
Maybe for what I need I can just read what I need instead of dumping everything.

Thanks for the help ... hope this can get me going ... if not I'll be back ;-)

Incidently the code example does not compile on my system:
Code:
root@pi:~# cc -O -o testme i2c_example.c
In file included from i2c_example.c:39:0:
/usr/local/include/linux/i2c-dev.h:37:8: error: redefinition of 'struct i2c_msg'
In file included from i2c_example.c:38:0:
/usr/include/linux/i2c.h:68:8: note: originally defined here
In file included from i2c_example.c:39:0:
/usr/local/include/linux/i2c-dev.h:89:7: error: redefinition of 'union i2c_smbus_data'
In file included from i2c_example.c:38:0:
/usr/include/linux/i2c.h:126:7: note: originally defined here
root@pi:~#
But maybe it's some conflict on structure definitions in i2c.h and i2c-dev.h ... if I comment out the first it seems to compile ok.

Last edited by louigi600; 04-03-2014 at 10:45 AM.
 
1 members found this post helpful.
Old 04-03-2014, 11:31 AM   #8
rtmistler
Moderator
 
Registered: Mar 2011
Location: USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 9,883
Blog Entries: 13

Rep: Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930Reputation: 4930
I'd be more concerned if it couldn't compile and the compiler couldn't therefore understand the structures. But a conflict of two include files or some other behavior is less a concern. You can also compile it with a debugging flag, for instance using gcc you'd add -ggdb as the argument to use GDB debugging, and then debug under GDB and verify that it properly recognizes the structures; or add debug print/log statements to do the same. You're on the correct track though.
 
Old 04-03-2014, 04:09 PM   #9
louigi600
Member
 
Registered: Dec 2013
Location: Italy
Distribution: Slackware
Posts: 635

Original Poster
Blog Entries: 20

Rep: Reputation: 81
Cool I can now read repetitively and successfully as many times as I want and take an average over let's say the last 10 readings (which should settle out the gyro jitter dew to ambient vibration).

Thanks rtmistler ... your post #7 was really helpful but it has already 2 people finding it helpful so it won't let me add a 3'rd.
Good to know that I was not the only one stuck on this sort of issue

For anyone intrested I'm keeping notes of what I'm doing for future reference and for anyone else intrested: have a look
here. There are examples of bash scripts reading data via i2c-tools and the C code doing the same sort of thing but by using i2c-dev kernel module to do the low lever communication (much like the i2c-tools do internally).

Last edited by louigi600; 04-04-2014 at 04:31 AM.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
linux c program for i2c reading from specified location pillisrikanth Linux - Software 15 03-05-2012 01:15 AM
How to forward IOCTL's from i2c-dev.c to adapter driver (i2c-rt3352.c) elonica.pl Linux - Kernel 2 08-23-2011 02:03 AM
[SOLVED] No /dev/i2c-0 entry after insmod i2c-dev sajnanazeer Linux - Newbie 1 08-10-2011 07:39 AM
Help! Problems with i2c-dev. No more /dev/i2c-* em1l Fedora 0 10-28-2005 05:48 AM
reading & interpreting data from /dev/gpmdata Zither Programming 1 11-02-2003 07:08 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 08:45 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration