ESP32 の I2C 通信クロックストレッチ対応 (ESP-IDF 編)

組み込み
スポンサーリンク

ESP32 の I2C 通信におけるクロックストレッチ対応について注意点を紹介します.

スポンサーリンク

はじめに

ESP32 は I2C のクロックストレッチ(clock stretch) に対応しています.ただ,実際に活用するに当たっては注意点があります.

ESP-IDF を使用する場合を例にとって具体的に紹介します.

注意点

結論としては下記になります.

  • ハードウェア制約により,クロックストレッチできる期間は最大 13ms
  • ESP-IDF の場合,デフォルトでは 8 クロックでタイムアウト.これより待ちたい場合はタイムアウト時間の変更が必要.
  • タイムアウト時間の変更は,i2c_set_timeout で行う
  • i2c_master_cmd_begin の第3引数の ticks_to_wait は関係ない

以降で,順に説明していきます.

クロックストレッチできる期間は最大 13ms

ESP32 のクロックストレッチのタイムアウト時間は,下記の I2C_TIME_OUT_REG レジスタに設定します.単位は APB clock cycles となっており,このクロックの周波数は 80MHz です.

レジスタのサイズは 20bit なので,最大値は 0xFFFFF となります.0xFFFFF / 80MHz = 13ms ですので,クロックストレッチできる最大時間は 13ms となります.

I2C 通信を 400kHz で行っている場合,13ms は 5,200 サイクルに相当しますので,通常問題になることはないと思いますが,特殊なデバイスを使っている場合注意が必要かもしれません.

デフォルトでは 8 クロックでタイムアウト

ESP-IDF では,上記レジスタへの設定を esp-idf/components/driver/i2c.c の i2c_param_config 関数のなかで行っています.

具体的には下記の箇所になります.

I2C_MASTER_TOUT_CNUM_DEFAULT は 8 と define されており,8サイクルでタイムアウトさせるようになっています.

タイムアウト時間の変更は i2c_set_timeout

第2引数にタイムアウト時間を 80MHz でのクロック数で指定します.特にこだわりがなければ,最大値である 0xFFFFF を設定しておけば良いと思います.Arduino core for the ESP32 ではそうなっています.

i2c_master_cmd_begin の第3引数は関係ない

i2c_master_cmd_begin の第3引数は ticks_to_wait となっており,ぱっと見クロックストレッチに関係しそうな気がしますが,実は全く関係ないです.

ESP32 の I2C ドライバは複数タスクから呼ばれることを想定して排他処理を行っています.ticks_to_wait はこの排他処理のロックをとれるまでの最大待ち時間を設定するものになります.

動作サンプル

上記を踏まえた I2C 通信のサンプルを紹介します.題材としては,温湿度センサである SHT3x を使用します.

SHT3x は測定データの読出し時に,下記のようにクロックストレッチを使うことができますので,これを試します.

コード全体は次のようになります.44行目の i2c_set_timeout(i2c_port, 0xFFFFF); でクロックストレッチのタイムアウト時間を変更しています.これをコメントアウトすると計測データが正しく読めなくなります.

コメント

  1. 初心者 より:

    ESPrdeveloper32(soc:ESP32)を先日購入した初心者です。
    これと、マスタ側がclockstretch対応が必要なセンサをI2Cで繋げようとしていて果たして問題がないのか調べていてもわからなかった矢先にkimata様の記事をみつけて大変勉強になりました。ありがとうございます。
    ご質問させていただきたいのですが、上記、具体的には、SCD30(sensirion製)をつなぎたい状況です。
    しかし、このセンサの通信仕様書の中のclock stretchingに関する記述を見ると
    “Maximal I2C speed is 100 kHz and the master has to support clock stretching. Clock stretching period in write- and readframes is 12 ms, however, due to internal calibration processes a maximal clock stretching of 150 ms may occur once per day. ”
    とあります。
    この場合、最大150msが一日に一度程度の頻度でSCLをLOWにしようとセンサ側がしてしまう(ことが起きうる)ということだと思います。
    こういった場合、ESPr developper 32にてこのセンサを接続するのはいかに工夫をしてもできないということになるのでしょうか。最大のおきうる時間に合わせて周波数が約3Hz(遅いですが、)のソフトウェアI2Cを実装するなどで、データの取得間隔に対する要求が緩い場合は対応できるのでしょうか。
    aws IoTへ手軽にセンサ情報を送れそうだということで活用を検討しているのですが、どうしてもこの製品だと難しい場合代替案などありますでしょうか。
    当方、周りに聞ける人がおらず困っております、
    お手すきの際にご回答いただけますと幸いです。

  2. kimata より:

    クロック周波数にかかわらず,クロックストレッチ期間が 13ms を超えると i2c_master_cmd_begin が ESP_ERR_TIMEOUT を返してしまいます.

    お使いのデバイスの場合,150ms を超えるのは一日に一回の頻度のようですので,リトライするのが良いように思います.

    具体的には,ESP_ERR_TIMEOUT になった場合,一旦 i2c_driver_delete を呼んでから,再度 i2c_driver_install を呼ぶことで,また I2C 通信が行えるようになります.

    (これをしないとドライバ内部の状態が中途半端なままなって,その後の i2c_master_cmd_begin が ESP_ERR_TIMEOUT を常に返すようになってしまいます)

    • 初心者 より:

      こんなにお早く対応いただけるとは、、教えてくださりありがとうございます。

      重ね重ね申し訳ないのですが、現状arduinoIDEを用いた実装(arduino.hとWire.h使用)で実現できないかこころみております。
      ご教授いただいた内容と同様のことは実現できそうでしょうか。
      もしご存知でしたら教えていただけますと幸いです。

      • kimata より:

        ざっとコード見た感じですと,タイムアウト後に復旧させることは考えてなさそうなので,簡単ではないと思われます.素直に ESP-IDF 使うのがおすすめです.

        • 初心者 より:

          ESP-IDF使いこなせるよう頑張ってみます。
          今後とも参考にさせていただきます。
          本当にありがとうございました。