I'm currently trying to read out the registers of the LTC2992 power monitor with a userspace application and did notice a behavior I can't really explain while reading out a register from the device.
So, after writing a value to a register on the LTC2992 either using a write()
syscall, i2cset
or I2C_RDWR
I can read back the value using i2cget
or I2C_RDWR
. However, when using the write()
and read()
syscall I only get 0 returned.
This is the code using write()
and read()
syscalls for reading a value
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
int main() {
...
int file = open(device, O_RDWR);
if (file < 0) {
perror("Failed to open I2C bus");
return 1;
}
if (ioctl(file, I2C_SLAVE, addr) < 0) {
perror("Failed to set I2C address");
close(file);
return 1;
}
if (write(file, ®, 1) != 1) {
perror("Failed to write register address");
close(file);
return 1;
}
if (read(file, &data, 1) != 1) {
perror("Failed to read from register");
close(file);
return 1;
}
close(file);
return 0;
}
And this is the code were I used I2C_RDWR
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <string.h>
int main() {
...
int file = open(device, O_RDWR);
if (file < 0) {
perror("Failed to open I2C device");
return 1;
}
struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[2];
messages[0].addr = addr;
messages[0].flags = 0;
messages[0].len = 1;
messages[0].buf = ®
messages[1].addr = addr;
messages[1].flags = I2C_M_RD;
messages[1].len = 1;
messages[1].buf = &data;
packets.msgs = messages;
packets.nmsgs = 2;
if (ioctl(file, I2C_RDWR, &packets) < 0) {
perror("I2C_RDWR ioctl failed");
close(file);
return 1;
}
close(file);
return 0;
}
To my understanding, reading back a value expects a repeated start condition, so my guess is that the first method may fail in generating this condition in that the write()
syscall may generate the following transaction
START -> [ADDR+W] -> [REG] -> STOP
and the read()
syscall this transaction
START -> [ADDR+R] -> [DATA] -> STOP
meaning there is a STOP condition before the next START condition.
However, in this tutorial someone uses similar method to the first one where he succeeds in reading out a value. Looking at the datasheet of the device he uses, the device also expects a repeated start condition. So, I'm not sure if my intuition is right here, or if this may be a timing issue, or if the two methods may have some crucial differences?
Looks like your device resets the address on a STOP. This is on page 19 of the datasheet. "A STOP condition resets the register address pointer to 0x00." The read() is always going to read from address 0 which happens to have a reset value of 0.