2

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.

Dogus Ural
  • 583
  • 1
  • 9
  • 20
  • 1
    Can the device tolerate reading more bytes than is indicated by the first byte? – Ian Abbott Mar 20 '19 at 16:13
  • 2
    This looks like an SMBus Block Read transaction. Can you use `i2c_smbus_read_block_data` from **libi2c** (part of **i2c-tools**)? – Ian Abbott Mar 20 '19 at 16:48
  • It expects to be read the exact number of bytes indicated in its first message byte, else it returns error. – Dogus Ural Mar 21 '19 at 09:12
  • s32 i2c_smbus_read_block_data ( const struct i2c_client * client, u8 command, u8 * values); The thing is , I just do a read request to my slave address with a predefined command structure. I dont know what to do with command and *values parameters. – Dogus Ural Mar 21 '19 at 09:14
  • 1
    `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 Mar 21 '19 at 11:15
  • 1
    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 Mar 21 '19 at 11:48
  • 1
    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 Mar 21 '19 at 11:52
  • When I do , i2cdetect -F 1 it returns : SMBus Block Read no, I doubt using smbus functions will solve my problems. Thanks for trying tho. It is a shame raspberry 3 doesnt support this. – Dogus Ural Mar 21 '19 at 13:23
  • I also checked external libs like pigpio, but their block read functions have a register parameter which I dont need at all. int i2cWriteI2CBlockData(unsigned handle, unsigned i2cReg, char *buf, unsigned count) .Do you think I can use the function without providing a register ? – Dogus Ural Mar 21 '19 at 14:42
  • 1
    Yes, you are correct. It uses "emulated" SMBus transfers, but it doesn't advertise all possible emulated SMBus transfer types, probably because not all of them can be emulated using the RPi's I2C controller. – Ian Abbott Mar 21 '19 at 16:15
  • 1
    Would it be possible to read just the length byte (followed by a NACK and STOP), and then repeat the whole thing from scratch once you know how long the response is supposed to be? – Ian Abbott Mar 21 '19 at 16:18
  • Then I would have made an extra transfer, the IC expects me to do whole thing in one transfer. – Dogus Ural Mar 21 '19 at 16:23
  • 1
    It will be an extra transfer, but it will have had an I2C STOP before the START of the extra transfer. So what I mean is do a transfer that sends the PCB byte and receives the length byte (`S Addr Wr [A] PCB [A] Sr Addr Rd [LEN] NA P`), then do a completely separate transfer that sends the same PCB but receives LEN+1 bytes (e.g. if LEN was 3, receive 4 bytes: `S Addr Wr [A] PCB [A] Sr Addr Rd [LEN] A [D1] A [D2] A [D3] NA P`). Whether it will work will depend on whether the received LEN is the same for the initial, incomplete transfer, and for the final, complete transfer. – Ian Abbott Mar 21 '19 at 16:35
  • 1
    Looking at AN12207, my suggestion wouldn't work due to the slave raising a "Protocol Exception". – Ian Abbott Mar 21 '19 at 17:05
  • 1
    I think in theory, pigpio's bbI2CZip could be extended to support GPIO bit-banged SMBus Block Read transfers, but it doesn't support it at the moment. The bit-banging would also impact system performance. I wonder if there are any USB I2C adapters that support SMBus block transfers? – Ian Abbott Mar 21 '19 at 17:35

2 Answers2

1

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.

Dogus Ural
  • 583
  • 1
  • 9
  • 20
1

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);
fadedbee
  • 42,671
  • 44
  • 178
  • 308