Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
.NET対応組み込みデバイス「Netduino」入門(4)

.NET対応組み込みデバイス「Netduino」入門(4)

Netduinoシリアル通信(I2C)の基本

2015年5月12日

アナログ入力の次はシリアル通信(I2C)の基礎を習得しよう。I2C対応の温度センサーの値をNetduinoで取得するサンプルを作成する。

Microsoft MVP for Windows Platform Development 初音 玲
  • このエントリーをはてなブックマークに追加

 前回で、アナログ入力の基本を解説した。

 今回は、Netduinoのもう一つの入出力機能であるI2C通信について取り上げる。I2Cは周辺機器との間で特定バイト長のバイナリ値としてデータをやりとりする。

I2Cとは

 I2CはInter-Integrated Circuitの略であり、アイ・スクエア・シーまたはアイ・ツー・シーと呼ばれるシリアルバス規格だ。

 I2Cで使われるのはSDA(シリアルデータ)とSCL(シリアルクロック)の2つであり、この2つに複数の入力機器(センサーなど)と出力機器(モーターや液晶パネル)をつなげることができる。

 各機器(I2Cではノードと呼ぶ)の識別は、個々のノードに割り当てたアドレスにより行う。アドレスは、7bitのアドレス空間すなわち128個のアドレスから16個の予約アドレスを除いた112ノードを同時に接続できる。

 しかしながらアドレス空間の割り当て機関などは存在しないため、各メーカーが独自に使用アドレスを決定し、アドレスがバッティングしたときのためにディップスイッチやジャンパー接続によりアドレスを変更できるようにしている場合が多い。そのため112ノードを接続しようとするとかなり綿密に機器を選択するなどの工夫が必要になる。

NetduinoのI2C

 Netduinoのボードの左上にI2C関連のピンがある(図1)。SCSCL(シリアルクロック)ピンで、SDSDA(シリアルデータ)ピンだ。

図1 Netduinoボード上のI2C関連のピン

ADT7410温度センサーモジュールについて

 今回のサンプルでは、I2C対応機器としてADT7410温度センサーモジュールという温度センサー(図2)を使って室温を測定する。

図2 温度センサーモジュール「ADT7410」

 このADT7410モジュールは、購入後に基盤とピンを合計4カ所はんだ付けしなければならない。本連載では極力はんだ付けはしたくなかったのだが、手ごろな価格でピンがはんだ付け済みのものを探したが見つけることができず、比較的はんだ付け箇所が少なくて作業しやすいものを選択したので、この点は了承してほしい(はんだごてが手元にない場合は、「Amazon.co.jp: goot電子工作用はんだこてセット X-2000E」などを購入すると、全てそろっているので便利だ)。

 多少はんだ付けの手間はあるが、ADT7410温度センサーモジュールは、温度センサーとしてADT7410を採用し、パスコン(図3のC1)やプルアップ抵抗(図3のR1やR2)など必要な周辺部品がすでに組み込まれている(図3)。

