Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading / writing from using I2C on Linux

Tags:

c

linux

ioctl

i2c

I'm trying to read/write to a FM24CL64-GTR FRAM chip that is connected over a I2C bus on address 0b 1010 011.

When I'm trying to write 3 bytes (data address 2 bytes, + data one byte), I get a kernel message ([12406.360000] i2c-adapter i2c-0: sendbytes: NAK bailout.), as well as the write returns != 3. See code below:

#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>

int file;
char filename[20];
int addr = 0x53; // 0b1010011; /* The I2C address */
uint16_t dataAddr = 0x1234;
uint8_t val = 0x5c;
uint8_t buf[3];

sprintf(filename,"/dev/i2c-%d",0);
if ((file = open(filename,O_RDWR)) < 0)
    exit(1);

if (ioctl(file,I2C_SLAVE,addr) < 0)
    exit(2);

buf[0] = dataAddr >> 8;
buf[1] = dataAddr & 0xff;
buf[2] = val;

if (write(file, buf, 3) != 3)
    exit(3);

...

However when I write 2 bytes, then write another byte, I get no kernel error, but when trying to read from the FRAM, I always get back 0. Here is the code to read from the FRAM:

uint8_t val;

if ((file = open(filename,O_RDWR)) < 0)
    exit(1);

if (ioctl(file,I2C_SLAVE,addr) < 0)
    exit(2);

if (write(file, &dataAddr, 2) != 2) {
    exit(3);

if (read(file, &val, 1) != 1) {
    exit(3);

None of the functions return an error value, and I have also tried it with:

#include <linux/i2c.h>

struct i2c_rdwr_ioctl_data work_queue;
struct i2c_msg msg[2];
uint8_t ret;

work_queue.nmsgs = 2;
work_queue.msgs = msg;

work_queue.msgs[0].addr = addr;
work_queue.msgs[0].len = 2;
work_queue.msgs[0].flags = 0;
work_queue.msgs[0].buf = &dataAddr;

work_queue.msgs[1].addr = addr;
work_queue.msgs[1].len = 1;
work_queue.msgs[1].flags = I2C_M_RD;
work_queue.msgs[1].buf = &ret;

if (ioctl(file,I2C_RDWR,&work_queue) < 0)
    exit(3);

Which also succeeds, but always returns 0. Does this indicate a hardware issue, or am I doing something wrong?

Are there any FRAM drivers for FM24CL64-GTR over I2C on Linux, and what would the API be? Any link would be helpful.

like image 514
TheSeeker Avatar asked Feb 02 '09 21:02

TheSeeker


People also ask

How do I read an I2C device in Linux?

Each registered i2c adapter gets a number, counting from 0. You can examine /sys/class/i2c-dev/ to see what number corresponds to which adapter. Alternatively, you can run "i2cdetect -l" to obtain a formatted list of all i2c adapters present on your system at a given time. i2cdetect is part of the i2c-tools package.

How does I2C work in Linux?

I2C is a multi-master bus; open drain signaling is used to arbitrate between masters, as well as to handshake and to synchronize clocks from slower clients. The Linux I2C programming interfaces support the master side of bus interactions and the slave side.

What is I2C client?

I2C is a protocol for communication between devices. In this column, the author takes the reader through the process of writing I2C clients in Linux. I2C is a multi-master synchronous serial communication protocol for devices. All devices have addresses through which they communicate with each other.


2 Answers

I do not have experience with that particular device, but in our experience many I2C devices have "quirks" that require a work-around, typically above the driver level.

We use linux (CELinux) and an I2C device driver with Linux as well. But our application code also has a non-trivial I2C module that contains all the work-around intelligence for dealing with all the various devices we have experience with.

Also, when dealing with I2C issues, I often find that I need to re-acquaint myself with the source spec:

http://www.nxp.com/acrobat_download/literature/9398/39340011.pdf

as well as the usage of a decent oscilloscope.

Good luck,

Above link is dead, here are some other links:

http://www.nxp.com/documents/user_manual/UM10204.pdf and of course wikipedia: http://en.wikipedia.org/wiki/I%C2%B2C

like image 125
Matthew Eshleman Avatar answered Oct 03 '22 04:10

Matthew Eshleman


The NAK was a big hint: the WriteProtect pin was externally pulled up, and had to be driven to ground, after that a single write of the address followed by data-bytes is successful (first code segment).

For reading the address can be written out first (using write()), and then sequential data can be read starting from that address.

like image 43
TheSeeker Avatar answered Oct 03 '22 03:10

TheSeeker