From 1205011f96981c7cabf218c1a90343d97c54258c Mon Sep 17 00:00:00 2001 From: chuck todd Date: Fri, 17 Aug 2018 11:33:11 -0600 Subject: [PATCH] Improve bus recovery If the esp32 is reset during a i2c read cycle the slave device may be in control of the SDA line. If the SDA line is held low, the esp32 cannot issue a START or STOP to recover the bus. The previous code did not correctly configure the SCL output pin, and it cycled SCL 9 times with SDA Low. Since the slave device was in a READ cycle, it just continued outputting the bits of the current byte. When the ACK/NAK bit space occurred, The low output value of SDA was interpreted as ACK so the slave device continued with the next byte. It never terminated the READ cycle. This new code will correctly recover from an interrupted READ --- cores/esp32/esp32-hal-i2c.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/cores/esp32/esp32-hal-i2c.c b/cores/esp32/esp32-hal-i2c.c index c864419b54a..973a5778328 100644 --- a/cores/esp32/esp32-hal-i2c.c +++ b/cores/esp32/esp32-hal-i2c.c @@ -1350,33 +1350,32 @@ static void i2cReleaseISR(i2c_t * i2c) } static bool i2cCheckLineState(int8_t sda, int8_t scl){ - if(sda < 0 || scl < 0){ - return true;//return true since there is nothing to do + if(sda < 0 || scl < 0){ + return false;//return false since there is nothing to do } - // if the bus is not 'clear' try the recommended recovery sequence, START, 9 Clocks, STOP + // if the bus is not 'clear' try the cycling SCL until SDA goes High or 9 cycles digitalWrite(sda, HIGH); digitalWrite(scl, HIGH); - pinMode(sda, PULLUP|OPEN_DRAIN|OUTPUT|INPUT); - pinMode(scl, PULLUP|OPEN_DRAIN|OUTPUT|INPUT); + pinMode(sda, PULLUP|OPEN_DRAIN|INPUT); + pinMode(scl, PULLUP|OPEN_DRAIN|OUTPUT); if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state - log_w("invalid state sda=%d, scl=%d\n", digitalRead(sda), digitalRead(scl)); - digitalWrite(sda, HIGH); + log_w("invalid state sda(%d)=%d, scl(%d)=%d", sda, digitalRead(sda), scl, digitalRead(scl)); digitalWrite(scl, HIGH); - delayMicroseconds(5); - digitalWrite(sda, LOW); for(uint8_t a=0; a<9; a++) { delayMicroseconds(5); digitalWrite(scl, LOW); delayMicroseconds(5); digitalWrite(scl, HIGH); + if(digitalRead(sda)){ // bus recovered + log_d("Recovered after %d Cycles",a+1); + break; + } } - delayMicroseconds(5); - digitalWrite(sda, HIGH); } if(!digitalRead(sda) || !digitalRead(scl)) { // bus in busy state - log_e("Bus Invalid State, TwoWire() Can't init"); + log_e("Bus Invalid State, TwoWire() Can't init sda=%d, scl=%d",digitalRead(sda),digitalRead(scl)); return false; // bus is busy } return true;