マスタリングWireライブラリ その2 温度センサMCP9808で温度測定

 前回、I2Cインターフェースの基本的なタイミングを見ました。今回からは、デバイスごとにWireライブラリを使って読み書きします。
 センサMCP9808の温度レジスタ(ポインタ)の読み出しは、最初に、レジスタを指定した後、読み出しだけを繰り返しました。これはマニュアルにも書かれていた、一度レジスタを設定したら、再度指定する必要はないという理由です。逆に、読み出しのたびにレジスタを指定しても、温度を読み出す動作に変わりはありません。

MCP9808のおもなスペック

 電源電圧は2.7~5.5Vなので、5Vもしくは3.3VのArduinoで利用できます。I2Cインターフェースのデータ転送速度は0~400kHzです。Wireライブラリのデフォルトは100kHzで、高速の400kHzにはWire.setClock(400000)で変更できます。

 測定温度範囲は-40~125℃の範囲で確度±0.25°C(typ)です。データシートのAccuracyは日本では精度と訳すことが多いですが、ここでは確度という言葉を使います。typはtypicalの省略形で、代表値のことです。データシートではmin(minimal;最小値)、typ、max(maxim;最大値)が書かれていることが多いです。設計者はminの値に注目します。分解能の最大は+0.0625°C(12ビット時)です。

 利用したプリント基板はAdafruitです。スイッチサイエンスなどから入手できます。Adafruitではブレーク・アウト・ボードと呼んでいます。同様な製品はebayでもたくさん見つかります。デバイス単体はDigi-Keyでも購入できます。

接続

 プリント基板上にA0、A1、A2のスレーブ・アドレスを設定する端子があります。回路図から、10kΩでプルダウンされているので、Lowです。データシートから0011000=0x18です。0011は固定されていて、下位3ビットのA0からA2のいずれかをVddにつなぐことでアドレスを変更できます。

 SDAとSCLも10kΩでプルアップされています。ArduinoでこのMCP9808だけをつないで利用するときは、このまま使います。ほかにI2Cデバイスを使うときは、取り外したほうがよいでしょう。ラズパイでは本体側で1.8kΩでプルアップされているので、実験時に取り外しました。はんだゴテでチップ抵抗R1とR2を温めて外します。新規に購入した場合は、そのまま使います。チップ抵抗の表面には1002と書かれています。100×10^2=10000Ωです。

 取り去った抵抗の代わりに追加したプルアップ抵抗の2本は6.8kΩを使いました。4.7~10kΩの範囲の抵抗値なら何でもかまわないと思います。電力は1/2W、1/4W、1/6Wのいずれでも、炭素皮膜抵抗もしくは金属皮膜抵抗のどちらでもかまいません。電源5VとグラウンドGND間には、0.1uF(104)の積層セラミック・コンデンサと10~47uFのアルミ電解コンデンサを入れます。電解コンデンサはマーキングがあるほうがマイナス端子です。図では半円形のグレーのマーキングがあるので、その端子をGNDへつなぎます。黒色や茶色の電解コンデンサであれば、白い帯のマーキングがあります。コンデンサの耐圧は16~50Vの製品を使います。

 二つのコンデンサは、電源のデカップリング用です。ノイズを軽減し、安定な動作が望めます。なくても動きます。

 図では空中配線になっていますが、実際はブレッドボードを利用して配線します。

スケッチとタイミング

 最初に、MCP9808スレーブ・アドレス0x18と温度ポインタの0x05を送ったのちストップ・コンディションになります。そのあと、2バイトの読み出しを行うために、再度スタート・コンディションになります。最初に読んだ1バイトが温度データの上位バイト、次が下位バイトであることが確認できます。デバイスによっては、上位と下位が逆になっていることがあります。

 ここで、温度ポインタ0x05を送った後Wire.endTransmission();を忘れるとどうなるかを見ます。スケッチでは0x05を送ったように見えますが、実際は読み出しだけが行われています。読み出しでは、正常な値を読み込めているようです。

 ここで、MCP9808の電源を入れなおすと次の意味不明な波形が観測できました。先ほど正常な読み出しができていたのは、温度ポインタが一度書き込まれていたので、継続してその指定が有効だったため、温度データが読めていたようです。

 Wire.endTransmission();を有効にし、読み出すデータを2バイトから5バイトに変更してみます。温度データ2バイトに続いて3バイト読み込んでいます。I2Cでは、連続読み出しをするとき、スレーブ側のポインタが自動でインクリメントされる思っていました。データシートによれば、温度データ2バイトの次には製造者IDが2バイト、デバイスIDが続きます。しかし、正しくないので、ポインタは正しく指定して2バイトずつ読み出す必要があるようです。

