Fix I2c Wire to remove SINGLE_THREADED_SECTION()

SINGLE_THREADED_SECTION() was added to Wire in response to this issue http://%20https://github.com/spark/firmware/issues/698

The above issue occurs when these calls are made in a tight loop in the MCP23017 library.

Wire.endTransmission();
Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);

I removed SINGLE_THREADED_SECTION() from spark_wiring_i2c.cpp and ran a test program. It fails in a matter of seconds.

I connected a digital scope and caught the failure.

The transfers up to the gap in SCL is a successful call to endTransmission()/requestFrom(). It is followed by a successful call to endTransmission() followed by a call to requestFrom() that is only a start condition followed immediately by a stop condition.

After looking at the code in i2c_hal.c, I found that there is a problem at about line 560 where endTransmission() does not wait for the stop condition to complete.

I did a quick hack with this fix from “BEGIN WHG ADD” to “END WHG ADD”.

/* Send STOP Condition */
if(stop == true)
{
      /* Send STOP condition */
      I2C_GenerateSTOP(i2cMap[i2c]->I2C_Peripheral, ENABLE);
      /* BEGIN WHG ADD  */
      int timeout = 0X1000;
      while (i2cMap[i2c]->I2C_Peripheral->CR1 & I2C_CR1_STOP) {
        if (--timeout <= 0) return 6;
      }
      /*  END WHG ADD */
}

I tried the test program again and it ran for hours with no failures, more than 40 million time through the loop. This is thousands of times longer than the unmodified code.

Edit: I now think that SINGLE_THREADED_SECTION() fixes the problem for some applications by adding delay.

The bug is still there even though SINGLE_THREADED_SECTION() reduces its impact for some applications. I was able to make it happen even with SINGLE_THREADED_SECTION() by changing the clock rate from 400kHz to 100kHz.

I suspect I2C failures are occurring in various applications.

4 Likes