温度を測る その6 0.1℃まで測りたい

テレビで今日の朝の気温は0.3℃でしたと

 小数点第1位を表示するには、小数点第2位を四捨五入します。温度センサLM335Zはアナログ出力ですから温度データの分解能を上げることは可能です。しかし、Arduino UNOで使っている内蔵の10ビットのA-Dコンバータでは、細かさが足りません。

 0 [℃] =     2.7315[V]
 0.01[℃] = 2.7316[V]
 0.02[℃] = 2.7317[V]

 小数点第4位目を正しく表示するにはA-Dコンバータは小数点5位のデータを取得しなければなりません。つまり、10uVの桁が必要です。

 基準電圧を5Vとしたとき、細かな電圧の最小単位LSBはA-D変換のビット数に関係します。

ビット数 10 12 16 18 20 24
LSB 4.89mV 1.22mV 76.3uV 19.1uV 4.8uV 0.30uV

 20ビットのA-Dコンバータが必要です。20ビットのA-Dコンバータは品種が少なく、製品としては24ビットが入手しやすいです。測定するものがゆっくりと変化する温度なので、A-Dコンバータの変換速度は遅くてかまいません。

 もう一つ問題があります。LM335Zの確度は非校正時2℃です。このデバイスは、次の回路で25℃のときに2.982Vに合わせると、校正ができます。校正後は、温度係数の直線性エラー0.3℃の誤差範囲で温度が測れます。

 現実的な話として、校正するのは費用がかかるので、ここでは省略します。

手軽な24ビットA-Dコンバータ・ボードを入手

 24ビットのデータ変換を正確に行うのは不可能です。その理由は、

  • データを取り込むマイコンがノイズを発生して電源や入力に飛び込んでランダムな誤差を発生する
  • マイコン・ボードのグラウンドが常に変動する
  • 基準電圧が温度で変動する
  • 基準電圧源に電源からノイズが混入して値が変化する
  • AC100Vの誘導ノイズの影響を受けて周期的にデータが変動する

 程度の差はありますが、上記の問題を対策しないと、電源をつないだだけでは24ビットのデータの多くは不確かなデータになってしまいます。がんばって対策しても20ビット程度が限界です。

 最近のA-Dコンバータは、

  • 温度変動の少ない基準電圧源
  • AC100Vの誘導ノイズ50/60Hzのフィルタ

などを内蔵しているモデルがあります。そのようなICを使うと、より24ビットのデータを扱いやすいです。ここではebayで購入した、A-DコンバータLTC2400+基準電圧源REF3040を搭載したボードを利用します。約2500円でした。基準電圧源を内蔵したICはADS1220などがありますが、試作用ボードの形では入手できないようです。

ボードのスペック

 基準電圧源に4.096VのREF3040を用いているので、Vin1端子の入力電圧は0~4.096Vです。分圧抵抗が用意されているので、Vin2端子は17Vまで入力できます。誘導ノイズ50/60Hzのどちらを取り除くかのジャンパがあります。関東なので50Hzの設定にしました。

 マイコンとのデータ転送はSPIバスを使います。ArduinoにはSPIのライブラリがありますが、ダウンロードできる同ボードのデモ・プログラムは、SPIのライブラリを使わず、直接マイコンを操作しています。

接続とテスト

 Arduino UNOのSPI端子付近に挿し込んで使います。ボード上面には5V電源と入力があります。5Vはローノイズの電源をつなぐのがベターですが、実験ではArduino UNOの5V端子をつなぎます。

 Vin1にArduino UNOの3.3Vをつないで、デモ・プログラムを動かします。3か所修正します。

//#include <Stdio.h>
//#include<stdlib.h>
Serial.begin(9600);

 include文をコメントアウトします。シリアル・モニタの速度をデフォルトの9600に変更します。また、必要ならば表示の小数点がカンマなので、ピリに変更します。実行結果です。

SPIライブラリを利用したスケッチ

 デモ・プログラムは、AVRマイコンを直接たたいて動かしていました。ArduinoにはSPIのライブラリがあります。チップ・セレクト信号は自分で制御しますが、データ転送を行えば、クロックを作ってくれます。

 SPI.transfer(&buf,4); を使えば、変換データを取り込めます。LTC2400は、特にレジスタの設定を行う必要がなく、32ビットのデータを読み込めばOKです。先頭の4ビットはステータス関連で、残り28ビットがA-D変換された結果です。読み込むと、バイトが逆順でした(buf[0]に0xEC850028)。

 読み込んだ32ビットのデータを逆に並び替えます。

