This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

I2C programming

Other Parts Discussed in Thread: OMAP-L137, TCA6416

Hi all,

I want to control battery LTC4155 through i2c4 interface.

In LTC4155 datasheet the slave address they have given is.

0b0001001[R/W];

Read = 0x13

Write = 0x12

Through the user space, I want to do program.

Refered Documentation/i2cinterface/dev-interface document. The code given below;

//#include <glib.h>
//#include <glib/gprintf.h>
#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 I2C_SLAVE       0x0703

void
sensors_LTC_init(void) {
    int file;
    char filename[40];
    char *buffer;
    int addr_write = 0x12;        // The I2C address of the LTC4155
    int addr_read = 0x13;        // The I2C address of the LTC4155

    sprintf(filename,"/dev/i2c-3");
    if ((file = open(filename,O_RDWR)) < 0) {
        printf("Failed to open the bus.");
        /* ERROR HANDLING; you can check errno to see what went wrong */
        exit(1);
    }

    if (ioctl(file,I2C_SLAVE,addr_write) < 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);
    }

    char buf[10] = {0};
    float data;
                                                                                                                                                      1,1           Top
    char channel;
    int i;

    buf[0] = 0x00;

    if (write(file,buf,1) != 1) {
        /* ERROR HANDLING: i2c transaction failed */
        printf("Failed to write to the i2c bus.\n");
        printf("\n\n");
    }

    if (ioctl(file,I2C_SLAVE,addr_read) < 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(i = 0; i<4; i++) {
        // Using I2C Read
        if (read(file,buf,2) != 2) {
            /* ERROR HANDLING: i2c transaction failed */
            printf("Failed to read from the i2c bus.\n");
            printf("\n\n");
        } else {
            //data = (float)((buf[0] & 0b00001111)<<8)+buf[1];
            //data = data/4096*5;
            //channel = ((buf[0] & 0b00110000)>>4);
            printf("Channel %02d Data:  %04f\n",channel,data);
        }
    }

}

int main () {
        sensors_LTC_init();
        return 0;

}

 

I 'm unable to read and write using i2c , what may be the problem Let me know?

Regards,