温度を得る

 読み出した2バイトのデータは、次の符号付き2の補数形式です。

 temp1にはbit15~bit8が、temp2にはbit7~bit0が入っています。bit15~bit13は不要なので、

temp1 & 0b00011111

 

でマスクします。C言語の演算子&は論理積で、temp1 と0b00011111のどちらのビットも1なら1に、そうでないときは0になります。したがって、上位3ビットは何であっても強制的に0になり、残りの5ビットは、もとの1/0が変化しません。

 Arduinoのintは16ビット長です。読み出したのは1バイト=8ビットなので、上位1バイトは全部0でうずめられています。8回左へビット・シフトします(256を乗じても同じ)。そうすると、上位1バイトへ読み出したデータが移動します。

(temp1 & 0b00011111)<<8

 下位8ビットが空いた状態なので、そこへtemp2を加算すると、符号1ビット+12ビットのデータが作られます。

tempSign = (temp1 & 0b00011111)<<8 + temp2

 図で説明します。

 温度データは符号付き2の補数形式で入っています。bit4が2^0=1℃単位、小数点以下は、bit3が2^-1=0.5℃単位、bit2が2^-2=0.25℃単位、bit1が2^-3=0.125℃単位、bit0が2^-4=0.0625℃単位です。したがって、12ビット・データに0.0625を乗じてスケーリングすると、温度temperature が得られます。tempは符号を除いた12ビット・データです。

temperature = temp * 0.0625

 2の補数形式では、0は0x000、正のフルスケールは0x7ff、マイナスのフルスケールは0x800です。温度はフルスケールにはなりません。身近な温度のバイナリ値を計算しました。

温度[℃] バイナリ 16進
50 0011 0010 0000 320
25 0001 1001 0000 190
0.25 0000 0000 0100 004
0 0000 0000 0000 000
-0.25 1111 1111 1100 ffc
-5 1111 1011 0000 fb0
-25 1110 0111 0000 e70

 ここまでのスケッチでは、マイナスの温度は考慮しませんでした。bit12は符号です。1ならばマイナスです。任意のビットが1かどうかをテストする命令がないので、13ビット目の符号を含むtempSignを右に12回シフトすると、bit12だけが残るのでif文で判断します。マイナスの2の補数を10進に直すには、ビット反転し1を加えます。12ビットを扱っているので、適時マスクをかけます。

if (tempSign >>12 ) temp = -( (~temp & 0x0fff) +1);

#include <Wire.h>
unsigned char MCP9808_address = 0x18;
unsigned char Temp_register = 0x05;

void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("start ");
}

void loop() {
Wire.beginTransmission(MCP9808_address);
Wire.write(Temp_register);
Wire.endTransmission();
Wire.requestFrom(MCP9808_address, 2);
int temp1 = Wire.read();
int temp2 = Wire.read();
int tempSign = ((temp1 & 0b00011111 ) << 8 ) + temp2;
int temp = tempSign & 0x0fff;
if (tempSign >>12 ) temp = -( (~temp & 0x0fff) +1);
float temperature = temp * 0.0625 ;
Serial.println(temperature);
}

(※1)Arduino IDEは1.8.5を利用しています。プログラム・リストは、表示の関係でTabキーが無視されるので、スペースに代えてあります。また、リスト中を2回クリックすると全選択になるので、CTRL-Cでコピーし、テキスト・エディタにCTRL-Vで貼り付けて利用してください。エディタに持っていくと、スペースやリターン・コードなどが化けていることがあるので、一度消して、Arduino IDEのテキスト・エディタで修正してください。

前へ

マスタリングWireライブラリ その1 スタートとストップ・コンディション

次へ

マスタリングWireライブラリ その3 温度センサMCP9808のアラート