Arduino を使った ESP32 の I2C 通信におけるクロックストレッチ対応について注意点を紹介します.
はじめに
前回,ESP-IDF を使った場合についてまとめましたので,今回は Arduino を使った場合の注意点についてまとめます.
注意点
結論としては下記になります.
- ハードウェア制約により,クロックストレッチできる期間は最大 13ms.
- 追加設定は不要
以降で,順に説明していきます.
クロックストレッチできる期間は最大 13ms
これは,ハードウェアの制約のため,ESP_IDF 編と同じです.
ESP32 のクロックストレッチのタイムアウト時間は,下記の I2C_TIME_OUT_REG レジスタに設定します.単位は APB clock cycles となっており,このクロックの周波数は 80MHz です.
レジスタのサイズは 20bit なので,最大値は 0xFFFFF となります.0xFFFFF / 80MHz = 13ms ですので,クロックストレッチできる最大時間は 13ms となります.
I2C 通信を 400kHz で行っている場合,13ms は 5,200 サイクルに相当しますので,通常問題になることはないと思いますが,特殊なデバイスを使っている場合注意が必要かもしれません.
追加設定は不要
ESP-IDF とは違い,Arduino を使う場合,上記レジスタには自動的に最大値が設定されます.
具体的には下記の順で呼び出される i2cProcQueue
の中で設定されます.
- Wire.cpp
TwoWire::requestFrom
- Wire.cpp
TwoWire::readTransmission
- esp32-hal-i2c.c
i2cRead
- esp32-hal-i2c.c
i2cProcQueue
i2cProcQueue の中には次の処理があります.
1 |
i2c->dev->timeout.tout = 0xFFFFF; // max 13ms |
動作サンプル
上記を踏まえた I2C 通信のサンプルを紹介します.題材としては,ESP-IDF の場合と同様に温湿度センサである SHT3x を使用します.
SHT3x は測定データの読出し時に,下記のようにクロックストレッチを使うことができますので,これを試します.
コード全体は次のようになります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#include <Arduino.h> #include <Wire.h> static const uint8_t I2C_SHT3x_ADR = 0x44; static const uint8_t I2C_SCL_NUM = 22; static const uint8_t I2C_SDA_NUM = 21; static const uint32_t I2C_FREQ_HZ = 100000; static uint8_t crc8(const uint8_t *data, uint32_t len) { static const uint8_t POLY = 0x31; uint8_t crc = 0xFF; for (uint32_t i = 0; i < len; i++) { crc ^= data[i]; for (uint32_t j = 0; j < 8; j++) { if (crc & 0x80) { crc = (crc << 1) ^ POLY; } else { crc <<= 1; } } } return crc; } static bool sht3x_check_val(uint8_t *buf) { if (crc8(buf + 0, 2) != buf[2]) { Serial.printf( "CRC of temperature is INVALID. (exp: 0x%02X, calc: 0x%02X)\n", buf[2], crc8(buf + 0, 2)); return false; } if (crc8(buf + 3, 2) != buf[5]) { Serial.printf( "CRC of humidity is INVALID. (exp: 0x%02X, calc: 0x%02X)\n", buf[5], crc8(buf + 3, 2)); return false; } return true; } static void sht3x_dump(uint8_t *buf) { uint16_t temp_val; uint16_t humi_val; sht3x_check_val(buf); temp_val = ((uint16_t)buf[0]) << 8 | buf[1]; humi_val = ((uint16_t)buf[3]) << 8 | buf[4]; Serial.printf("TEMP: %.2f\n", -45.0 + (175.0 * temp_val) / ((1 << 16) - 1)); Serial.printf("HUMI: %.2f\n", 100.0 * humi_val / ((1 << 16) - 1)); } static void sht3x_read() { uint8_t buf[6]; Wire.beginTransmission(I2C_SHT3x_ADR); Wire.write(0x2C); Wire.write(0x0D); Wire.endTransmission(); Wire.requestFrom(I2C_SHT3x_ADR, (uint8_t)6); if (Wire.available() != 6) { Serial.println("Failed to read sensor."); return; } for (uint32_t i = 0; i < 6; i++) { buf[i] = Wire.read(); } sht3x_dump(buf); } void setup() { Serial.begin(9600); Wire.begin(I2C_SDA_NUM, I2C_SCL_NUM, I2C_FREQ_HZ); delay(100); // NOTE: pull up が有効になるのを待つ } void loop() { sht3x_read(); delay(2000); } |
コメント