Accessing I2C under Linux

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've been trying to communicate with the I2C interface of a video sensor under Linux on my custom AP7001 board. The video sensor is the Micron MT9T001. When I compiled the kernel, I chose the bit-banging version of Atmel I2C support. I compiled the I2C as a module, and loaded it using the following:


modprobe i2c-core
modprobe i2c-dev
modprobe i2c-gpio

Under /dev, I noticed that the I2C module showed up as i2c-0. So far, so good. I wrote a program to try and read register 0x00. This is the register that gives the chip version of the part. Using the following code, I managed to detect the image sensor (i.e. "The camera could be found on the bus"), but I cannot read register 0x00 (an error occurs, but I am unable to determine the error because the debugger continues beyond the end of the program and does not stop after I attempt the read).

The Chip Version is listed in the datasheet as haing the default value of 0x1621. The datasheet also lists the default value in binary:

1011 0001 0000 0001

Here is the test program that I am using:

//Program to test the I2C bus
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{
	int file;

	//Note that the first forward slash tells me
		//that the device is relative to the root directory
	if( (file = open("/dev/i2c-0",O_RDWR)) < 0 )
	{
		printf("An error has occurred opening the I2C bus\n");
	}
	
	//Find a device with a certain address on the bus
	int addr = 0x5d;
	if(ioctl(file,I2C_SLAVE,addr) < 0)
	{
		printf("The camera could not be found on the bus\n");
		exit(1);
	}

	//Find a camera device on the bus
	printf("The camera could be found on the bus\n");
	
	//Obtain the chip version of the camera
	
	//Chip register
	char buf[3] = {0,0,0};
	buf[0] = 0x00;	//Read from register 0x00

	if(read(file,buf,2) != 2)
	{
		printf("Error reading from register\n");
	}
	
	printf("The value of the register is: %x\n", buf[0]);
	
	
	//Exit the program
	exit(0);
	
}//end main

Note that I am fond of using the double // for comments, but this is C code.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

nkinar wrote:
the register that gives the chip version of the part. Using the following code, I managed to detect the image sensor (i.e. "The camera could be found on the bus"),
Issuing the I2C_SLAVE ioctl makes sure no-one else is playing with that device but doesn't actually probe the bus for it. I don't know whether you knew that but it just sounded kinda like you though that the camera is in communication with the i2c but this is not necessarily the case.
scanlon wrote:
but I cannot read register 0x00 (an error occurs, but I am unable to determine the error because the debugger continues beyond the end of the program and does not stop after I attempt the read).
What debugger are you using? Can you just strace it? That'll give you the return code of the read at least.

