rambo / TinyWire

My modifications to TinyWire Arduino libs
284 stars 121 forks source link

Support for Start/Stop (non Repeated Start) reads? #14

Closed brendanarnold closed 9 years ago

brendanarnold commented 9 years ago

I'm not familiar with the I2C conventions but it seems that some implementations (notably the Raspberry Pi, see this answer http://raspberrypi.stackexchange.com/a/7142/12232) use a STOP/START sequence rather than a START sequence when performing a I2C read e.g.

[ 3 0 ] [ 2 r r r r r ]

instead of

[ 3 0 [ 2 r r r r r ]

Is this something that the TinyWire library can be made resilient to? Is this something that should be supported?

rambo commented 9 years ago

Tinywire does not care, neither should any of my examples (in fact the star/stop events are not even exposed to the user code). BUT Raspberry Pi has a I2C master that cannot deal with a slave that does clock-stretching and on attiny85 @8MHz the slave will always stretch the clock.

brendanarnold commented 9 years ago

Hmm if that is the case then I am at a loss - I have tried reducing the Baud rate of the I2C bus down to much lower rates progressively from 100kbps, to 32kpbs, 10kbps 1kbps and even 500bps and the results are progressively improved but always break down after enough block reads.

The code I am using is https://github.com/rambo/TinyWire/blob/master/TinyWireS/examples/attiny85_i2c_slave/attiny85_i2c_slave.ino and so should not be using too many cycles to process each read - is it really the case that clock stretching is always used?

The ATTiny85 is running at 8MHz on an internal clock at 3.3V. The Raspberry Pi code is Python using the smbus module wrapper.

rambo commented 9 years ago

The streching happens already at when tinywire (on the library level) checks if this is our slave address or not. In theory on low enough clock speeds the streching should not matter any more but for practical speeds you will hit http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html often enough for things to be unreliable.

You can use this sketch on an ATMega328 based Arduino (Uno, Duemilanove etc) to verify that things work correctly with a properly working I2C master: https://github.com/rambo/I2C/blob/master/examples/i2crepl/i2crepl.ino

brendanarnold commented 9 years ago

I found a few things that might be of interest

I tried out the code you posted here https://github.com/rambo/I2C/blob/master/examples/HMC5883L/HMC5883L.ino on an UNO and using the I2C library it worked perfectly. However I also tried the Wire.h example in the comments below.

Generally I found about 5 to 20 read errors in every 10,000 reads. I took out the Wire.endTransmission() and it worked perfectly (presumably this gives true repeated start rather than the stop-start). This was similar behaviour to what I was finding on the Raspberry Pi after lowering the Baud rate to 32,000. Final code snippet below (for device at address 0x05)

Wire.beginTransmission(0x05);
Wire.write(0x00);
Wire.requestFrom(0x05, 3);
x = Wire.read();
y = Wire.read();
z = Wire.read(); 

I think the problem is the ATTiny code occasionally not hitting the Stop check in time even though it was the only bit of logic in the loop() function.

So it seems Raspberry Pi with the out-of-the-box SMBUS has two problems when using TinyWireS, i.e. clock stretching (fix by raising speed of ATTiny using external 16MHz crystal and lowering RPi I2C BaudRate) and the ATTiny occasionally missing the stop signal in start-stop I2C read requests.

Most libraries for RPi just wrap the SMBUS commands but the PiGPIO library provides a deamon that you can call in Python to 'bit bang' at the level of the bus-pirate notation and it support clock stratching. To read using repeated-start, try the following

import pigpio

# Use the existing I2C pins since they have pullups builtin
SDA = 2 
SCL = 3

pi = pigpio.pi()
fh = pi.bb_i2c_open(SDA)

# The following uses the pigpio notation found at 
# http://abyz.co.uk/rpi/pigpio/python.html#bb_i2c_zip
# Basically the numbers in HEX are the data sent, the rest are START, STOP etc.
(b, d) = pi.bb_i2c_zip(SDA, [4, 0x05, 2, 7, 1, 0x00, 2, 6, 0x03, 3, 0])

print [int(x) for x in d] # Prints contents of first three registers

This worked without error for as long as I tested (100,000 reads)

Sorry for long comment but I wanted to make sure that user of my device could use the ATTiny85 with out-of-the-box components as much as possible.

rambo commented 9 years ago

Cool, need to add link to that library to the README so other RPi users can benefit from it. Thanks for the info.

jonasbits commented 4 years ago

updated link http://abyz.me.uk/rpi/pigpio/python.html#bb_i2c_zip