Explicat
Posts: 7
Joined: Tue Aug 06, 2013 3:45 pm

Understanding i2c write in C

Tue Aug 06, 2013 11:09 pm

Hi there,
by using the following program I'm trying to write data to the registers of the PWM servo driver from Adafruit. Each of it's channels is connected to an LED.
Actually, I want to write the value 0x0F to the register 0x09 and the value 0xAB to the register 0x08, so the value for Channel 0 will be 0x0FAB.

Code: Select all

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

#define I2C_ADDR 0x40

int main (void) {
        int value;
        int file;

        file = open("/dev/i2c-1", O_RDWR);

        if (file < 0) {
                printf("Error opening file: %s\n", strerror(errno));
                return 1;
        }

        if (ioctl(file, I2C_SLAVE, I2C_ADDR) < 0) {
                printf("ioctl error: %s\n", strerror(errno));
                return 1;
        }

        __u8 deviceRegister = 0x09; /* Device register to access */
        __s32 res;
        char buf[10];

        // Write 0x0FAB
        buf[0] = deviceRegister;
        buf[1] = 0xAB;
        buf[2] = 0x0F; // more siginificant bits

        if (write(file, buf, 3) != 3) {
                fprintf(stderr, "i2c transaction failed\n");
        } else {
                fprintf(stderr, "Success!\n");
        }

        close(file);
        return 0;
}
After program execution register 0x09 is set appropriately. But when I take a look at register 0x08 with

Code: Select all

i2cget -y 1 0x40 0x08
it's value is still 0x00 as before.
Where does my value 0xAB get lost? Respectively, how can I address 0x09 and 0x08 in one single write call?

drhastings
Posts: 113
Joined: Wed Feb 06, 2013 11:38 pm

Re: Understanding i2c write in C

Wed Aug 07, 2013 6:29 am

I would go through the data sheet and see if there is any kind of setting that allows multiple bytes to be written at a time. Some devices let you do this, some don't.

From looking at your code I suspect what is happening is that you aren't ever writing to register 0x08, as you never send that value to the controller. Instead you are first writing 0xAB to register 0x09, then immediately overwriting that with 0x0F.

Depending on the device you would either need to figure out how it should be set to allow multibyte transfer to sequential registers (read the datasheet) and start the transfer pointing at 0x08, or just do it in two transfers. Just a guess though.
http://www.dansrobotprojects.com/

User avatar
Richard-TX
Posts: 1549
Joined: Tue May 28, 2013 3:24 pm
Location: North Texas

Re: Understanding i2c write in C

Wed Aug 07, 2013 5:14 pm

The simple way to write a word to the Adafruit 16 channel controller is to use the word option to i2cget/i2cset.

i2cset 1 0x40 0xaddr 0xFFFF w

In this manner, register 0xaddr and 0xaddr+1 will get written in a single write operation.

In your c code define your variable as a word instead of an int and it will work.

If I were you I would do the proof of concept testing using the native commands (i2cget, etc) and then rewrite it in c.

Rich
Richard
Doing Unix since 1985.
The 9-25-2013 image of Wheezy can be found at:
http://downloads.raspberrypi.org/raspbian/images/raspbian-2013-09-27/2013-09-25-wheezy-raspbian.zip

Explicat
Posts: 7
Joined: Tue Aug 06, 2013 3:45 pm

Re: Understanding i2c write in C

Wed Aug 07, 2013 8:21 pm

Richard-TX wrote: i2cset 1 0x40 0xaddr 0xFFFF w
In this manner, register 0xaddr and 0xaddr+1 will get written in a single write operation.
Hi Rich,
thanks for the tip with the word mode. That's what I was looking for.
However, it's not woking as intended. I wrote

Code: Select all

i2cset 1 0x40 0x0C 0x0ABC w
Registser 0x0C is set correctly, but the value of 0x0D remains 0x00.

Code: Select all

$ i2cget -y 1 0x40 0x0C
0x0a
$ i2cget -y 1 0x40 0x0D
0x00
Am I missing something?

drhastings
Posts: 113
Joined: Wed Feb 06, 2013 11:38 pm

Re: Understanding i2c write in C

Wed Aug 07, 2013 8:58 pm

I'm even more convinced that there is some setting in some control register on the chip you are using that needs to be set appropriately in order to allow multi-byte transfers to sequential registers. Look in the data sheet for something controlling multibyte transfers, sequential registers or auto incrementing register addresses or some such.

I don't think the issue is on the linux i2c side and sending the values but rather you are omitting some step needed to initialize the external part into the operational state you are looking for. I think the chip you are talking to is just dumping some of the info you are sending because it is not configured to accept a new value for more than one register per transfer.
http://www.dansrobotprojects.com/

Explicat
Posts: 7
Joined: Tue Aug 06, 2013 3:45 pm

Re: Understanding i2c write in C

Wed Aug 07, 2013 10:00 pm

Hey mate,
thank you so much! In fact, the Auto Increment (AI) mode was disabled.
Bit 4 had to be enabled at address 0x00.
Since the value of 0x00 had been 0x11 before, I set it to 0x31

Code: Select all

i2cset -y 1 0x40 0x00 0x31
Now writing a word to the registers is finally possible :D

drhastings
Posts: 113
Joined: Wed Feb 06, 2013 11:38 pm

Re: Understanding i2c write in C

Thu Aug 08, 2013 12:45 am

Glad to be of assistance, good luck with the rest of your project!
http://www.dansrobotprojects.com/

User avatar
Richard-TX
Posts: 1549
Joined: Tue May 28, 2013 3:24 pm
Location: North Texas

Re: Understanding i2c write in C

Fri Aug 09, 2013 1:14 pm

Those silly registers will get you every time. :D

I recently had a MCP23S17 (SPI bus) where I needed to turn the addressing pins on.

I do think that you are doing the right thing by programming in c. Just don't forget to trap errors.

My preferred method of application development is to use the quick and dirty programming languages like /bin/sh or python for proof of concept testing and then move to something like c.

Please post a demo program for that 16 channel controller so that others can benefit from your efforts.

My programs for the LED controller feature stepping the LED brightness with various step sizes and various step speeds. This also gives the effect making the LED look like an incandescent bulb with it's inherent warm up and cool off times. It is subtle but less jarring/startling than simply turning the LED on.
Richard
Doing Unix since 1985.
The 9-25-2013 image of Wheezy can be found at:
http://downloads.raspberrypi.org/raspbian/images/raspbian-2013-09-27/2013-09-25-wheezy-raspbian.zip

Return to “Interfacing (DSI, CSI, I2C, etc.)”