I2Cのクロックストレッチ

I2Cバスは、通常はマスターがSCLラインを制御することで通信を主導し、スレーブはマスターのタイミングに従います。しかし、スレーブ側の処理が間に合わないとき、スレーブはSCLラインをLowに保持することでマスターを待たせることができます。これを「クロックストレッチ」といいます。

例えばBOSCH社の9軸センサBNO055はしばしばクロックストレッチでACKビットを100μsec以上待たせます。

上: SCL, 下: SDA, 横軸: 50μsec/DIV
f:id:licheng:20220123102901p:plain:w480

ところが、Raspberry Pi PicoのArduino環境 (本家版ではなくEarle Philhower版のほう) では、I2Cのクロックストレッチ待ちは100μsecでタイムアウトしてしまいます。このため、AdafruitのBNO055ライブラリが正しく動作しなくなります。タイムアウト時間が100μsecというのはいささか短いように思われます。

f:id:licheng:20220123103125j:plain:w480

そこで、I2Cドライバのソース( Wire.cpp ) の_clockStretch関数を下記のように修正します。

static bool _clockStretch(pin_size_t pin) {
//  auto end = time_us_64() + 100;  // 100usecでタイムアウト
    auto end = time_us_64() + 1000; // 1000usecでタイムアウト
    while ((time_us_64() < end) && (!digitalRead(pin))) { /* noop */ }
    return digitalRead(pin);
}

Wire.cppは、Windowsインストーラ版であれば下記のディレクトリにあります。

%LOCALAPPDATA%\Arduino15\packages\rp2040\hardware\rp2040\x.x.x\libraries\Wire\
(x.x.x はバージョン番号)