buf[1] = ((buf[0] & 0x0000ffff) << 16) | ((buf[0] >> 16) & 0x0000ffff);
buf[2] = (( buf[1] & 0x00ff00ff) << 8) | (( buf[1] >> 8) & 0x00ff00ff);

 上位4ビットを捨てたデータに基準電源の電圧Vrefを掛け、2^28-1で割れば電圧が求まります。24ビット以上のデータが読めます。

 volt = (buf[2] & 0x0fffffff)*Vref/(16777216.0*16.0 );

 符号はbit29にあるので、1なら正、0なら負なので、負のときにマイナス符号をつけます。

if (!(bitRead( buf[2] , 29) ) ) volt = -1*volt ;

 少しでもノイズや誘導ノイズの影響を減らすために、9回分の平均を表示しました。

 スケッチです。

#include <SPI.h>
#define SS 10 // LTC2400 Chip Select
#define MISO 12 // LTC2400 SDO
#define SCK 13
int32_t buf[4];
float Vref = 4.095 ;
#include <i2clcd.h>
i2clcd lcd(0x3e, 5);
float volt;

void setup() {
Serial.begin (9600);
SPI.setBitOrder(MSBFIRST);
SPI.begin();
pinMode(SS,OUTPUT);digitalWrite(SS,1);
lcd.init_lcd();
}

void loop() {
float d[10];
for (int i=0; i < 10; i++){
delay(150);
digitalWrite(SS,0);
SPI.transfer(&buf,4);
buf[1] = ((buf[0] & 0x0000ffff) << 16) | ((buf[0] >> 16) & 0x0000ffff);
buf[2] = (( buf[1] & 0x00ff00ff) << 8) | (( buf[1] >> 8) & 0x00ff00ff);
volt = (buf[2] & 0x0fffffff)*Vref/(16777216.0*16.0 );
if (!(bitRead( buf[2] , 29) ) ) volt = -1*volt ;
// Serial.println(" sokuteichi "+String(volt,6));
d[0] = volt ;
d[9]=d[8];d[8]=d[7];d[7]=d[6];d[6]=d[5];d[5]=d[4];d[4]=d[3];d[3]=d[2];d[2]=d[1];d[1]=d[0];
digitalWrite(SS,1);
SPI.endTransaction();
} //for
lcd.lcdclear();
float Averagevolt = (d[1]+d[2]+d[3]+d[4]+d[5] +d[6]+d[7]+d[8]+d[9]) /9;
Serial.println(" Avrage "+String(Averagevolt,6));
lcd.lcdcu_set(0, 0);lcd.i2cprint( "LTC2400="+String(volt,6) );

}

実行結果

 測定する電圧は、電池駆動の基準電圧用ICのTL431出力で、約2.48Vです。室温付近で短時間にかぎれば電圧は動きません。

 Arduino UNOをPCとUSBでつなぎ、Arduino UNOの5VをA-Dコンバータ・ボードの電源とした実験環境では2.485までの数字は変化しないのですが、その下3桁がランダムに変動します。こちらの解説によると、ほぼ12ビットのA-Dコンバータの性能に等しいようです。

 LTC2400ボードの電源端子にエネループ4個の約5Vをつなぎました。LTC2400の最大電源電圧は7Vなので、充電直後でもここまで高い電圧は出ないと思います。

 そうすると、下2桁が変動する状態になりました。A-Dコンバータ用電源の安定度などが変換データに影響を及ぼしていることがわかります。次に9回測定値を平均したプログラムの結果を見ます。下2桁が変動しますが、変化は落ち着いています。ときどき下1桁の変動にとどまることがあります。

 PCからケーブルを抜き、Arduino UNOをモバイル・バッテリで駆動します。改善されません。全体を金属ケースに入れればより安定な測定ができるかもしれません。

 次は確度です。

 DMMの読みは2.49226Vです。LTC2400ボードの読みは2.484957V前後と相当異なります。Vrefの電圧をDMMで読みます。4.10672V前後でした。これをスケッチのVrefに書き換えます。その結果、2.49218V前後を表示しました。DMMとの誤差は0.0032%(32ppm)なので、確度は高いかもしれません。

温度

 Vin1をLM335Zの出力+端子につなぎ、温度に換算します。

lcd.lcdcu_set(0, 1);lcd.i2cprint( "Temp="+String((volt*100-273.15),2) );

 内蔵A-Dコンバータではころころと値が変わっていましたが、小数点第2位が安定に表示されています。

 これで、十分な確度で0.1℃単位の表示ができたでしょうか。興味のある方は検証してみてください。

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

(※2)DMMは岩崎通信VOAC7602を使いました。

(※3)本記事は、むりやりビット数の多いA-Dコンバータを使ってみたいという記事で、24ビットA-Dコンバータが本当に必要なわけではありません。例えば、A-Dコンバータの前に10倍から100倍の増幅器を置けば、安価な16ビットA-Dコンバータでもよいかもしれません。

前へ

温度を測る その5 警報ビー

次へ

モータを動かす その1 重ねるだけで使えるシールド