lucullusTheOnly / TinyWire

Composite Master and Slave I2C library for Atmels ATTiny microcontrollers
108 stars 26 forks source link

Slave SDA stuck to zero #11

Open felias-fogg opened 6 years ago

felias-fogg commented 6 years ago

Using the library as a slave client on an ATtiny85 with 1 MHz clock, I experienced quite often that the SDA line got stuck to zero. Disabling the Timer0 interrupt (responsible for counting millis etc) seems to help. I haven't seen this problem again even running the slave client for quite some time. I am not really sure whether this is problem of the library or of the way Arduino implements the Timer0 IRQ routine. I just wanted to mentioned the problem so that others might be aware of the potential problem.

lucullusTheOnly commented 6 years ago

Does the code of the library gets stuck completely, or is just SDA stuck to low and the code runs further?

felias-fogg commented 6 years ago

I added a "being alive" signal in the main loop, which does not show any more, when the the SDA line gets stuck. So I guess, it gets stuck inside the library code. It seems to happen during the execution of an onrequest call-back function. I can send you the code, if you want. However, it usually takes quite some time before the error shows up.

lucullusTheOnly commented 6 years ago

I thought a while about this. For pure slave mode there are only 2 places, where a block can happen:

  1. Start condition ISR: The ISR checks, if the start condition was completed correctly (first SDA pulled low during SCL high, then SCL has to go low too). It loops until this happens. If the start condition is not done correctly, it will loop forever.
  2. onRequest callback function: The onRequest callback function is called from the USI overflow ISR. If it doesn't exit, so will the ISR not exit and the code is blocking.

Can you check, if the start condition is completed correctly by your master in the case of a stuck? Maybe you can capture it with a scope, or you just check the state of the SCL line.

felias-fogg commented 6 years ago

I noticed that the lockup happened during the transfer of 4 bytes from the slave to the master. The first 2 bytes were transfered OK, the second 2 bytes were 0. So, my best guess is that it happens inside the onRequest callback function.

phillipdavidstearns commented 4 years ago

I'm experiencing this issue as well and am not able to resolve it given the info above.

Hardware: Jetson Nano is Master on its I2C bus 1 (pins 3 and 5)

This is the code I'm running on the ATTiny85 (16MHz)

// Simple Sketch for ATtiny85
// Listens to I2C for an 8-bit value
// writes that value to PWM output

#include <TinyWire.h>

#define ADDRESS 0x40

#define PWM_PIN 1 // pin #6 on the ATtiny85

float ease_rate = 0.125;
float value = 0;
int target = 0;

//////////////////////////////////////////////////////////////////////////////

void setup() {

  //  warning: this contains cryptic register settings for the ATTiny85 timers

  cli(); // clear interrupts

  // timer0 PWM freqeuncy ~8Khz (16Mhz / 256 / 8)
  TCCR0A = 0b00000011; // waveform generation mode (WGM) = fast pwm
  TCCR0B = 0b00000010; // bits 0-2 are prescaler /8

  // timer1 frequency ~ 60Hz ( 16Mhz / 8192 / 60 ~ 32.5 )
  // used for timed smoothing PWM values
  TCCR1 = 0b10001110; //CTC - 16Mhz / 8192
  OCR1A = 0b00000000; //interrupt when the clock is reset
  OCR1C = 0b00010000; //clock is reset when it hits 32
  TIMSK = 0b01000000; // attach interrupt to OCR1A

  sei(); // set interrupts

  pinMode(PWM_PIN, OUTPUT);

  value = 0;
  target = 0;

  // config TinyWire library for I2C slave functionality
  TinyWire.begin( ADDRESS );
  // sets callback for the event of a slave receive
  TinyWire.onReceive( onI2CReceive );

}

//////////////////////////////////////////////////////////////////////////////

void loop() {
  analogWrite(PWM_PIN, int(value));
}

//////////////////////////////////////////////////////////////////////////////

ISR(TIM1_COMPA_vect) {
  value += ease(value, target, ease_rate);
}

//////////////////////////////////////////////////////////////////////////////

void onI2CReceive(int howMany) {
  while( TinyWire.available() > 0 ){
    target = TinyWire.read();
  }
}

//////////////////////////////////////////////////////////////////////////////

float ease(float _val, int _target, float _ease) {
  return ( float(_target) - _val ) * _ease;
}

I noticed that when I use the i2cdetect tool on the Jetson, the device wasn't showing up and I wasn't able to send data to the ATTiny85. I checked the SDA and SCL pins on a scope and found the SDA pin was driven low on the ATTiny85. Only after resetting the ATTiny85, does the SDA pin return high again.

Out of desperation, I skipped the detection and went straight for sending data to the ATTiny85. Using the smbus python library and can send data that is successfully received by the ATTiny85.

I can use the ATTiny85, but am troubled by the prospect that the SDA pin can get stuck low.

Detection shouldn't cause a device to hang.

Was the source of the problem ever properly identified? Is there a solution?