4

I'm willing to send 20 bytes at a time to Arduinos via I2C bus. I'm writing a C program (that will become a Daemon) that should achieve this, and thus I downloaded via Git the last wiringPi library, and it is working great when sending one byte of data to the Arduino :

wiringPiI2CWrite(0x77, 0x40);

Sends byte 0x40 to the slave device with address 0x77.

Then it comes to sending more than a byte. The following works great :

wiringPiI2CWriteReg8(0x77, 0x40, 0x01);

Sends bytes 0x40 then 0x01 to the slave device with address 0x77.

wiringPiI2CWriteReg16(0x77, 0x40, 0xFD01);

Sends bytes 0x40 then 0x01 then 0xFD to the slave device with address 0x77.

Now I went into the wiringPi source code, in wiringPiI2C.c, and based on what I saw, added a new function :

static int WriteBigData (int fd, uint8_t* b)
{
  union i2c_smbus_data data;
  int i;
  for(i = 0; i < 19)
    data.block[i] = b[i + 1];

  return i2c_smbus_access(fd, I2C_SMBUS_WRITE, b[0], 19, &data);
}

Which would result in fact in :

static int WriteBigData (int fd, uint8_t* b)
{
  union i2c_smbus_data data;
  int i;
  for(i = 0; i < 19)
    data.block[i] = b[i + 1];

  struct i2c_smbus_ioctl_data args ;
  args.read_write = I2C_SMBUS_WRITE ;
  args.command    = b[0] ;
  args.size       = 19 ;
  args.data       = data ;
  return ioctl (fd, I2C_SMBUS, &args) ;
}

But when executing, Arduino gets nothing. If I change size to 5, Arduino receives 3 bytes (Command + 2 first data bytes), if I change size to 8, Arduino receives 2 bytes (Command + first data byte). If I change size to 10, Arduino receives nothing.

So I'm wondering, may understand that Raspberry Pi's I2C is in fact the SMBus version of I2C ? Are there any limitations ? I also found that SMBus has limitation of 32 data bytes, which should be enough for my 20 bytes ...

Is this a limitation of the Linux (Raspbian) driver for I2C ?

Did anyone manage to send more than 2 data bytes over I2C from a Raspberry Pi ?

(Just to clarify if the detail is needed : I use a Model 1 Raspberry Pi B Rev. 2)

Sierramike
  • 43
  • 1
  • 4

2 Answers2

5

I'm wondering, may understand that Raspberry Pi's I2C is in fact the SMBus version of I2C ?

I don't think it is limited in that sense but that is the normative way to use it. There is no such limitation with the kernel, since the protocol docs for the interface say, "If you write a driver for some I2C device, please try to use the SMBus commands if at all possible [...] This makes it possible to use the device driver on both SMBus adapters and I2C adapters". Since the Pi is described as having an I2C bus, I would assume that is the case.

In any case, it is not so relevant to your problem. Also in that doc you will find reference to two functions from the API:

  • The i2c_smbus_write_i2c_block_data() function, which is "supported by the SMBus layer ... [but] not defined by the SMBus specification". The length parameter is 8-bits, but internally truncated to <= 32. That includes the length itself in the first byte, although the difference between this function and the next one is that may or may not actually be sent. The complete signature and definition is in the header (/usr/include/linux/i2c-dev.h) if you have the correct version installed; I have a description of how to do that here.

  • The i2c_smbus_write_block_data() function (notice the subtle difference), which is exactly the same except for one of the internal parameters used (this would be the one that does send the length). I am not sure of the significance of this, but if one does not do what you expect, try the other one. Your data sheet should should be explicit about exactly what form requests should take.

It is also possible to use plain read() and write() on the handles; in this case the first byte should function as the register address.


By the way, contra joan, I would strongly encourage you to not use wiringPi or any other pi specific library for this, and instead use the linux kernel interface. For some reason, there is a trend with the pi to intentionally write non-portable code, which is the opposite of what would normally be considered best practice. It is easy to say now, "Well I am only ever going to be using this on a raspberry pi anyway," but if at some point you get another board/device/whatever with I2C pins running linux (there are many such things), code using the kernel dev API will work without modification. Code written using wiringPi will not. Generally there is no purpose to using such libraries for I2C. I would guess the reason it is there at all, at least in the wiringPi case, is because the library was intended to mimic an Arduino library, making it familiar for novice programmers coming from there. Judging by your examination of the source in the question, it is built on ioctl(), meaning it is just using the kernel interface anyway!

goldilocks
  • 58,859
  • 17
  • 112
  • 227
  • You are right, wiringPi seems to copy/paste a lot from i2c_dev.h, omitting some functionalities. I got everything working by setting the length to I2C_SMBUS_I2C_BLOCK_DATA (length is actually not self explanatory). The actual length is to be set as the first byte of data. Data should contain "count" followed by "count" bytes of data. Using I2C_SMBUS_I2C_BLOCK_DATA, only the data bytes are sent. Using I2C_SMBUS_BLOCK_DATA, the whole bunch is sent : the "count" byte then all data. – Sierramike Jan 25 '16 at 10:36
  • !! Nice catch. I've corrected that to mention both corresponding i2c-dev API functions above. – goldilocks Jan 25 '16 at 13:49
3

It's a limitation of wiringPi. It only implements a couple of SMBus commands.

As you say using SMBus commands you are limited to 32 bytes.

Using the raw device you can send hundreds at a time. I'm not sure what limit, if any, is imposed by the kernel driver.

Personally I'd just use the raw device (/dev/i2c-1) as it's much simpler and more flexible.

If you want to use SMBus you probably ought to use the SMBus extension Write I2C Block Data which I implement as follows.

   union my_smbus_data data;

   int i, status;

   /* Unneeded detail removed */

   for (i=1; i<=count; i++) data.block[i] = buf[i-1];

   data.block[0] = count;

   status = my_smbus_access(
            i2cInfo[handle].fd,
            PI_I2C_SMBUS_WRITE,
            reg,
            PI_I2C_SMBUS_I2C_BLOCK_BROKEN,
            &data);
joan
  • 71,024
  • 5
  • 73
  • 106
  • You gave me the right direction with "count" set as first byte, but the constant is I2C_SMBUS_I2C_BLOCK_DATA. Anyway, goldilocks was right, it is best not to use wiringPi as it is a copy/paste of the standard i2c_dev.h with lots of things missing. – Sierramike Jan 25 '16 at 10:39