2021年3月21日日曜日

TeensyのI2C通信の読取りはレジスタ指定をendTransmission(false)で終える必要があることがある


背景

Teensyとは、Arduinoフレームワークで動かせる、ピンが多めで、処理能力も高めのマイコンです。
そのteensyでI2C通信で装置を動かそうとしたところ、書き込みはできるのに読み込みができませんでした。

標題が解決方法を示していますが、備忘録を兼ねて関連リンクと合わせて記事を残します。

使ったもの

  • Teensy3.6
    今回は保有していたTeensy3.6を使いましたが、新しくTeensyを使い始める場合はより高性能なのに価格がTeensy3.6より安いTeensy4.1をお勧めします。
  • I2C装置
    今回はI2cControlPanelとレーザー距離センサVL53X1で動作確認しました。

解決方法: レジスタ指定後のendTransmissionにfalseを渡す

コントローラーの読取りで0しか取得できなくて調査をしたところ、Teensy3.1のI2C通信に関する議論で有効な情報が提供されていました。

Teensy 3.1 reads wrong data through I2C

Wireライブラリを利用したI2C通信は下記の流れて行うことが多いと思います。
  1. 通信相手の端末アドレスとレジスタアドレスを指定
  2. 読取りバイト数を設定
  3. 読取り

コードにするとこうなります。
このとき、アドレス指定後のendTransmissionをfalseで終える必要がありました。
int devideAddress = 0x40;
int registerAddress = 0x00;
int length = 10;
chad data[length];

// アドレス指定
Wire.beginTransmission(deviceAddress);
Wire.write(registerAddress);
Wire.write(data, length);
Wire.endTransmission(false); // Important

// バイト数設定
Wire.requestFrom(deviceAddress, length);

// 読取り
while (Wire.available()) {
char d = Wire.read();
if (dataIndex < length) {
data[dataIndex] = d;
++dataIndex;
}
}
endTransmissionの説明によると、標準値のtrueだとリセット信号を送信しますが、falseだとそれを送信せずI2Cのバスを保持し続けるようです。
上記の変更により、コントローラーの値を読めるようになりました。

endTransmissionにfalseを渡さなくても動く装置もある

TeensyではendTransmissionにfalseを渡すのが必須かと思いましたが、レーザー距離センサVL53L1Xのライブラリはその記述になってないにも関わらず、Teensyで期待通りに値を読み取れました。

VL53L1Xは、レジスタ指定のための書き込みの後一度通信を止めて読込を行う仕様になっているため、endTransmissionにfalseを渡さなくても動くようです。
(コメントで教えていただきました。)

余談: Teensy3.6のI2Cは、プルアップ抵抗の外付けが必須


プログラムでI2Cの信号線をプルアップする記述ができますが、内部抵抗によるプルアップは抵抗値が低すぎて信号が無効化されます。
Teensy向けの多機能I2Cライブラリi2c_t3によると、Teensy3.6のI2Cのプルアップ内部抵抗は25Ω以下らしいです。

抵抗を利用してプルアップしましょう。
上記の写真は10kΩを利用しています。

まとめ

Teensy3.6でI2C通信で読み取れなかったコントローラーの値が、endTransmissionの引数をfalseにすることで読めるようになりました。

参考

Teensy 3.1 reads wrong data through I2C
Teensy | Wire Library

変更履歴

2021.06.01
VL53L1XでendTransmission(false)を使わなくても値が読めるのは、それの変わった仕様のためだとコメントで情報共有していただいたので、説明を追加しました。

2 件のコメント :

辻田 さんのコメント...

I2Cデバイスの仕様書のRead sequenceを確認してみるといいと思います。
おそらく、デバイスIDの指定→レジスタアドレスの指定(ここまではI2CのWrite)の後、Repeated startの後、デバイスIDの指定→スレーブからのデータ(I2CのRead)となっている場合は、endTransmission(false)にしてRepeated startを送らないといけないのだと思います。
VL53L1Xは、仕様書を見ると、レジスタアドレスを指定した後、Stopで終わって読み出すというシーケンスになっているので、どちらかというと珍しい仕様の気がします。

Asuki Kono さんのコメント...

情報ありがとうございます。
VL53L1Xはそういう仕様になっているのですね。