図3 温度センサーモジュール「ADT7410」の回路図(引用元: ADT7410のマニュアルPDF

 ADT7410の性能としては、測定レンジは-55~150℃、測定誤差の最大値は±0.5℃である。また、消費電力は最大210μA(25℃時)だ。

 なお、測定した温度は、ADコンバーターで13bit(デフォルト値)のデジタル値に自動変換される。よって1bitあたり0.0625℃(デフォルトの13bitモード時)の分解能がある。前回使ったLM16CIZからの入力が0~3.3Vを1024段階で取得するアナログ入力の制限から3mV=0.3℃の分解能であったのと比較すると、ADコンバーターでの丸め誤差も少なく正確な温度測定が期待できる。

Netduino+ADT7410モジュールのハード構成

ADT7410モジュールの物理仕様

 ADT7410モジュールには4つの足と4つのジャンパーがある(図4)。

足の役目
図4 足の役目(引用元: ADT7410のマニュアルPDF

 ジャンパーJ3とジャンパーJ4をそれぞれはんだ付けすることでアドレスを変更できるが、今回ははんだ付けせずアドレスは「0x48」を用いる。

 ジャンパーJ1とジャンパーJ2をそれぞれはんだ付けすることでSCLSDAをNetduinoで使うときに必要なプルアップ抵抗を有効にできる。今回は、ここもはんだ付けせず別途ブレッドボード上にプルアップ抵抗を使用する。基板上のプルアップ抵抗を使わないのは、はんだ付けしてしまうとプルアップしたくないときに対応できないからであり、それ以上に深い理由はない。

 ADT7410モジュールとNetduinoを接続するときには、図4の上から、Netduinoの5Vピン、SCピン、SDピン、GNDに接続すればよい。

ADT7410モジュール接続回路図

図5 ADT7410モジュール接続回路図

 I2Cはシリアル通信なので、流れるデータはOnとOffのビットデータとなる。Onのときには5V、Offのときには0Vになる。Netduinoで使用するときはプルアップ抵抗を入れることでOnとOffの電圧値がより安定する。

 ADT7410モジュール自体でもプルアップ抵抗を有効にできるが、ジャンパーをはんだ付けしてしまうとプルアップ抵抗が不要なときに対処できないので、ADT7410モジュールの外側で明示的にプルアップ抵抗を回路に組み込むことにした。

 プルアップ抵抗値は、ADT7410モジュールでも採用している10kΩを使用する。ADT7410の2番ピン(=SDA)と3番ピン(=SCL)を、Netduinoを接続するラインから分岐するように抵抗を挟んで+5Vラインに接続する。

ブレッドボード実装

 回路図を基にしてブレッドボードに部品を配置する。

図6 ADT7410モジュール・ブレッドボード配置

 Netduinoからの5Vラインは赤いラインを通ってブレッドボードの一番下のプラス電源ラインGNDからの線も同様に青いラインを通って下から二番目のGNDラインに接続する。

 A10E10は内部的に接続されているのでADT7410モジュールのVDDに+5Vを供給する。GNDもA7からE7の内部結線を経由してADT7410モジュールのGNDに接続する。

 I2C特有のSCLSDAの2つの信号線は、SCLがC8、SDAがB9でNetduinoに接続している。また、プルアップ用の10KΩ抵抗がA8A9からプラス電源ラインの間を接続している。

 実際に接続すると次のようになる。

図7  ADT7410モジュール実装写真

Netduino+ADT7410モジュールのプログラミング

 回路が完成したら、アプリのプログラミングを始める。

 最初に行う手順は、Visual Studioの[新しいプロジェクト]ダイアログで[Micro Framework]カテゴリの[Netduino Plus 2 Application]テンプレートを選択して、新規にプロジェクトを作成することだ(本稿の例では、プロジェクト名はVB.NET用は「ADT7410TemperatureVB」、C#用は「ADT7410TemperatureCS」とした)。

センサー値の理解

 ADT7410はレジスタと呼ばれる1byte(=8bit)の領域を介して値のやりとりを行う。レジスタは全部で13個ある(表1)。

レジスタアドレス レジスタ名 内容
0x00 Temperature value most significant byte 温度値(上位)
0x01 Temperature value least significant byte 温度値(下位)
0x02 Status ステータス
0x03 Configuration 設定
0x04 THIGH setpoint most significant byte 高温限界(上位)
0x05 THIGH setpoint least significant byte 高温限界(下位)
0x06 TLOW setpoint most significant byte 低温限界(上位)
0x07 TLOW setpoint least significant byte 低温限界(下位)
0x08 TCRIT setpoint most significant byte クリティカル限界(上位)
0x09 TCRIT setpoint least significant byte クリティカル限界(下位)
0x0A THYST setpoint  
0x0B ID  
0x2F Software reset ソフトウェアリセット
表1 ADT7410レジスタ

 温度データは0x000x01の2つのレジスタ、つまり2byte値で取得できる。2byteの中のデータフォーマットには、13bitモード16bitモードの2つのデータフォーマットがある。

図8 測定値のデータフォーマット

 どちらのモードでも「0℃=0」(13bitモード時は3ビット右シフトが必要)となる。初期設定は13bitモードだが、この連載では設定変更して16bitモードで使用する。16bitモードにするには、後述する設定レジスタ(0x03)に「0x80」を指定して設定変更を行う。

 16bitモードでの測定値を実際の気温に変換するには、以下のように変換を行う。

気温[℃]=測定値/128

 また、最上位bitが符号なので、「0x8000」(=32768)以上の場合はマイナス気温を表す。この場合、測定値から「65536」(=32768×2)を減算してから「128」で除算する。

 初期設定では高温限界が64℃、低温限界が10℃、クリティカル限界が147℃に設定されている。それぞれ限界値を越えると、INT端子やCT端子がアクティブ(0V→3.3Vまたは3.3V→0Vが流れる)になる。今回使用したADT7410モジュールではINT端子やCT端子が未使用になっているが、使用可能なモジュールを利用してINT端子やCT端子を監視することで、限界超えのときにアラートを上げるような回路も組むことが可能だ。

ADT7410の設定

 ADT7410の設定初期は以下の通りである。この設定は、設定レジスタ(0x03)により変更できる。

設定レジスタ
図9 設定レジスタ

 13bitモードと16bitモードの違いは測定値のところで説明した。16bitモードにしたいときは「0x80」を設定すればよい。

 動作モードもADT7410の特徴的な機能だ。具体的には以下の動作モードがある。

  • ノーマルモード: ノーマルモードでは連続して温度を計測する。計測した温度をAD変換するのに要する時間は240msなので500ms間隔以上でループしながらレジスタ0x000x01を読み取れば温度測定値を得られる。ノーマルモードの場合、常に210μAの電流が流れるため700μWの消費電力となる。
  • ワンショットモード: 測定時だけ測定用に回路に通電し、測定が終わると必要最小限の消費電力に落とすモードだ。このモードに設定したら最低でも240msの待ち合わせ後にレジスタからデータを取得する。再度測定するにはもう一度、ワンショットモードに設定レジスタを設定する。測定するときにだけ210μAの電流が流れるがそれ以外のときは2μAまで落ちる。2μAの場合の消費電力は7μWと100分の1になるため測定間隔を調整することで電池稼働時などの稼働時間を長くできる。
  • 1SPSモード: 1SPS(1サンプル/秒)モードは1秒間に1回だけAD変換を行うモードである。1秒以上の間隔での測定でよければ、ノーマルモードではなく1SPSモードにすることで46μAの電流しか必要でなくなり消費電力も180μW程度に抑えられる。

I2C用クラス作成

 I2Cを使う場合、単にピンの値を取得するのではなく、レジスタの指定や指定バイト数を読み取るなど、I2Cの約束事に合わせたコードが必要になってくる。そこで今後の再利用も考えて、まずはI2C用クラスを作成しておく。

 ソリューションエクスプローラーでプロジェクト名を右クリックして(表示されるコンテキストメニューの)[追加]-[クラス]でダイアログを表示して「I2CLib」という名前で新規にクラスファイルをプロジェクトに追加する。クラスファイルには次のコードを記述する。

Visual Basic
Imports Microsoft.SPOT.Hardware

Friend Class I2CLib
  Implements IDisposable
 
  Private I2c As I2CDevice
  Private Timeout As Integer
  Private I2cAddr As UShort
 
  Public Sub New(addr As UShort, clockRate As Integer, timeout As Integer)
    Me.I2cAddr = addr
    Me.Timeout = timeout
    Me.I2c = New I2CDevice(New I2CDevice.Configuration(addr, clockRate)) ' ……1
  End Sub
 
  Private Sub Write(buffer As Byte())
    Dim tx() = New I2CDevice.I2CWriteTransaction() {I2CDevice.CreateWriteTransaction(buffer)}
    Dim result = Me.I2c.Execute(tx, Me.Timeout)
    ' ……中略……
  End Sub
 
  Private Sub Read(buffer As Byte())
    Dim tx() = New I2CDevice.I2CReadTransaction() {I2CDevice.CreateReadTransaction(buffer)}
    Dim result = Me.I2c.Execute(tx, Me.Timeout)
    ' ……中略……
  End Sub
 
  Protected Sub WriteToRegister(register As Byte, value As Byte)         ' ……2
    Me.Write(New Byte() {register, value})
  End Sub
 
  Protected Function ReadToRegister(register As Byte, length As UShort) As Byte() ' ……3
    Dim buffer(length - 1) As Byte
 
    Me.Write(New Byte() {register})
    Me.Read(buffer)
    Return buffer
  End Function
 
#Region "IDisposable Support"
    ' ……中略……
#End Region
 
End Class
C#
using Microsoft.SPOT.Hardware;
using System;
 
namespace ADT7410TemperatureCS
{
  internal class I2CLib : IDisposable
  {
    private I2CDevice I2c;
    private int Timeout;
    private ushort I2cAddr;
 
    public I2CLib(ushort addr, int clockRate, int timeout)
    {
      this.I2cAddr = addr;
      this.Timeout = timeout;
      this.I2c = new I2CDevice(new I2CDevice.Configuration(addr, clockRate)); // ……1
    }
 
    private void Write(byte[] buffer)
    {
      var tx = new I2CDevice.I2CWriteTransaction[] { I2CDevice.CreateWriteTransaction(buffer) };
      var result = this.I2c.Execute(tx, this.Timeout);
      // ……中略……
    }
 
    private void Read(byte[] buffer)
    {
      var tx = new I2CDevice.I2CReadTransaction[] {I2CDevice.CreateReadTransaction(buffer)};
      var result = this.I2c.Execute(tx, this.Timeout);
      // ……中略……
    }
 
    protected void WriteToRegister(byte register, byte value)   // ……2
    {
      this.Write(new Byte[] { register, value });
    }
 
    protected byte[] ReadToRegister(byte register, int length)  // ……3
    {
      var buffer = new byte[length];
 
      this.Write(new byte[] { register });
      this.Read(buffer);
      return buffer;
    }
 
    #region "IDisposable Support"
      // ……中略……
    #endregion
  }
}
リスト1 I2C連携のためのサンプルコード(上:I2CLib.vb、下:I2CLib.cs)

ここではポイントだけ掲載している。省略されている箇所は、サンプルコードをダウンロードして、該当ファイルを参照してほしい。

  • 1I2Cデバイスのアドレスとクロックレートを指定。
  • 2レジスタへの出力(I2Cデバイスへの出力)は、レジスタアドレスと値を連続して出力する。
  • 3レジスタからの入力(I2Cデバイスからの入力)は、レジスタアドレスを出力後に値を入力する。

ADT7410用クラス作成

 上記のI2CLibクラスをそのまま使ってもよいが、I2CLibクラスを継承してADT7410Libクラスを作ってADT7410特有の部分を隠ぺいしてから使うことにする。具体的には次のようなコードになる。

Visual Basic
Private Class ADT7410Lib
  Inherits I2CLib
 
  Private Enum Registers As Byte
    TEMP_MSB = &H0
    TEMP_LSB = &H1
    CONFIGRATION = &H3
  End Enum

  Public Sub New()
    MyBase.New(&H48, 100, 500)                       ' ……1
    Me.WriteToRegister(Registers.CONFIGRATION, &H80) '16bitモード指定 ……2
  End Sub
 
  Public Function ReadTemperatre() As Single
    Dim data() As Byte = Me.ReadToRegister(Registers.TEMP_MSB, 2) ' ……3
    Dim val As Long = CType(data(0), Long) * 256 + CType(data(1), Long)
 
    If (val >= 32768) Then
      val = val - 65536  '負数計算
    End If
    Return CType(val, Single) / 128.0F               ' ……4
  End Function
End Class
C#
private class ADT7410Lib : I2CLib
{
  private enum Registers : byte
  {
    TEMP_MSB = 0x0,
    TEMP_LSB = 0x1,
    CONFIGRATION = 0x3
  }
 
  public ADT7410Lib()
    : base(0x48, 100, 500)                           // ……1
  {
    this.WriteToRegister((byte)Registers.CONFIGRATION, 0x80); //16bitモード指定 ……2
  }
 
  public Single ReadTemperature()
  {
    byte[] data = this.ReadToRegister((byte)Registers.TEMP_MSB, 2); // ……3
    long val = (long)data[0] * 256 + (long)data[1];
 
    if (val >= 32768)
    {
      val = val - 65536;  //負数計算
    }
    return (Single)val / 128.0F;                     // ……4
  }
}
リスト2 I2C連携のためのサンプルコード(上:Module1.vb、下:Program.cs)

Visual BasicはModule1モジュール内に、C#はProgramクラス内に、上記のクラスを追記する。

  • 1I2Cデバイスのアドレス(0x48)とクロックレート(100kHz)を指定。
  • 2設定レジスタに「0x80」を指定して16bitモードに設定。
  • 3測定値レジスタ0x000x01の2バイトを取得。
  • 4「128」で除算し気温を取得。

 あとは、Mainメソッドで上記のADT7410Libクラスのインスタンスを生成して、ReadTemperatreメソッドを呼び出して気温の値を取得すればよい。その実装方法は本論ではないので、サンプルコードを参照してほしい。

アプリ実行

 以上でサンプルアプリは完成だ。実際に動かしてみると、Visual Studioの[出力]ウィンドウに気温が表示される。出力値を前回のLM61の値と一緒にグラフ化してみる。

図10 測定結果

 ADT7410での測定値の方が±0.1℃の範囲内に揺れが収まっている。

まとめ

 I2Cで接続する場合、プログラムコード自体はアナログ入力よりも複雑だが今回のようにI2Cクラスやセンサー固有クラスを作ることでその部分は解消できる。

 そうなってくると、I2Cは測定精度もよく値の扱い方もPCなどでの通信に近いものがあるのでなじみやすい。アドレス競合があるので同種センサーを大量に接続するとかは難しいが、複数機器が電源込みで4本の接続線でNetduinoにつながるのは魅力的である。

 次回はADT7410で接続したものをブレッドボード上に配置した液晶画面に表示する方法を紹介する。使用する部品としてはAQM0802Aを予定している。

※以下では、本稿の前後を合わせて5回分(第1回~第5回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

.NET対応組み込みデバイス「Netduino」入門(4)
1. Netduino、知ってますか?

電子工作キットの“Arduino”と“Netduino”は何が違うのか? Netduinoの概要と、開発環境の準備方法を解説する。

.NET対応組み込みデバイス「Netduino」入門(4)
2. Netduino、まずは基本のLチカ

初めてのNetduinoアプリ開発。まずは基本の基本であるオンボードのLチカから。次にブレッドボード上の外部LEDのLチカも行う。また、ファームウェアの更新方法も説明する。

.NET対応組み込みデバイス「Netduino」入門(4)
3. Netduinoアナログ入力の基本(温度センサー活用)

Lチカができたら、アナログ入力を使ってみよう。温度センサーの値をNetduinoで取得するサンプルを作成し、アナログ入力の基礎を説明する。

.NET対応組み込みデバイス「Netduino」入門(4)
4. 【現在、表示中】≫ Netduinoシリアル通信(I2C)の基本

アナログ入力の次はシリアル通信(I2C)の基礎を習得しよう。I2C対応の温度センサーの値をNetduinoで取得するサンプルを作成する。

.NET対応組み込みデバイス「Netduino」入門(4)
5. Netduinoシリアル通信(I2C)でLCD表示

シリアル通信(I2C)をさらに学ぼう。I2C通信で液晶ディスプレイ(LCD)に文字を出力するサンプルを作成する。

サイトからのお知らせ

Twitterでつぶやこう!