2018年11月1日木曜日

Arduino同士でI2C通信をしてみる。

ArduinoのI2Cはあまり使ったことがなかったので、スケッチ例を試してみました。

Master Writer/Slave Receiver (https://www.arduino.cc/en/Tutorial/MasterWriter)

2つのArduinoの一方をI2C Master、もう一方をI2C Slaveとして互いに通信するものです。

回路図

この回路図を見るまで知らなかったんですが、プルアップ抵抗が入っていません。

I2Cはオープンドレインでプルアップ抵抗を入れないと普通は動作しません。

オープンドレインの概略図

左側の「Open Drain」がI2Cの(大雑把な)出力です。「OUT」端子はMOS-FET Q1のドレインそのままで、これでは信号を取り出せません。

右側の「Open Drain Pullup」は、MOS-FET Q2のドレインをR1でVddにプルアップしてOUTから信号を取り出します。Q2のGateがHになったときQ2がONし、R1に電流が流れて電圧降下し(「OUT」のポイントがGNDに短絡される)、「OUT」はLレベルになります。Q2のGateがLのときはQ2がOffし、VddからR1を通してOUTに電流が流れ、「OUT」はHレベルになります。

R1の抵抗値が高すぎるとOUTにつながった容量成分に流れ込む電流が少なくなり、Hレベルになるまでに時間がかかり信号波形がなまることになります。

ArduinoのWire(I2C)の内部プルアップ


今まで知らなかったんですが、ArduinoのWireクラスはデフォルトで内部プルアップが行われているそうです。

参考「ArduinoでI2Cする際のプルアップ抵抗について

「<Arduinoのインストール先>\hardware\arduino\avr\libraries\Wire\src\utility\twi.c」の「void twi_init(void)」関数を見ると、

  // activate internal pullups for twi.
  digitalWrite(SDA, 1);
  digitalWrite(SCL, 1);

とWireクラスの中で内部プルアップが行われています。

Arduinoではデフォルトで内部プルアップされているとすると、このBlogのI2Cタグの記事を見直してみても、I2Cのプルアップがらみでいろいろ不思議に思っていた現象に説明が付きます。

内部プルアップを無効にするには、Wire.begin()したあとに

  pinMode(A4, INPUT);  // disable pullup
  pinMode(A5, INPUT);  // disable pullup

とすればピンの状態がHi-Zになりオープンドレインになります。Master/Slave両方でArduinoを使う場合、内部プルアップを完全に無効にするためには、Master/Slave両方で行って無効化する必要があります。

波形の測定


最近よく使っている中華製のArduino Pro Mini 8MHz/3.3V版で実験してみました。

Arduinoのスケッチ
https://github.com/ryood/Arduino_I2C/tree/master/Arduino

ブレッドボード配線図

ブレッドボード配線図の、下の「Direct」は内部プルアップを使う場合の配線、上の「Pullup」は外付けのプルアップ抵抗を使う場合の配線です。

MasterのI2Cのピン(A4、A5)に(Slaveにつながず)直接オシロのプローブを当てて測定しました。

電源電圧:3.29V


ch1:A4(SDA) ch2:A5(SCL)

内部プルアップが効いているので信号が出力されています。スレーブに接続していないので、SDAの9ビット目がLに引っ張られていません。

前述のように内部プルアップを無効化すると


ch1:A4(SDA) ch2:A5(SCL)

信号が現れません。Slaveに接続せず、信号線をVDD(3.3V電源)に2.2kΩの抵抗でプルアップすると


ch1:A4(SDA) ch2:A5(SCL)

信号が出力されているのを測定できました。しかも、内部プルアップの場合より波形がきれい(立ち上がり、立ち下がりが速い)です。これは2.2kΩとプルアップ抵抗の抵抗値が低いためです。

スレーブに接続して、プルアップ抵抗の抵抗値を変えてみる。


プルアップ抵抗値を変えてI2Cの信号波形を測定しました。

プルアップ抵抗:2.2kΩ

ch1:A4(SDA) ch2:A5(SCL)

Slaveに接続したので、SDAの9ビット目がLに引っ張られて(ACK)、以降の通信が成功しています。

プルアップ抵抗:10kΩ

ch1:A4(SDA) ch2:A5(SCL)

プルアップ抵抗:47kΩ

ch1:A4(SDA) ch2:A5(SCL)

プルアップ抵抗:100kΩ

ch1:A4(SDA) ch2:A5(SCL)

プルアップ抵抗値を上げていくてと、立ち上がりが遅くなっています。このスケッチでは、ざっと見た感じ、いずれもプログラム通りSlaveのSerialから文字列が出力されていますが、100kΩの場合はSCL/SDAの電圧やタイミングがかなり怪しくなっています。信号路の状態によっては通信エラーが発生する可能性が高いと思います。

10kΩあたりまでが無難でしょうか。プルアップ抵抗値を下げるとLレベルになった場合、電流が多く流れるので(特にバッテリー駆動の場合)なんでもかんでも抵抗値を下げればいいというわけでもありません。

I2Cクロックを上げてみる。


ArduinoのデフォルトではI2Cクロックは100kHz(Standard Mode)ですが、ATMega328Pを8MHz駆動すると400kHz(Fast Mode)まで動作させられます。

スケッチで

  Wire.setClock(400000);

と指定すれば400kHzのFast Modeになります。Fast Modeの場合もプルアップ抵抗値を変えて信号波形を測定しました。

プルアップ抵抗:10kΩ

ch1:A4(SDA) ch2:A5(SCL)

プルアップ抵抗:4.7kΩ

ch1:A4(SDA) ch2:A5(SCL)

プルアップ抵抗:2.2kΩ

ch1:A4(SDA) ch2:A5(SCL)

I2Cクロックを上げると波形の崩れにさらに注意する必要がありそうです。

メモ:


ArduinoのWireクラスは難解なI2Cの仕様をうまくラッピングしていて、AVRを直に使う場合と比べてとても楽だと思います。

ただ、うまく動作している場合はいいんですが、そうでない場合は・・・

0 件のコメント:

コメントを投稿