こんにちは、shuji4649です。
今回はI2Cフリーズ時の対策として使えるI2Cバスリカバリという小技について紹介します。
この記事は Tuton Advent Calender 2025 の3日目の記事です。
I2Cとは
SDAとSCLという2本の信号線でIC間の通信を行う規格です。
本来はI²Cという表記が正しいですが本記事ではI2Cと表記させてください。
アドレスを変えることで同じ配線に複数の機器を接続できるという利点がありますが、ノイズに弱くフリーズしやすいという欠点があります。
オープンドレイン出力となっていて、信号線にはプルアップ抵抗が必要です。
通信の手順などの詳細についてはこれらの記事で紹介されているのでぜひご覧ください。
I2Cのフリーズ
クロックが間違って挿入されたり欠落したりすると、同期が狂って通信不可能になります。そしてスレーブがSDAをロックしているとマスターから情報を送ることができなくなりフリーズします。
信号線にノイズが乗ったり、配線長が長すぎて信号の立ち上がりが遅かったりすることが原因でフリーズすることが多いです。
バスリカバリ
本来は配線や接続などのハードウェア的問題を見直してフリーズしないようにするべきですが、どうしてもたまにフリーズしてしまうというような時のために、ソフトウェアで対策する方法があります。
多くの場合、スレーブは「クロックが9回来ると状態をリセットして IDLE に戻る」という動作をする設計になっています。
そのため、一度SCLをGPIOに切り替えて、HIGHとLOWを9回以上切り替えてあげると無理やりですがフリーズを解消することができます。
最後にSTOPコンディションを生成してI2Cに切り替えてバスリカバリは終了です。
以下にArduinoのサンプルコードを示します。
void i2c_bus_recovery()
{
pinMode(I2C_SDA, INPUT_PULLUP);
pinMode(I2C_SCL, OUTPUT_OPEN_DRAIN);
// 9クロック生成
for (int i = 0; i < 9; i++)
{
digitalWrite(I2C_SCL, HIGH);
delayMicroseconds(5);
digitalWrite(I2C_SCL, LOW);
delayMicroseconds(5);
}
// STOPコンディションを生成
pinMode(I2C_SDA, OUTPUT_OPEN_DRAIN);
digitalWrite(I2C_SDA, LOW);
delayMicroseconds(5);
digitalWrite(I2C_SCL, HIGH);
delayMicroseconds(5);
digitalWrite(I2C_SDA, HIGH);
delayMicroseconds(5);
// I2Cを再初期化
Wire.begin();
}
おわりに
こんなものに頼らなくても大丈夫なようにI2Cの配線には細心の注意を払いましょう。ソフトウェアでのI2Cバスリカバリは最終手段です。