From 9db207afbe3761ba6ad20d3b5b95ad2a3fd3d55c Mon Sep 17 00:00:00 2001 From: chuck todd Date: Sat, 18 Aug 2018 00:50:59 -0600 Subject: [PATCH] Improve bus recovery (#1767) 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 c864419b..973a5778 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;