santosh vastrad

  • I think the i2c library uses a single 7-bit address rather than two 8-bit addresses. Try using an address of 0x09 (0x12>>1 or 0x13>>1)  for both read and write. You only need to do a ioctl(I2C_SLAVE) once. Note that the read() and write() interface to the i2c library do not support repeated start. You have use ioctl() for that.

  • Hi Norman,

    How i did program to access from user-space, 

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

    #define I2C_SLAVE       0x0703

    int main () {

            int file;
            int adapter_nr = 3;

            unsigned char addr = 0x09;

            unsigned char reg = 0x00;
            char buf[2];

            buf[0] = 0x13;
            buf[1] = 0x00;

            char filename[20];

            snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);

            file = open(filename, O_RDWR);

            if (file < 0) {
                    printf ("unable to open file\n");
                    exit (1);
            }


            if (ioctl(file, I2C_SLAVE, addr) < 0) {
                    printf ("ioctl error\n");
                    exit (1);
            }

    if (write(file, buf, 2) != 1) {
                    printf ("Writing failed\n");
            }

            if (read(file, buf, 2) != 1) {
                    printf ("Reading failed\n");
            } else {
                    printf ("Buffer = %x\n", buf[0]);
                    printf ("Buffer = %x\n", buf[1]);
            }

            return 0;
    }


    stil this program is not working , reading and writing failed!

                                                                                                           1,1           Top
    Accroding to your suggestion , only once i did ioctl, and also slave address 0x09 only used,

    You mentioned (0x12 >> 1 and 0x13 >> 1),

    what else i should  make changes suggest me?

    Thanks ,

    santosh vastrad

  • The read() and write() functions return the number of bytes read/written or -1 for error. Your code read/writes 2 but tests for 1. You should test for 2 if your intent is to write 2 bytes. If you get a -1 then immediately print out the global variable errno to see what failed. Include errno.h to use errno. You code might be working as is.

    You should not have to define I2C_SLAVE. If should already be defined in linux/i2c.h or linux/i2c-dev.h. Forget which.

  • Hi Norman,

    This is what i have made changes accroding to your suggestions,

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <linux/i2c-dev.h>
    #include <linux/i2c.h>
    #include <errno.h> // errno.h included.

    int main () {

            int file;
            char filename[20];
            unsigned char addr = 0x09;
            unsigned char reg  = 0x00;
            unsigned char buf[2];
           unsigned char val;

           buf[0] = reg;

          sprintf (filename, "/dev/i2c-%d", 3);

            if ((file = open(filename, O_RDWR)) < 0) {
                    printf ("file not opened\n");
                    exit (1);
            }

            if (ioctl(file, I2C_SLAVE, addr) < 0) {
                    printf ("ioctl error\n");
                    exit (2);        if (write(file, &reg, 1) != 1) {
                    perror ("write failed\n");
            }

            if (read(file, &val, 1) != 1) {
                    perror ("read failed\n");
            } else {
                    printf ("val = %x\n", val);
            }

            return 0;
    }

            }

    By including errno.h file,

    on running on target board , i got error is;

    write failed

    :Remote I/O error

    read failed
    : Remote I/O error

     

    Regards,

    santosh vastrad

     

     

     

     

  • I think that error means that the device does not exist or not acknowledging the address. The preferable way to debug i2c is to attach a scope to the SCL and SDA lines. Check for master transmit and slave ack. On my platform OMAP-L137, the i2c ports are numbered oddly, I2C0 is at /dev/i2c-1. Your platform might be the same. For I2C4, you might have to use /dev/i2c-5. Or /dev/-i2c-4 if you starting numbering from 0. Also, you might have to pinmux the i2c4 controller to the outside world. In your case, that might be the pad configuration thing. Not familiar with the AM35xx or AM37xx.

  • Hi Norman,

    According to your suggestion,

    I checked with oscilloscope for sda and scl!

    scl is coming fine!

    but that binaries of above my programs, when i ran nothing is coming!

    We did some changes that i2c2 connected now to battery! because earlier i2c4 was an master-transmitter only!

    According to this, i have even changed my application program with /dev/i2c-2!!!!!!!!!!!

    still same error i'm getting on the target board!

    Bad address!!!!!!!!!!!!!!

    But in datasheet of LTC4155 battery the address is 0x0001001[R/W]!!!!!!!!!!!

    what might be the issue?

    Regards,

    santosh vastrad

  • Norman:

    In our schematic i2c2,

    so in target board they are saying it as /dev/i2c-1:

    by using i2ctools,

    I got error is:

    root@beagleboard:~/i2ctools# ./i2cget -f 1 0x09 0x00 b
    set_slave_addr function
    WARNING! This program can confuse your I2C bus, cause data loss and worse!
    I will read from device file /dev/i2c-1, chip address 0x09, data address
    0x00, using read byte data.
    Continue? [Y/n] y
    read_write=1,command=0,size=2,byte=2
    Error: Read failed

    What might be the reason?

    Regards,

    santosh vastrad

  • The Linux i2c driver is quite complicated with many layers. It would take a lot of work to trace all the error codes. I think you really should verify that you have the right /dev/i2c and the right HW port.

    I can't remember if SCL remains on all the time. I thought it should only turn on during a transaction. You should always see the device address on SDA regardless of the slave, You should see 7 bits of 0x09 on SDA and 7 or 8 SCL pulses. This should happen even if the slave is not there.

    Check that your board init is mux'ing your pins to your I2C controller. Check that your SCL and SDA lines have pullup resistors. Your processor might have internal pullups. Once you get SCL and SDA toggling for the first portion of a I2C transaction, then you can start debugging the rest.

  • Hi,

    instead of using IOCTL to control the I2C from user space, how to write a code of I2C driver module at Kernel Space level ? how to do memory mapping, configuring the I2C registers?

    Thanks.

  • Writing your own driver is always an option. Most don't have the time, motivation or the ability. I'm a bit of all three.

    You could use the Linux I2C driver at the kernel level. The userspace driver is implemented in drivers/i2c/i2c-dev.c. The kernel level I2C calls are functions like i2c_master_recv(), i2c_master_send(), i2c_transfer(), etc.

    If you want to write your driver that accesses the I2C controller directly, look at the OMAP level code at drivers/i2c/busses/i2c-omap.c. Physical memory is allocated using request_mem_region() and ioremap(). It is likely you will have to disable the Linux i2c driver as it will allocate the same memory region and also probably interfere with your driver.

    Staying the the "official" Linux i2c driver has other advantages besides code reuse and less effort. Building you own driver will mean you will have to tweak your driver everytime you change kernel versions. Linux kernel writers seems to love changing interfaces or driver models that break "unofficial" drivers. There userspace utilities that use the "official" i2c interface. You would lose those as well.

  • Hi Norman,

    i using beaglebone Rev A3 with arago image, curently i doing the I2C module interface with TCA6416 i2c port expander, i can use the i2c-tools sucessfully,

    i2cset 2 0x20 0x02 0xFF

    with no error come out. but seem like the data did not reach the output port of i2c port expander, i use a led as a indicator.

    in arch/arm/mach-omap2/board-am335xevm.c

    i change

    omap_register_i2c_bus(2, 100, NULL, 0);

    to

    omap_register_i2c_bus(2, 400, NULL, 0);

    because the TCA6416 i2c port expander required 400kHz.

    What i've miss?

  • Any time you're working with a Linux system, trying to interact with an I2C device, Always start by using i2cdetect. Begin with displaying all the i2c busses using the command: "i2cdetect -l". Next, have i2cdetect display all the devices on the i2c bus using the command: " i2cdetect -y -r 3" This will search and display devices attached to i2c-3 bus. If you don't see your device. FORGET trying to interact with it. There's some form of hardware problem, or you have requested the wrong bus. I hope this helps.