この文書ではI2Cで制御できる温湿度センサについて説明します.PICなどのマイコンと比べてPi4JではI2Cを簡単に扱うことができるようになっています.簡単にできますので気楽に取り組んでください.
回路構成
下の図は温湿度センサ周りの回路図となっています.HDC1000には3本の端子があり,そのうち2本(SDAとSCL)がI2Cのためのものです.ともに330[Ω]の抵抗でプルアップされております.この2本を使って温湿度を測定する指令を出したり,HDC1000の設定を行ったり,測定結果を受信したりします.RDY端子は温湿度を測定し始めるとHighになり終わるとLowとなりますので,測定終了をRPiで検知するときに用いられます.
I2C
I2Cはオランダのフィリップス社が中心となり制定された,2線式シリアル通信に分類される通信規格であり,SDAではデータを送受信し,SCLではクロックを送ります.この規格では基本的に1個のマスタデバイスとN個のスレーブデバイスがあり,すべての送受信はマスタが主導して行いますので,マスタがSCLを通じてクロックを送信します.マスタから見たとき送受信の区別は各スレーブに設定してあるアドレスと一緒に送る1ビットの信号で決められており,例えばスレーブアドレスが0x24(7ビット)のデバイスにマスタからデータを送信する場合には,前述の0x24に続き,Low(=0)をスレーブに送信します.つまり,アドレス(7ビット)+送信(1ビット)=0x48(8ビット)というようにマスターからスレーブへ送信するのです.その結果,0x24のアドレスを持つスレーブだけが反応し,そのスレーブがSDAをLowにします.その後,マスタからデータをSDAへ送信するようになっています.マスタがスレーブからのデータを受信するときには,アドレスに続きHighを出力しますので,0x49を送信し,その後はスレーブからデータがSDAを通じて送られてきます.送られてくるタイミングはすべてSCLに則っていますので,マスタはそのクロックを出しつつデータを受信すればよいのです.
プログラム
上記ではI2Cの送受信の仕組みについて説明しましたが,Pi4Jではその辺のことをうまくオブラートに包んでくれているため,実際のところ,上記のような仕組みを知らなくても通信を行えてしまいます.ここではPi4JによるI2Cデバイスの制御方法について説明します.
まずはI2Cバスのインスタンスを取得するため,クラスI2CFactoryのスタティックなメソッドgetInstanceを用いてインスタンスi2c_busとします.getInstanceにはI2Cのバスを引数にする必要があります.今回はI2CBus.BUS_1を引数にします.次にi2c_busにあるメソッドgetDeviceを使ってHDC1000のインスタンスhdc1000を得ます.このときHDC1000のアドレスは0x40ですのでこれをgetDeviceの引数にします.そしてhdc1000にあるメソッドwriteでRPiからHDC1000へ初期設定データを送ります.HDC1000への指令は下表のレジスタアドレスをHDC1000へ送ります.
ポインタ | 名前 | リセット時の値 | 説明 |
---|---|---|---|
0x00 | 温度 | 0x0000 | 温度 |
0x01 |
湿度 | 0x0000 | 湿度 |
0x02 | コンフィギュレーション | 0x1000 | HDC1000の設定と状態 |
0xFB | シリアルID | デバイスによる | シリアルIDの最初の2バイト |
0xFC | シリアルID | デバイスによる | シリアルIDの中間の2バイト |
0xFD | シリアルID | デバイスによる | シリアルIDの最後の2バイト |
0xFE | 製造者ID | 0x5449 | Texas InstrumentsのID |
0xFF | デバイスID | 0x1000 | HDC1000のID |
この中にはコンフィギュレーションレジスタがありアドレス0x02に割り付けられており,このコンフィギュレーションレジスタには2バイトの値を設定できるようになっています.下表にコンフィギュレーションレジスタに設定できる2バイトの用途を示します.
ビット | 名前 | 用途 | 0 | 1 |
---|---|---|---|---|
15 | RST | ソフトウェアリセットビット | ノーマル動作 | 1を書き込むとリセットされます.リセット動作後 ノーマル動作に移行した場合,自動的に0になります. |
14~13 | Reserved | 予約ビット | 必ず0に設定してください. | |
12 | MODE | モード設定ビット | 温度,湿度16bitを個別に取りこみます. | 温度,湿度の順で32bitを一度に取り込みます. |
11 | BTST | 電源電圧状態表示ビット | 電圧>2.8[V](リードのみ) | 電圧<2.8[V](リードのみ) |
10 | TRES | 温度の分解能設定ビット | 14ビット分解能 | 11ビット分解能 |
9~8 | HRES | 湿度の分解能設定ビット | 00=14ビット分解能,01=11ビット分解能,10=8ビット分解能 | |
7~0 | Reserved | 予約ビット | 必ず0に設定してください. |
今回,温度と湿度を16bitを個別に取り込み,分解能は14bitにします.従いまして0x00, 0x00とします.まとめますと,コンフィギュレーションレジスタのアドレス0x02に続き,0x00と0x00をHDC1000へ送信すればよいですので,メソッドwriteは次のようになります.
I2CBus i2c_bus = I2CFactory.getInstance(I2CBus.BUS_1); I2CDevice hdc1000_pins = i2c_bus.getDevice(0b1000000); hdc1000_pins.write(new byte[] { 0x02, 0x00, 0x00 }, 0, 3);
HDC1000にはI2C以外にRDY端子を用いますので,これをインスタンス化して プライベートなフィールド _readyとします.この端子は通常の入力端子ですのでスイッチと同様にクラスGpioPinDigitalInputのインスタンスにしてください.
演習
上記のプログラムをクラスHDC1000のコンストラクタに記載してください.ただし,hdc1000_pinsは他のメソッドでも使用しますので,名称を変更し,プライベートなフィールド _hdc1000Pinsとしてください.下にクラスHDC1000の外枠のみ示します.
{code}package jp.ac.nanano_nct.ashida_lab.hdc1000; import java.io.IOException; import com.pi4j.io.gpio.GpioController; import com.pi4j.io.gpio.GpioFactory; import com.pi4j.io.gpio.GpioPinDigitalInput; import com.pi4j.io.gpio.PinPullResistance; import com.pi4j.io.gpio.RaspiPin; import com.pi4j.io.i2c.I2CBus; import com.pi4j.io.i2c.I2CDevice; import com.pi4j.io.i2c.I2CFactory; public class HDC1000 { private GpioPinDigitalInput _ready; private I2CDevice _hdc1000Pin; public HDC1000() throws IOException{ /* ここに記載 */ } } {highlight}{end-code}
次に温度を取得するメソッドgetTempをHDC1000に追加します.温度を得るにレジスタは先ほど示した表の中にあるように,アドレス0x00に割り当てられていますのでこの値をHDC1000へ送ります.すると計測が始まり,RDY端子がHighになります.計測が終わるとRDY端子がLowになりますのでそれまで待ちます.今回はRDYがLowになるまでずっとその処理を行うことにしたいので,_readyの中にあるメソッドisHighを用います.スイッチのプログラムの時には,イベントリスナを使ったものでしたが,今回のようにずっとピンの状態を見張り続けるスタイルをポーリングといいます.HDC1000で温度を取得できましたら,_hdc1000Pinsにあるメソッドreadを用いて温度データを得ます.このメソッドには3個の引数があり,第1引数にはデータを格納する配列を,第2引数には配列の開始番号を,第3引数には読み込みデータ個数を指定します.今回は2バイトのbyte型配列 _bufをプライベートなフィールドとし,第1引数に用います.そして第2引数は0,第3引数を2にします._buf[0]には上位8ビット,_buf[1]には下位8ビットが格納が格納されますので,これにもとづき int型のraw_dataとします.このとき,_bufはbyte型なのでキャストを忘れずに行ってください.
最後に得たデータを摂氏に変換します.変換式は下のとおりです.
{jmimetex}\frac{165 \times raw\_data}{2^{16}}-40{/jmimetex}
変換した結果をgetTempからreturnしてください.
演習
パブリックなメソッドgetTempをHDC1000に追加してください.