munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 11:52 am

Hi,

I have a python program which needs to read the power, voltage and current from a LTC2945 chip. I use read_i2c_block_data to read the registers of the device but it doesn't work as expected. When i use

Code: Select all

read_i2c_block_data(0x6f, 0x05, 3)
to read the power registers I get

Code: Select all

[0x5, 0x0, 0x0]
returned. Same thing happens when I use

Code: Select all

read_i2c_block_data(0x6f, 0x14, 2)
or

Code: Select all

read_i2c_block_data(0x6f, 0x1e, 2)
to read the current and voltage registers (Off course it only returns two bytes then). It seems that no matter what I use as the first register value it always starts reading from register 0x00. I verified that with using

Code: Select all

read_i2c_block_data(0x6f, 0x05, 32)
and then it returns

Code: Select all

[0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x46, 0x0, 0x25, 0x80, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x60, 0x1, 0x40, 0x0, 0x0, 0xff, 0xf0, 0x0, 0x0, 0x1e, 0x10]

As you can see the values I need are in the right registers

Code: Select all

[0x0, 0xb, 0x46]
for the power in this example. What am I doing wrong?

thanks,
Martijn

User avatar
joan
Posts: 15090
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 12:37 pm

I2C devices don't have to obey arbitrary I2C commands. Does the device datasheet say the SMBUS read_i2c_block_data command is supported?

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 12:49 pm

Not explicitly but I have example code which runs on the Linduino device from Linear.

Code: Select all

// Reads a 12-bit adc_code from LTC2945
int8_t LTC2945_read_12_bits(uint8_t i2c_address, uint8_t adc_command, uint16_t *adc_code)
// The function returns the state of the acknowledge bit after the I2C address write. 0=acknowledge, 1=no acknowledge.
{
  // Use union type defined in Linduino.h to combine two uint8_t's (8-bit unsigned integers) into one uint16_t (unsigned 16-bit integer)
  // Then, shift by 4 bits and return in *adc_code
  int32_t ack;
  
  ack = i2c_read_word_data(i2c_address, adc_command, adc_code);

  *adc_code >>= 4;
  return ack;
}

// Reads a 24-bit adc_code from LTC2945
int8_t LTC2945_read_24_bits(uint8_t i2c_address, uint8_t adc_command, int32_t *adc_code)
// The function returns the state of the acknowledge bit after the I2C address write. 0=acknowledge, 1=no acknowledge.
{
  int8_t ack;
  
  LT_union_int32_4bytes data;
  
  ack = i2c_read_block_data(i2c_address, adc_command, (uint8_t)3, data.LT_byte);
  
  *adc_code = 0x0FFFFFF & data.LT_int32;
  return(ack);
}
The code is also available on http://www.linear.com/product/LTC2945 under the Linduino section.

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 1:02 pm

BTW my code is on GitHub https://github.com/munnik/ShipControl

For this question these files a relevant:
Also the LTC2945 must support something like read_i2c_block_data because it has 50 registers.

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 1:27 pm

I even tried this code:

Code: Select all

    def read_block(self, registers):
        read_bytes = []

        for register in registers:
            sleep(0.1)
            self._bus.write_byte(self._address, register)
            sleep(0.1)
            read_bytes.append(self._bus.read_byte(self._address))

        logger.info(
            'Read [{0}] from address {1:#x} and register [{2}]'.format(
                ', '.join(hex(c) for c in read_bytes),
                self._address,
                ', '.join(hex(c) for c in registers)
            )
        )
        return read_bytes
And I got:

Code: Select all

Read [0x5, 0x5] from address 0x6f and register [0x1e, 0x1f]

User avatar
joan
Posts: 15090
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 1:40 pm

Personally I would use my pigpio library and the pigs utility and try variations from the command line. All the I2C command variants are supported. It may be the quickest way to find the call you actually need.

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 1:55 pm

Thanks for your help but unfortunately it doesn't give better results:

Code: Select all

