I2C接続

最近、I2C接続のデバイスが増えてきました。I2C は電源を除けば、2本の信号だけで、複数のデバイスを接続可能です。センサー、表示器、時計、記憶、を I2C接続するとデータロガーの要素がそろいます。

  1. I2C接続

     I2C は2本の信号 SDA(データ信号) と SCL(クロック信号)で接続するシリアル通信の1種です。この信号線に複数のモジュールを接続し、デバイスのアドレスにより切り替えて送受信可能です。
     SDAとSCLの端子は ラインのどこか1箇所で 抵抗による電圧VCCへの「プルアップ(吊り上げ)」が必要です。Arduino UNO では SCL にはA5、SDA には A4 端子を接続します。

  2. 接続の仕組み

     通常、ディジタル信号線には、一つの出力端子しか接続できません。
     下図はディジタル出力端子の概念図です。左図で上下の素子 Q1,Q2 はトランジスタです。Q1 がオンで、Q2 がオフのとき、出力端子はHIGH状態になります。右図は、Q3がオフ、Q4がONで出力端子はLOW状態となります。このとき、点線のように、二つの出力を接続すると、電源>Q1>Q4>GND と電源からGNDへのショート状態になります。

         複数の出力端子の接続

     このように、複数の出力端子を接続し、一方が HIGH、他方が LOW を出力すると、HIGH 側から LOW 側に大きな電流が流れ、熱破壊を起こすします。
     I2C の出力端子は 下図のように、上部のトランジスタが省略されています。LOW 状態にはなりますが、HIGH 状態にはならず、「HIGH」状態では電気的には無接続状態(Z:ハイインピーダンスともよびます)になるよう設計されています。
     
        I2Cの出力端子

     どれかの端子が LOW 状態になれば信号線は LOW になりますが、すべて無接続状態(Z)になると、信号線に接続された外部抵抗により、HIGH レベルに引き上がります(プルアップともいいます)。抵抗は小さいほど速く引き上がりますが、状態が LOW の時に流れる電流が大きくなります。
     電源に余裕がある場合は 数kオーム、バッテリーなど消費電力を抑えたいときは 10kオーム程度の抵抗を利用します。
     
  3. I2Cによる制御

     I2Cでは通常一つの親デバイスと複数の子デバイスから構成されます。親デバイスは UNO(MINI) で、子デバイスはセンサーやディスプレイの接続デバイスになります。
     子デバイスには固有のアドレス(番地)があります。親デバイスはアドレスを指定して子デバイスにメッセージを送ります。子デバイスはアドレスを監視し、自分宛のメッセージのみを受け取ります。I2C のアドレスは通常上位 7ビットで、最下位ビットは読み出しと書き込みを区別するビットになっています。Arduino のI2C用ライブラリー(wire.h)で指定するアドレスは ビット1からビット7の値になります。
     子デバイスは多くの場合、いくつかの「レジスタ」をもっており 親デバイスは子デバイスのレジスタに値を書き込んだり、値を読み出します。読み書きは 1 バイト(8ビット)単位です。したがって、10 ビットのデータを読み出すには、2回読み出す必要があります。レジスタの値と意味は使用するデバイスで異なります。新しいデバイスでは 英文のマニュアルを読む必要があります

  4. ArduinoのI2C接続回路

     UNO(MINI)は I2C 接続用のハードウエアを内蔵しています。端子は アナログ端子と共用で、SCL はA5、SDA はA4 端子を利用します。SCL と SDA の端子を 2kオーム程度の抵抗を通して 5V に接続する(プルアップ)必要がありますが、Arduino の Wire ライブラリーを利用すると、内部の抵抗を利用して プルアップしてくれますから、外部での抵抗接続は不要です。
     下図は、UNO に 温度センサー(左下)、加速度センサー(中央赤)、液晶表示器(右上)を接続した例です。黄色がSDA、緑がSCL、赤が電源+、青が電源ー(GND)の配線です。
     これだけの配線で、温度と加速度を測定し、液晶に結果を表示することができます(続く節を参照)。
     
      UNO に 3種の I2C デバイスの接続例

  5. I2Cのライブラリー( wire.h)

     I2Cの接続には標準ではライブラリー  Wire.h を利用します。まず、Wire.begin で I2C の初期設定を行います。このとき、SCLとSDAのラインを内部の抵抗でプルアップします。I2C を通してデータを書き込むには まず、Wire.beginTransmission(I2Cアドレス) でI2Cアドレスを指定します。次に、Wire.write(レジスタ番号); でレジスタ番号、Wire.write(データ); でバイトのデータを書き込み、Wire.endTransmission(); で終了します。データが2バイトの場合、Wire.write(データ); を2回行います。

    #include wire.h
      
    void setup(){
     // 準備
       Wire.begin(); 
    }
     
    void loop()
    { 
      //データ書き込み
      Wire.beginTransmission(I2Cアドレス) ;
      Wire.write(レジスタ番号);
      Wire.write(データ);
      Wire.endTransmission(モード);
      
     //データ読み出し
      Wire.beginTransmission(I2Cアドレス) ;
      Wire.write(レジスタ番号); 
      Wire.endTransmission();
      Wire.requestFrom(I2Cアドレス, バイト数);
      while(Wire.available()) {
          b1 = Wire.read(); // 
      }
     
    }

     データを読みだす場合には、まず、書き込みと同様、レジスタ番号を指定します。その後、Wire.endTransmission(); で書き込みを停止後、Wire.requestFrom(I2Cアドレス, バイト数); でバイト数を指定して ライブラリ内部にデータを読みだします。Wire.available() で読み出しデータを確認しながら、Wire.read() で読みだします。

  6. 発展
     
    1. I2Cの信号線はどこまで長くできますか?

      通信速度やプルアップ抵抗にもよりますが、あまり長くはできません。10cm-20cm程度で使用するのが安全です。
    2. 通信速度はどのくらいですか?

      バージョン1.0では 400kbps、バージョン2.0では 3.4Mbps も規格化されています。
    3. 複数マスターは可能ですか

      1本のバス状に マスターを複数組み込むことは規格上は可能です。