Search code examples
ci2cioctl

I2C Read returns incorrect value


I am trying to read in an i2c value like you would with i2cget, but it is returning the wrong value in one of the cases.

i2cget -y 0 0x57 0x40 returns 0x57
i2cget -y 0 0x3b 0x09 returns 0x86

When I run my program with #define I2C_ADDR 0x57 and buffer[0] = 0x40 my program returns 0x57.

But when I run my program with #define I2C_ADDR 0x3b and buffer[0] = 0x09 my program returns 0x00.

Here is my code:

// #define I2C_ADDR 0x57
#define I2C_ADDR 0x3b

// #define I2C_REG 0x40
#define I2C_REG 0x09

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

    char buffer[1];
    buffer[0] = I2C_REG;

    int fd;

    // Get i2c File Descriptor
    if((fd = open("/dev/i2c-0", O_RDWR)) >= 0){

        // Set i2c Block Address
        if((ioctl(fd, I2C_SLAVE, I2C_ADDR)) >= 0) {

            // Set i2c Register Address
            write(fd, buffer, 1);

            // Read data at Register into buffer
            read(fd, buffer, 1);

            // Close fd
            close(fd);

            // Print Result
            printf("0x%02x\n", buffer[0]);

        } else {
            // ioctl error
            printf("ioctl error: %s\n", strerror(errno));
        }
    } else {
        // file error
        printf("Error opening file: %s\n", strerror(errno));
    }

    return 0;
}

I ran an strace on i2cget -y 0x3b 0x09 and my program. Here is a piece of output that shows how the 2 reads differ.

i2cget:

open("/dev/i2c-0", O_RDWR|O_LARGEFILE)  = 3
ioctl(3, 0x705, 0xbeae5bf8)             = 0
ioctl(3, 0x703, 0x3b)                   = 0
ioctl(3, 0x720, 0xbeae5b4c)             = 0
close(3)                                = 0

my program:

open("/dev/i2c-0", O_RDWR|O_LARGEFILE)  = 3
ioctl(3, 0x703, 0x3b)                   = 0
write(3, "\t", 1)                       = 1
read(3, "\0", 1)                        = 1
close(3) 

Solution

  • From the strace of i2cget I researched 0x720 and found out it is the value of I2C_SMBUS. I then found the source code for the i2cget in buildroot.

    There was no use of this in the C code, but there was use of a function i2c_smbus_read_byte_data . I added this to my code and ran it, it wouldn't run due to the function being undefined. I copied the i2c-dev.h file from buildroot to my folder and changed the #include to include the local file and it ran, and output the right data.

    Here is the code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    #include <errno.h>
    #include "i2c-dev.h"
    
    #define I2C_ADDR 0x3b
    #define I2C_REG 0x09
    
    // Prints Value of /dev/i2c-0 at Block 0x3b Register 0x09
    int main(int argc, char **argv) {
        int fd, res;
    
        if ((fd = open("/dev/i2c-0", O_RDWR)) >= 0) {
    
            ioctl(fd, I2C_SLAVE, I2C_ADDR);
    
            res = i2c_smbus_read_byte_data(fd, I2C_REG);
    
            close(fd);
    
            printf("Value - 0x%02x\n", res);
        } else {
            printf("Error opening file: %s\n", strerror(errno));
        }
    
        return 0;
    }