pi@raspberrypi:~/PIGPIO $ pigs i2co 1 0x6f 0
0
pi@raspberrypi:~/PIGPIO $ pigs i2cri 0 0x05 3
3 5 0 0
pi@raspberrypi:~/PIGPIO $ pigs i2cri 0 0x05 3
3 5 0 0
pi@raspberrypi:~/PIGPIO $ pigs i2cri 0 0x05 32
32 5 0 0 0 0 0 3 194 0 37 128 0 0 0 255 255 255 0 0 0 0 32 1 64 0 0 255 240 0 0 30 16
pi@raspberrypi:~/PIGPIO $ pigs i2crk 0 0x05
ERROR: SMBus command not supported by driver
-107
pi@raspberrypi:~/PIGPIO $ pigs i2crb 0 0x05
5
pi@raspberrypi:~/PIGPIO $ pigs i2crw 0 0x05
5

User avatar
joan
Posts: 15090
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 2:03 pm

Try something like

pigs i2cwd 0 5 i2crd 0 3

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 2:20 pm

I have found that Linear defined the method i2c_read_block_data:

Code: Select all

// Read a block of data, starting at register specified by "command" and ending at (command + length - 1)
int8_t i2c_read_block_data(uint8_t address, uint8_t command, uint8_t length, uint8_t *values)
{
  uint8_t i = (length-1);
  int8_t ret = 0;

  if (i2c_start()!=0)                                //I2C START
    return(1);                                  //Stop and return 0 if START fail
  ret |= i2c_write((address<<1)|I2C_WRITE_BIT);       //Write 7-bit address with W bit
  ret|= i2c_write(command);                           //Write 8 bit command word
  if (i2c_start()!=0)                                 //I2C repeated START
  {
    i2c_stop();                                 //Attempt to issue I2C STOP
    return(1);                                  //Stop and return 0 if START fail
  }
  ret |= i2c_write((address<<1)|I2C_READ_BIT);        //Write 7-bit address with R bit

  if (ret!=0)   //If NACK return 1
  {
    i2c_stop();                         //I2C STOP
    return(1);
  }
  while (i>0)                         //Begin read loop
  {
    values[i] = i2c_read(WITH_ACK); //Read from bus with ACK
    i--;
  }

  values[0] = i2c_read(WITH_NACK);    //Read from bus with NACK for the last one;

  i2c_stop();                         //I2C STOP


  return(0);                           // Success!
}
So I tried to mimic that in python:

Code: Select all

    def read_block_ltc(self, registers):
        registers = self._prepare_for_i2c(registers)

        self._bus.write_byte(self._address, registers[0])
        read_bytes = []
        for _ in registers:
            read_bytes.append(self._bus.read_byte(self._address))
        logger.info(
            'Read [{0}] from address {1:#x} and register [{2}]'.format(
                ', '.join(hex(c) for c in read_bytes),
                self._address,
                ', '.join(hex(c) for c in registers)
            )
        )
        return read_bytes
Still same result...

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 2:21 pm

joan wrote:Try something like

pigs i2cwd 0 5 i2crd 0 3

Code: Select all

pi@raspberrypi:~/PIGPIO $ pigs i2cwd 0 5 i2crd 0 3
3 5 0 0

User avatar
joan
Posts: 15090
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 2:23 pm

If it does require repeated starts it's probably simplest to bit bang (as the standard driver doesn't really support repeated starts).

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 2:24 pm

Getting 32 bytes and turning the potentiometer to change the current/power

Code: Select all

pi@raspberrypi:~/PIGPIO $ pigs i2cwd 0 5 i2crd 0 32
32 5 0 0 0 0 0 3 194 0 37 128 0 0 0 255 255 255 0 0 0 0 32 1 64 0 0 255 240 0 0 30 16
pi@raspberrypi:~/PIGPIO $ pigs i2cwd 0 5 i2crd 0 32
32 5 0 0 0 0 0 11 70 0 37 128 0 0 0 255 255 255 0 0 0 0 96 1 64 0 0 255 240 0 0 30 16
pi@raspberrypi:~/PIGPIO $ pigs i2cwd 0 5 i2crd 0 32
32 5 0 0 0 0 0 35 160 0 37 128 0 0 0 255 255 255 0 0 0 1 48 1 64 0 0 255 240 0 0 30 0
pi@raspberrypi:~/PIGPIO $ pigs i2cwd 0 5 i2crd 0 32
32 5 0 0 0 0 0 5 163 0 37 128 0 0 0 255 255 255 0 0 0 0 48 1 64 0 0 255 240 0 0 30 16
pi@raspberrypi:~/PIGPIO $ pigs i2cwd 0 5 i2crd 0 32
32 5 0 0 0 0 0 1 225 0 37 128 0 0 0 255 255 255 0 0 0 0 16 1 64 0 0 255 240 0 0 30 16

User avatar
joan
Posts: 15090
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 2:31 pm

munnik wrote:Getting 32 bytes and turning the potentiometer to change the current/power
...
I'm not sure if that is good or bad. Does it show anything useful?

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 2:58 pm

Well I haven't checked the values but as you can see the values for 0x05, 0x06 and 0x07 are changing (0x05 stays 0 but circuit is very low power).

pi@raspberrypi:~/PIGPIO $ pigs i2cwd 0 5 i2crd 0 32
32 5 0 0 0 0 0 37 128 0 37 128 0 0 0 255 255 255 0 0 0 1 64 1 64 0 0 255 240 0 0 30 0

[0 37 128] = 0 << 16 + 37 << 8 + 128 = 9600
POWER_LSB = 6.25305E-07
6.002928 mW

[1 64] = 1 << 4 + 64 >> 4 = 20
SHUNT_VOLTAGE_LSB = 2.5006105E-05
SHUNT_RESISTANCE = .02
25.006105 mA

[30 0] = 30 << 4 + 0 >> 4 = 480
INPUT_VOLTAGE_LSB = 2.5006105E-02
12.0029304 V

The load was 470 Ohm so these values seem to be correct (there is something wrong with the power, not sure why but that's another issue)

User avatar
joan
Posts: 15090
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 3:22 pm

If the problem is repeated starts the following may work

pigs bi2co 2 3 50000 # open 2/3 for bit banging I2C

pigs bi2cz 2 0x04 0x6f 0x02 0x07 0x01 0x05 0x02 0x06 0x03 0x03 0x00

Code: Select all

Explanation

Set address 0x6f (0x04 0x6f)
start (0x02)
write 0x05 (0x07 0x01 0x05)
re-start (0x02),
read 3 bytes (0x06 0x03)
stop (0x03)
End (0x00)

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 4:20 pm

For that I need to connect the device to GPIO pin 2 and 3 of the raspberry I assume?

User avatar
joan
Posts: 15090
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 4:37 pm

No. The script uses GPIO 2 and 3 which you are already using, so you shouldn't need to change any wiring. It will stop the I2C bus working but that will be restored if you use bi2cc 2 after the test.

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 5:47 pm

That gives the expected result!

Code: Select all

pi@raspberrypi:~/PIGPIO $ pigs bi2co 2 3 50000
pi@raspberrypi:~/PIGPIO $ pigs bi2cz 2 0x04 0x6f 0x02 0x07 0x01 0x05 0x02 0x06 0x03 0x03 0x00
3 0 5 163
The question is off course, how does this translate to python code? I see have a python module, I'll go and experiment with it!

User avatar
joan
Posts: 15090
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: read_i2c_block_data doesn't work as expected

Fri Dec 18, 2015 6:10 pm

munnik wrote: ...
The question is off course, how does this translate to python code? I see have a python module, I'll go and experiment with it!
That does suggest it is a repeated start problem. http://abyz.co.uk/rpi/pigpio/python.html#bb_i2c_zip does the equivalent of pigs bi2cz.

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Sat Dec 19, 2015 10:26 am

joan wrote:I2C devices don't have to obey arbitrary I2C commands. Does the device datasheet say the SMBUS read_i2c_block_data command is supported?
On page 15 of the datasheet http://cds.linear.com/docs/en/datasheet/2945fb.pdf
I2C Interface
The LTC2945 includes an I2C/SMBus-compatible interface to provide access to the onboard registers. Figure 5 shows a general data transfer format using the I2C bus. The LTC2945 is a read-write slave device and supports the SMBus Read Byte, Write Byte, Read Word and Write Word protocols. The LTC2945 also supports extended Read and Write commands that allow reading or writing more than two bytes of data. When using the Read/Write Word or extended Read and Write commands, the bus master issues an initial register address and the internal register address pointer automatically increments by 1 after each byte of data is read or written. After the register address reaches 31h, it will roll over to 00h and continue incrementing. A Stop condition resets the register address pointer to 00h. The data formats for the above commands are shown in Figures 6 to 11.
So I would guess that my original code in this post should work as expected? Is this a bug in the Linear device, I'm not an I2C expert so I'm not sure.

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Sat Dec 19, 2015 10:36 am

With the pigpio module it now works as expected (Thank you very much for your help Joan!). I still have some issues but I'm not sure if this is my own fault. For example this assert fails https://github.com/munnik/ShipControl/b ... or.py#L306. And reading back the values from MIN_CURRENT_REGISTER and MAX_CURRENT_REGISTER return values which differ from what I have set them to.

Code: Select all

self._hardware_device.read_block(self.MIN_CURRENT_REGISTER) = [112, 0]
self._hardware_device.read_block(self.MAX_CURRENT_REGISTER) = [240, 240]
And I use these commands to write the values

Code: Select all

[4, 111, 2, 7, 1, 28, 2, 7, 2, 0, 112, 3, 0]
[4, 111, 2, 7, 1, 26, 2, 7, 2, 0, 240, 3, 0]
So I would have expected to get [0, 122] and [0, 240] from the read_block methods. The read_block method works correct when I use it to retrieve the current, power and voltage.

User avatar
joan
Posts: 15090
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: read_i2c_block_data doesn't work as expected

Sat Dec 19, 2015 10:47 am

munnik wrote: ...
I2C Interface
The LTC2945 includes an I2C/SMBus-compatible interface to provide access to the onboard registers. Figure 5 shows a general data transfer format using the I2C bus. The LTC2945 is a read-write slave device and supports the SMBus Read Byte, Write Byte, Read Word and Write Word protocols. The LTC2945 also supports extended Read and Write commands that allow reading or writing more than two bytes of data. When using the Read/Write Word or extended Read and Write commands, the bus master issues an initial register address and the internal register address pointer automatically increments by 1 after each byte of data is read or written. After the register address reaches 31h, it will roll over to 00h and continue incrementing." A Stop condition resets the register address pointer to 00h. The data formats for the above commands are shown in Figures 6 to 11.
So I would guess that my original code in this post should work as expected? Is this a bug in the Linear device, I'm not an I2C expert so I'm not sure.
From memory it's more of a bug in the Pi's I2C software driver.

The problem is the handling of repeated starts.

The driver is sending start address (0x6F) write (5) stop start address (0x6f) read ... stop, it needs to send start address (0x6F) write (5) start read ... stop.

From the Linear datasheet "A Stop condition resets the register address pointer to 00h.". It's that superfluous stop which causes the problem.

The I2C module does have a combined flag which you can try setting. That is meant to permit repeated starts. I have not had reliable results when I tried using it, quite possibly my mistake.

munnik
Posts: 14
Joined: Sun Nov 29, 2015 11:12 am

Re: read_i2c_block_data doesn't work as expected

Sat Dec 19, 2015 12:09 pm

Great! I switched back to SMBus after:

Code: Select all

echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined
and now everything works fine, even setting the MAX and MIN values which failed before. PROBLEM SOLVED, thank you very much for your help!

Return to “Interfacing (DSI, CSI, I2C, etc.)”