Arduino と ADT7410とI2C通信で温度測定

2019年9月5日

この記事ではArduinoを使ってI2C通信を行う方法を紹介します。

I2C通信とは

I2Cは アイ・スクエアド・シー、アイ・アイ・シー などと呼びますが、 フィリップス社が開発した基板上のIC間での通信方式です。

デジタルICは昔はバス数だけ配線をしてHi、Loの信号を送っていました。それが最近は時間で信号を分けるシリアル方式になり、そのフォーマットをきれいに取り決めて I2C のような規格が誕生しました。

今ではたくさんのICメーカーがこの I2C 規格に賛同しています。

I2C通信では電源線(Vcc、GND)の他にシリアルデータ(SDA)とシリアルクロック(SCL)を使います。ICとICの間は4本の線をつなぎます。以前は10本とかつないでいましたので、基板上の配線がだいぶ省略されます。

また、 I2C は各ICに7ビットのアドレスを振るので、4本の線を使ってデイジーチェーンを作ることができます。数珠繋ぎというとわかりやすいかもしれません。

ですので、 I2C を使った場合基板上に I2C デバイスがたくさんあるほど配線の簡略化ができます。 I2C デバイスは比較的高価ですが、このような実装スペースの削減とのコストバランスで今後使用が広がるかもしれません。

I2C通信の使い方

I2C通信によってArduinoはいろいろなセンサーICとつながることができ、Arduinoは万能計測器になります。今日はADT7410で温度を測定してみます。それではがんばってやりましょう。

ケーブリング

ADT7410は秋月電子に変換基板実装済みモジュールが売っていますのでこちらが便利です。高いと思われる方はAnalog Devices社から無料のサンプルを請求することができます。

ADT7410のVDDはArduinoの5VへGNDはGNDへつなぎます。ArduinoのA4はSDAになるのでA4とSDA、A5端子はSCLになるのでA5とSCLをつなぎます。

IC単体でも使えますが、変換基板があった方がいいですね。ピンアサインを表示しておきます。

プログラミング

ではコード全体から見ていきましょう。

#include <Wire.h>                 //I2C通信のライブラリ  

int adt7410I2CAddress = 0x48;     //温度センサのアドレス

void setup(void) {               // 初期化
  Serial.begin(9600);            //シリアル通信開始
  Wire.begin();                  //I2C通信開始
}

void loop(void) {
  uint16_t uiVal; //2バイト(16ビット)の領域
  float fVal;
  int iVal;

  Wire.requestFrom(adt7410I2CAddress, 2);   //2バイト要求

  uiVal = (uint8_t)Wire.read() << 8;   // 1バイト読み出しで上位にシフト
  uiVal |= Wire.read();                 // 1バイト読み出して下位にセット

  uiVal >>= 3;                          // シフトで13bit化

  if (uiVal & 0x1000) {                // 13ビットで符号判定
    iVal = uiVal - 0x2000;             // マイナスの時 (10進数で8192)
  }
  else {
    iVal = uiVal;                      //プラスの時
  }


  fVal = (float)iVal / 16.0;           // 温度換算(摂氏)
  Serial.println(fVal, 4);             // シリアル送信 小数点以下4桁表示
  delay(1000);                         //1秒待つ
}

setup()の中で1回だけwire.begin()を実行します。センサーのコントローラーとして機器と接続する場合は()の中には何も書きません。

loop()の中ではじめにbeginTransmission()を実行します。()の中はICのアドレスです。2は2バイトのデータを要求するという意味です。

次にwire.read()でデータを1バイトごとに読み出しuiValにセットします。

ビット演算

ADT7410はここでビット演算が出てきますので、ちょっと複雑な計算が必要です。(あまりI2C通信の例としては適していなかったか・・・)

初めにWire.read()で1バイト読んで1バイト(8ビット)上位にシフトします。

uiVal = (uint8_t)Wire.read() << 8;   // 1バイト読み出しで上位にシフト

0000000010101010 → 1010101000000000 こんな感じ

次に次の1バイトを読んで論理和で足します。

uiVal |= Wire.read();                 // 1バイト読み出して下位にセット

1010101000000000 | 0000000011111111 = 1010101011111111 こんな感じ

2回目の1バイトの下3桁はデータが無いので3桁ビットシフトします。

uiVal >>= 3;  

1010101011111111 → 1010101011111 こんな感じ

uiValの13ビット目が1ならマイナスの温度で0ならばプラスの温度になっています。次のif文はプラス温度のときはそのまま、マイナス温度のときは8192℃を引きます。

最後に16で割っているのは温度係数です。このICを使うときは最後に16で割ります。

プログラムの書き込み

上記のプログラムがかけたら→ボタンを押してコンパイルと書き込みをします。

Arduinoに書き込めたら、シリアルモニターで送られてくるデータを見てみましょう。

温度のデータが見えました。

ADT7410を16ビットで測定する

前回はADT7410の13ビット温度測定を記事にしましたが、この温度測定ICは16ビット測定ができます。16ビット測定の場合は最小分解能は0.0078℃の測定が可能です。

16ビット測定する場合はコンフィグレーションレジスタ(0x03)の7ビットを1にします。

7ビットを1にするためにWire.write(0x00 | 0x80);としています。

#include <Wire.h>;
  
int I2CAdrs;
  
// 初期化
void setup(void) {
  I2CAdrs = 0x48;
  Serial.begin(19200);
  Wire.begin();       // マスタ初期化
  
  // ノーマル
  Wire.beginTransmission(I2CAdrs);  // 
  Wire.write(0x03);                 // Configuration register 選択
  Wire.write(0x00 | 0x80);          // 0110 0000
  Wire.endTransmission();           // 
}
  
// メインループ
void loop(void) {
  uint16_t val;
  float tmp;
  long int ival;
   
  Wire.requestFrom(I2CAdrs, 2);       // 
  val = (uint16_t)Wire.read() << 8;   // データの読み出し(上位)
  val |= Wire.read();                 // データの読み出し(下位)
  
  ival = (long int)val;
  if(val & 0x8000) {         // 符号判定
    // 負数
    ival = ival - 65536;
  }
  
  tmp = (float)ival / 128.0;
  Serial.println(tmp, 2); 
  
  delay(1000);
}

Wire.read()は8ビットずつ読まれるので<<8で一回目の読みを上位に8ビットシフトして下位8ビットを読みます。

またvalの先頭ビットが1だとマイナスの温度なので65536を引きます。

最後にivalには温度の128倍の値が入っているので128で割って正規化し、値をシリアルコンソールに表示します。

まとめ

この記事ではArduinoと温度測定IC ADT4710とでI2C通信をするプログラムを説明しました。ADT4710はビット演算があるのでちょっと難しい例だったと思って反省しています。しかし、I2C通信ではビット演算が必要になる場合があるので、ここで理解しておくのも役に立つかもしれません。

Arduinoを使った計測についてはまたサンプルがあったら紹介します。