int main()
{
	//Chip register
	char buf[3] = {0,0,0};
	buf[0] = 0x00;	//Read from register 0x00

buf[0] is being written to 0 twice but I guess that's OK.  And you're only reading 2 bytes so your buffer only need be 2 bytes long.  And those bytes should be overridden by the read call anyway so need not be initialized at all, so long as your error handling is up to scratch.

	if(read(file,buf,2) != 2)
	{
		printf("Error reading from register\n");
	}

So do you actually see any printf activity here?  Does adding some fflush() action after each printf call change the output you get?
...
	//Exit the program
	exit(0);
Not required, usually you'd just return 0, but it shouldn't be harmful.

Does a CRO/LA balanced on the TWI lines show you activity?

-S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Squidgit--

Thank you so much for your advice! To check the bus for the presence of my device, I tried downloading and compiling i2c-tools from the following website:

http://www.lm-sensors.org/wiki/I...

This could easily be compiled for AVR32 by modifying the makefile to use the avr32 gcc linux compiler. (Maybe you could add these tools to your package repository, Squidgit.) Probing the bus using the I2C tools, I found that there was no reply from the slave address.

Strangely enough, an oscilloscope showed that the SCK line went low (and stayed low) after the Linux kernel booted.

I am using the official version of Atmel buildroot to create the root image.

I am using gdbserver on my custom board. The error code returned from the i2c driver is 0x2bf8.

I have compiled the Atmel bit-banging I2C driver.

Could this problem be related to the fact that I have set up USART0 as a serial port? I am using USART0 as the debugging terminal for my custom board, and I have patched the clock for USART0. If this is the case, then could I switch over USART0 to use a bit-banging implementation?

OR...would another distro such as Octotux be more better suited for my situation?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

nkinar wrote:
http://www.lm-sensors.org/wiki/I...

This could easily be compiled for AVR32 by modifying the makefile to use the avr32 gcc linux compiler. (Maybe you could add these tools to your package repository, Squidgit.)

Good tip, will do.
nkinar wrote:
I am using gdbserver on my custom board. The error code returned from the i2c driver is 0x2bf8.
Exactly where did this code come from? It's an odd kind of code, usually they'll either be negative down to about -30 odd or just -1 on error.
nkinar wrote:
Could this problem be related to the fact that I have set up USART0 as a serial port?
I wouldn't have thought so. Check your boot logs and make sure there're no stack dumps in there from clashing pin requests. If there aren't then you're fine on that front.
nkinar wrote:
I am using USART0 as the debugging terminal for my custom board, and I have patched the clock for USART0. If this is the case, then could I switch over USART0 to use a bit-banging implementation?
There's no bit-banging USART too my knowledge, but the hardware works well as far as I've seen so I wouldn't blame that.
nkinar wrote:
OR...would another distro such as Octotux be more better suited for my situation?
I don't think the distro would help you out at all. The problem seems to be deeper than userland and as far as kernels go, the newer the better.

-S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Squidgit,

Thank you so much for your advice! I will try compiling a newer kernel. Old kernels dating back a year or so seem to have a number of difficulties when working with custom hardware. I'll try a newer kernel and see where this leads me. :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This thread looks like it has been dead for a while, but I am doing a senior project interfacing an MT9T001 with a Beagle Board under Linux.

I can load my device driver, but I cant seem to get an acknowledge bit from the sensor, and it cant read anything at the address (it reads 0xfff which makes sense if nothing ever pulls it down.)

Anyone know of any work around?

I have a SCL from LH running at 100KHz and a ~2.5MHz PIXCLK for the camera sensor (as per the timing diagrams on data sheet) and still no acknowledge bit :S

Any help?

--Daniel M.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rings a bell but can't quite place it at the moment...

Are you using the i2c-atmel driver or i2c-gpio? The -gpio one has a bit more overhead but works more reliably.

-S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd suggest to play with more simple i2c device like eeprom's to be sure that your i2c is working good. In my case I use 24LC256 chip.

Here is my output

Quote:

# lsmod
Module Size Used by Not tainted
i2c_dev 5476 0
i2c_gpio 2400 0
i2c_algo_bit 6500 1 i2c_gpio
i2c_core 17648 3 i2c_dev,i2c_gpio,i2c_algo_bit

# ./test_ee

00: 52 6f 6d 61 6e 44 4f 20 | RomanDO
08: 4e 4f 54 20 45 44 49 54 | NOT EDIT
10: 20 54 48 49 69 69 69 69 | THIiiii
18: 69 69 69 69 69 69 69 69 | iiiiiiii
20: 69 69 69 69 69 69 69 69 | iiiiiiii
28: 69 69 69 69 69 69 69 69 | iiiiiiii
30: 69 69 69 69 69 69 69 69 | iiiiiiii
38: 69 69 69 69 69 69 69 69 | iiiiiiii
40: 69 69 69 69 69 69 69 69 | iiiiiiii
48: 69 69 69 69 69 69 69 69 | iiiiiiii
#

Here's my code


#include 
#include 
#include "i2c-dev.h"


int main () {
    int device_file;
    char filename[32];
    char buffer[128];
    int i, j;

    sprintf(filename,"/dev/i2c-0");

    int slave_address = 0x50;

    if ((device_file = open(filename,O_RDWR)) < 0) {
        perror("I2C open");
        return -1;
    }

    if (ioctl(device_file,I2C_SLAVE,slave_address) < 0) {
        perror("ioctl I2C_SLAVE");
        return -1;
    }

    buffer[0] = 0;
    buffer[1] = 0;

    if (write(device_file,buffer,2) != 2) {
        perror("write");
        return -1;
    }

    if (read(device_file, buffer, 80) != 80) {
        perror("read");
        return -1;
    } else {
        for (i=0; i<80; i++) {
            if (!(i % 8))
                printf("\n%02x:", i);
            printf(" %02x ", buffer[i]);

            if (!((i+1) % 8)  ) {
                printf(" | ");
                for (j=0;j<8;j++) {
                    if (isprint( (int)buffer[i-7+j]) )
                        printf( "%c", buffer[i-7+j] );
                    else
                        printf(".");
                }
            }
        }
        printf("\n");
    }
}

I advise to use bit-banging i2c rather then atmel-twi one (I got wrong eeprom dump with atmel-twi).