2
votes

I am trying to interface A71CH with raspberry PI 3 over i2c, the device requires repeated starts and when a read request is made the first byte the device sends, is always the length of the whole message. When I am trying to make a read, instead of reading a fixed sized message , I want to read the first byte then send NACK signal to the slave after certain amount of bytes have been received that is indicated with the first byte. I used to following code but could not get the results I expected because it only read one byte than sends a NACK signal as you can see below.

struct i2c_rdwr_ioctl_data packets;
struct i2c_msg messages[2];
int r = 0;
int i = 0;

if (bus != I2C_BUS_0) // change if bus 0 is not the correct bus
{
    printf("axI2CWriteRead on wrong bus %x (addr %x)\n", bus, addr);
}

messages[0].addr  = axSmDevice_addr;
messages[0].flags = 0;
messages[0].len   = txLen;
messages[0].buf   = pTx;

// NOTE:
// By setting the 'I2C_M_RECV_LEN' bit in 'messages[1].flags' one ensures
// the I2C Block Read feature is used.
messages[1].addr  = axSmDevice_addr;
messages[1].flags = I2C_M_RD | I2C_M_RECV_LEN|I2C_M_IGNORE_NAK;
messages[1].len   = 256;
messages[1].buf   = pRx;
messages[1].buf[0] = 1;

// NOTE:
// By passing the two message structures via the packets structure as
// a parameter to the ioctl call one ensures a Repeated Start is triggered.
packets.msgs      = messages;
packets.nmsgs     = 2;

// Send the request to the kernel and get the result back
r = ioctl(axSmDevice, I2C_RDWR, &packets);

Signals

Is there any way that allows me to make variable sized i2c reads ? What can I do to make it work ? Thanks for looking.

2
Can the device tolerate reading more bytes than is indicated by the first byte?Ian Abbott
This looks like an SMBus Block Read transaction. Can you use i2c_smbus_read_block_data from libi2c (part of i2c-tools)?Ian Abbott
command is the command byte value you need to send in the transmit part of the transaction. values is a pointer to a 32 byte region to receive the part of the response after the first "count" byte. The function returns the value of the "count" byte (the length of data returned in the values buffer) on success, or a negative error value on error.Ian Abbott
Since you called ioctl, I'm assuming you are doing this from user-level. Your comment above containing the prototype of i2c_smbus_read_block_data is for the kernel API. The user-space API from libi2c has __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 * values);. file is the file descriptor from opening the /dev/i2c-n device. Your original code passes the I2C slave address in the transaction, but for SMBus messages you need to set the I2C slave address first using the I2C_SLAVE ioctl command.Ian Abbott
It's possible to do it without libi2c and call the ioctls directly. Check the source code for i2c-tools to see what it does. It's a fairly simple wrapper around the ioctls.Ian Abbott

2 Answers

1
votes

Raspbery doesn't support SMBUS Block Reads, only way to overcome this is to do bitbanging on GPIO pins. As @Ian Abbott mentioned above, I managed to modify bbI2CZip function to fit my need by checking the first byte of the received message and updating the read length afterwards.

1
votes

I had a similar issue with the rpi3. I wanted to read exactly 32 bytes of data from a register on a slave device, but i2c_smbus_read_block_data() was returning -71 and errno 71 EPROTO.

The solution was to use i2c_smbus_read_i2c_block_data() instead of i2c_smbus_read_block_data().

/* Until kernel 2.6.22, the length is hardcoded to 32 bytes. If you
   ask for less than 32 bytes, your code will only work with kernels
   2.6.23 and later. */
extern __s32 i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length,
                                           __u8 *values);