-
-
Save JamesDunne/9b7fbedb74c22ccc833059623f47beb7 to your computer and use it in GitHub Desktop.
#include <stdio.h> | |
#include <string.h> | |
#include <fcntl.h> | |
#include <linux/i2c-dev.h> | |
// Terrible portability hack between arm-linux-gnueabihf-gcc on Mac OS X and native gcc on raspbian. | |
#ifndef I2C_M_RD | |
#include <linux/i2c.h> | |
#endif | |
typedef unsigned char u8; | |
// Global file descriptor used to talk to the I2C bus: | |
int i2c_fd = -1; | |
// Default RPi B device name for the I2C bus exposed on GPIO2,3 pins (GPIO2=SDA, GPIO3=SCL): | |
const char *i2c_fname = "/dev/i2c-1"; | |
// Returns a new file descriptor for communicating with the I2C bus: | |
int i2c_init(void) { | |
if ((i2c_fd = open(i2c_fname, O_RDWR)) < 0) { | |
char err[200]; | |
sprintf(err, "open('%s') in i2c_init", i2c_fname); | |
perror(err); | |
return -1; | |
} | |
// NOTE we do not call ioctl with I2C_SLAVE here because we always use the I2C_RDWR ioctl operation to do | |
// writes, reads, and combined write-reads. I2C_SLAVE would be used to set the I2C slave address to communicate | |
// with. With I2C_RDWR operation, you specify the slave address every time. There is no need to use normal write() | |
// or read() syscalls with an I2C device which does not support SMBUS protocol. I2C_RDWR is much better especially | |
// for reading device registers which requires a write first before reading the response. | |
return i2c_fd; | |
} | |
void i2c_close(void) { | |
close(i2c_fd); | |
} | |
// Write to an I2C slave device's register: | |
int i2c_write(u8 slave_addr, u8 reg, u8 data) { | |
int retval; | |
u8 outbuf[2]; | |
struct i2c_msg msgs[1]; | |
struct i2c_rdwr_ioctl_data msgset[1]; | |
outbuf[0] = reg; | |
outbuf[1] = data; | |
msgs[0].addr = slave_addr; | |
msgs[0].flags = 0; | |
msgs[0].len = 2; | |
msgs[0].buf = outbuf; | |
msgset[0].msgs = msgs; | |
msgset[0].nmsgs = 1; | |
if (ioctl(i2c_fd, I2C_RDWR, &msgset) < 0) { | |
perror("ioctl(I2C_RDWR) in i2c_write"); | |
return -1; | |
} | |
return 0; | |
} | |
// Read the given I2C slave device's register and return the read value in `*result`: | |
int i2c_read(u8 slave_addr, u8 reg, u8 *result) { | |
int retval; | |
u8 outbuf[1], inbuf[1]; | |
struct i2c_msg msgs[2]; | |
struct i2c_rdwr_ioctl_data msgset[1]; | |
msgs[0].addr = slave_addr; | |
msgs[0].flags = 0; | |
msgs[0].len = 1; | |
msgs[0].buf = outbuf; | |
msgs[1].addr = slave_addr; | |
msgs[1].flags = I2C_M_RD | I2C_M_NOSTART; | |
msgs[1].len = 1; | |
msgs[1].buf = inbuf; | |
msgset[0].msgs = msgs; | |
msgset[0].nmsgs = 2; | |
outbuf[0] = reg; | |
inbuf[0] = 0; | |
*result = 0; | |
if (ioctl(i2c_fd, I2C_RDWR, &msgset) < 0) { | |
perror("ioctl(I2C_RDWR) in i2c_read"); | |
return -1; | |
} | |
*result = inbuf[0]; | |
return 0; | |
} |
Thanks for this example.
A question:
Is the I2C_M_NOSTART
in msgs[1].flags = I2C_M_RD | I2C_M_NOSTART;
needed ?
I don't understand the explanation that I read from the document.
I guess I2C_M_NOSTART
usage depends on your use case.
https://www.kernel.org/doc/Documentation/i2c/i2c-protocol
I2C_M_NOSTART:
In a combined transaction, no 'S Addr Wr/Rd [A]' is generated at some
point. For example, setting I2C_M_NOSTART on the second partial message
generates something like:
S Addr Rd [A] [Data] NA Data [A] P
If you set the I2C_M_NOSTART variable for the first partial message,
we do not generate Addr, but we do generate the startbit S. This will
probably confuse all other clients on your bus, so don't try this.
This is often used to gather transmits from multiple data buffers in
system memory into something that appears as a single transfer to the
I2C device but may also be used between direction changes by some
rare devices.
It worked for the particular I2C device I was talking to and there were no other I2C devices on the bus. It's probably better to remove it in general.
Hi..
I am new to this ioctl calling..
in the below function declaration
int i2c_read(u8 slave_addr, u8 reg, u8 *result)
what is reg here..If it is register then how do we find out this register for some i2c?
Yes, reg
is the register number to write to or read from. Which register number you want is documented by your I2C device you're talking to and what you're trying to achieve. It's device specific.
I've put
memset(msgs, 0, sizeof(msgs));
memset(msgset, 0, sizeof(msgset));
after declaration, to prevent valgrind from running wild for each call. Hope, it is not that wrong.
Hi,
It is possible to write and read in one operation ioctl(i2c_fd, I2C_RDWR, &msgset)?
I mean, write value to reg and read it to verify it was written correctly.
How to get the reg address for the i2c slave device?
I've just singed in to github to thank you Sr. this helped me a lot. I was using the example below but it did not work.
if (read(file, buf, 2) != 2) {
/* ERROR HANDLING: i2c transaction failed
std::cout << "Failed to Read Address " << std::endl;
}
It was just until i read this post that I realized that I was doing something something wrong. Now the communication with the peripheral from my beaglebone black seems to be working.
This code works very well. Thank you for sharing with us
I get erroes:
--Multiple definition of 'i2c_fd'
--Multiple definition of 'i2c_fname'
How can i fix it?..
Thank you for the code. It just worked on my Raspberry 4 first time. Code is a nice simple Read/Write and it straight forward.
https://www.kernel.org/doc/Documentation/i2c/dev-interface
Flags are documented there. For example: