Raspberry Pi I2C Hardware Bug.
As detailed in I Hate I2C the raspberry Pi has a hardware bug with it’s I2C implementation that can cause problems for some devices. To explain this bug we have to go into the details of the I2C protocol & where the Raspberry pi gets it wrong.
The bus consists of 2 signal wires SCL (Serial Clock) & SDA (Serial Data), these lines are open collector & should be pulled high with a suitable resistor (in many cases the inbuilt pull ups of the Pi will suffice).When Idle both lines should be high. devices can be either Masters or Slaves, Masters are responsible for initiating & controlling data transfer. the Master is responsible for controlling the SCL line to shift data in or out, if necessary a slave can delay the master by pulling the clock low, this is known as clock stretching (which is also a known problem with the Pi).
When a master wants to initiate data transfer on the bus it Asserts (pulls low) SDA while SCK is high (this is the bit the Pi gets wrong as we shall see shortly) it then clocks out 8 bits (7 bit I2c Addr & a read/write bit) followed by an acknowledgement bit, if a salve recognises its own device it pulls SDA low during this period. after the ACK the master may send or receive any number of bites by clocking 8 bits + acknowledge for a write the Slave asserts SDL for a read the master assets SDL for all but the last byte.
when the full packet has been completed the master may send either a stop condition, a low to high on SDA whilst Clock is high or another Start condition (this is known as a repeated start).
Many I2C devices are configured as a “Register Bank”. This requires a write to set select the register followed by the data to write (most devices increment the register with each write so that multiple bytes can be written) or it can read the data either by sending a stop then as start or more commonly a repeated start.
Word Read from command line (words are sent low-byte high-byte)
$>i2cget -y 1 0x76 0xd0 w 0x0060
This shows a write Addr (0x76), a data byte (0xd0) followed by a repeated start a read Addr (0x76) followed by a number of read bytes (0x60,0x00)
if the device is slow at releaseing SCL after receiving a byte (because it needs time to process) the Pi Gets things wrong because it does not check the state of SCL before trying to start the next sequence.
$> i2cget -y 1 0x60 0x00 0xff
Here we are attempting a read of register 0 from device 0x60.
The initial write progresses without problem, but the processing time can be clearly seen in the clock stretching after the Addr +write (9 clocks, 8 bits + ack) and the data byte. After the 1st data byte the pi lets SDA float high before but re asserts it for the start condition BEFORE the SCL is released, because of this the Slave sees the Address + Read as more data & does not switch to read mode, master reads the next byte as 0xff because neither divice is driving SDL.
Further tests, trying to read & write to the bus with SCL physically pulled low prove that the problem is because the Pi does not check the status of SCL before asserting SDA for a start condition.
To get around this problem it is necessary to set the register & read the data separately.
$>i2cset 0x60 00 $>i2cget 0x60 00 0xaa
Or in python:-
from smbus import SMBus bus=SMBus(1) ##Bad Read data=bus.read_byte_data(0x60,0x00) ##Good Read bus.write_byte(0x60,0x00) data= bus.read.byte()