Skip to content

enableWire() Needs to check SCL and SDA before setting bus state to idle #743

Description

@studiohsoftware

sercom->I2CM.STATUS.bit.BUSSTATE = 1 ;

I have a multimaster I2C system running the Arduino Wire library on a SAMD21 MKR1000 that uses general call for message broadcasts. Each device runs in slave mode and then switches to master briefly to send a message.

I found many hangs, BUSERR, ARBLOST, and RXNACK problems when bus traffic was heavy. The problem is that enableWire() in the Arduino SERCOM.cpp library (called from Wire.begin) blindly sets the bus state to IDLE.

While you can place checks on isBusBusyWIRE before beginTransmission and endTransmission to avoid collisions in your Arduino sketch, those checks will fail if the bus has been set to IDLE when it is actually busy. The master will come up and if there is a message on the bus it will get overwritten. (Note the SERCOM library itself uses these checks in critical locations within the code.)

The answer is to check the bus to see if it is actually idle before setting the bus state to IDLE in enableWire(). Here is the workaround using the Arduino Sketch before calling Wire.begin():

//Wire.begin sets bus to idle. Make sure bus is actually idle first.
pinMode(SCL_PIN, INPUT);
pinMode(SDA_PIN, INPUT);
int start_time = micros();
int idle_time = 0;
while (idle_time < 50) {
  //Each clock cycle is about 10uS. These digitalRead calls take a few uS each.
  if ((digitalRead(SCL_PIN) == LOW) || (digitalRead(SDA_PIN) == LOW)) {
    start_time = micros();
  }
  idle_time = micros() - start_time;
}
Wire.begin();

The correct place for this check is within enableWire().

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions