|
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().
ArduinoCore-samd/cores/arduino/SERCOM.cpp
Line 398 in 993398c
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, andRXNACKproblems when bus traffic was heavy. The problem is thatenableWire()in the ArduinoSERCOM.cpplibrary (called fromWire.begin) blindly sets the bus state toIDLE.While you can place checks on
isBusBusyWIREbeforebeginTransmissionandendTransmissionto avoid collisions in your Arduino sketch, those checks will fail if the bus has been set toIDLEwhen 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
IDLEinenableWire(). Here is the workaround using the Arduino Sketch before callingWire.begin():The correct place for this check is within
enableWire().