2010年1月25日月曜日

Arduinoで温度を計測する -2 オペアンプ使用

この投稿は、建築発明工作ゼミ2009 『温度センサ』をもとに、新しく書き直したものです。

デュアルオペアンプLMC6032IN

オペアンプを使って、温度センサの最大出力電圧1Vを5V、五倍に引き上げてみます。
簡単に言うと、1℃につき10mV出力であったのを、回路をいじって1℃につき50mVの出力にしてやるということです。アナログ入力値1に対する分解能は、

100℃ / 1024 = 0.0976...

つまり、0.09℃単位で計測できるようになります。

図1 図2

G(増幅率) = 1 + (R1 / R2)

図1のように、オペアンプは基本的に5つのピンを持っています。
  1. プラス電圧入力「差動入力ピン」
  2. マイナス電圧入力「差動入力ピン」
  3. プラス電源ピン(オペアンプ自身のプラス電源ピン)
  4. マイナス電源ピン(オペアンプ自身のマイナス電源ピン)
  5. 出力ピン
プラスとマイナスの差動入力ピンの電圧差が増幅されて出力される、という電子部品です。
入力された電圧の増幅率(ゲイン)は、二つの抵抗値の比によって決まります。
オペアンプは、自身に入力される電源よりも高い電圧を出力することはできません。
例えば、オペアンプのプラス電源に5V、マイナス電源をGNDに接続するとします。入力が1V、増幅率を10倍に設定しても、理論的には出力は5Vまでしか増幅されません。
しかし、オペアンプの増幅率は105倍というような大きさのため、差動入力電圧がわずかでもあると出力は+か−の最大値しか出力できません。
実用的に使用するには、ネガティブフィードバックという方法を使います。

オペアンプの回路には、大別して「反転増幅回路」と「非反転増幅回路」の二種類があります。
非反転増幅回路は入力された電圧をそのまま増幅(プラスだったらプラス、マイナスだったらマイナス)して出力します。
反転増幅回路は入力された電圧を反転増幅(プラスだったらマイナス、マイナスだったらプラス)して出力します。
反転増幅回路を組む時はマイナス電源が必要になる(マイナス電源ピンに回路のGNDを基準としてマイナスの電圧をつなぐ)ため、簡単には回路を組めません。
非反転増幅回路をプラスの単源源で使用すれば、マイナス電源は回路のGNDに接続すればよいので扱いやすくなります。

図2は非反転増幅回路です。
プラスとマイナスの差動入力ピンの電圧差を極性をそのままに増幅して出力しますが、出力の途中で抵抗R1を通し、出力信号をマイナス差動入力ピンにフィードバックしています。
これにより、差動電圧差がなくなるように働き、結果として二つの抵抗R1とR2で設定した増幅率に落ち着きます。

outputV(出力電圧) = (1 + (R1 / R2)) × inputV(差動入力電圧)


温度センサLM35DZとオペアンプLMC6032INを使用し、回路を組んでみます。

図3
オペアンプはLMC6032を使用しました。
図3の通り、このLMC6032INには二つのオペアンプが内蔵されています。V+ピンは共通のプラス電源ピン、V-は共通のマイナス電源ピンです。今回は一つしか使いません。


回路図

考え方としては、まず計測温度範囲を決めます。0℃〜100℃とします。
温度センサLM35DZは1℃あたり10mV、100℃でちょうど1Vです。Arduinoのアナログ入力最大電圧5Vまでで、フルレンジで使いますから増幅率は5倍です。とすると、上記の式に当てはめて、

5V(出力電圧) = (1 + (R1 / R2)) × 1V(差動入力電圧)

これを解くと、抵抗の比は

R1 : R2 = 4 : 1

となります。
LM35DZの+VsピンとLMC6032のV+ピンに5Vの電圧をかけます。C というのはコンデンサです。電気を溜めて吐き出す役割があり、上下する不安定な電気の流れを安定化させるバイパスコンデンサとして機能します。
抵抗R0は、R1とパランスをとるために使っています。この回路では、オペアンプのマイナス入力端子をGNDにつないでいるので、入力も出力もGNDとの電圧差が基準となります。

スケッチ
/* 
  温度計測(0℃〜100℃)
*/

int   A_inPin = 0;  // アナログ入力ピン番号
float A_val;        // アナログ入力値(0〜1023)
float tempC   = 0;  // 摂氏値( ℃ )

void setup()
{
  // シリアル通信速度
  Serial.begin(9600);
}

void loop()
{
  // アナログピンから計測値を取得(0〜1023)
  A_val = analogRead( A_inPin );

  // 摂氏に換算
  tempC = (100 * A_val) / 1024;

  // 改行しながら出力
  Serial.println( tempC );

  // 1秒停止
  delay(1000);
}



シリアルモニタで表示してみると、以下のようになりました。

だいたい0.1℃単位で変化しているので、おおむね精密になったと思います。

ついでに、以下のようなスケッチも試してみます。
1秒ごとに摂氏温度を計測し、10秒ごとに平均摂氏、最大摂氏温度、最小摂氏温度をシリアルモニタに出力します。
日付時刻も表示するために、Datetimeライブラリを使ってみます。
DateTimeライブラリは標準でArduinoIDEにインストールされていないので、以下のスケッチをコピーして実行しようとするとエラーになります。
以下のリンクからDateTimeライブラリをダウンロードして解凍し、指定のディレクトリ内に配置しなければなりません。

こちらからダウンロード

ダウンロードして解凍したら、DateTimeというフォルダができます。
その中のDateTimeフォルダとDateTimeStringsフォルダを、以下の場所にコピーします。

(Macの場合)
○○○(ユーザディレクトリ)/書類/Arduino/library/

スケッチ
/* 
  1秒ごとに摂氏温度を計測
  10秒ごとに平均摂氏、最大摂氏温度、最小摂氏温度を
  シリアルモニタに出力
*/

// DateTimeライブラリをインポート
#include <DateTime.h>

// プログラム開始日付時刻
int    year       = 2010;  // 年
int    month      = 1;     // 月
int    day        = 25;    // 日
int    hour       = 18;    // 時
int    minute     = 3;     // 分
int    second     = 0;     // 秒

int    A_inPin    = 0;     // アナログ入力ピン番号
int    A_val;              // アナログ入力値(0〜1023)
int    outputTerm = 10;    // 計測結果出力ターム(10秒ごとに平均摂氏値を出力する)
float  v          = 5;     // 基準電圧値( V )
float  tempC      = 0;     // 摂氏値( ℃ )
float  tempCPlus  = 0;     // 10秒ごとの合計摂氏値( ℃ )
float  maxTempC   = 0;     // 10秒ごとの最大摂氏( ℃ )
float  minTempC   = 0;     // 10秒ごとの最小摂氏( ℃ )

void setup()
{
  // シリアル通信速度
  Serial.begin(9600);

  // DateTime ライブラリの makeTime() メソッドで日付時刻作成
  time_t prevtime = DateTime.makeTime(second, minute, hour, day, month, year);

  // DateTime ライブラリの sync() メソッドで初期設定(日付時刻設定)
  DateTime.sync(prevtime);
}

void loop()
{
  // 1秒に一回、10秒間分温度計測
  for (int i = 0; i < outputTerm; i++ ) {

    // アナログピンから計測値を取得(0〜1023)
    A_val = analogRead( A_inPin );

    // 摂氏に換算
    tempC = (100 * A_val) / 1024;

    // 現for文ループ内で使わなくなった変数は解放
    A_val = 0;

    // 平均値を取得するために1秒ごとの値を10秒分合計しておく
    tempCPlus += tempC;

    // 1秒過去の最大摂氏値と現在の摂氏値を比較
    // 大きい方を格納
    maxTempC = max( maxTempC, tempC );

    // 1秒過去の最小摂氏値と現在の摂氏値を比較
    // for文1ループ目のみ、現在の摂氏値を格納
    if (i == 0) minTempC = tempC;

    // 小さい方を格納
    minTempC = min( minTempC, tempC );

    // 現for文ループ内で使わなくなった変数は解放
    tempC = 0;

    // 1秒間ストップ
    delay(1000);
  }

  // 10秒間の平均摂氏値
  tempC = tempCPlus / outputTerm;

  // 日付時刻更新
  DateTime.available();

  // シリアルモニタに出力
  // 日付時刻
  Serial.print(DateTime.Year+1900,DEC);
  Serial.print("/");
  Serial.print(DateTime.Month,DEC);
  Serial.print("/");
  Serial.print(DateTime.Day,DEC);
  Serial.print(" ");
  Serial.print(DateTime.Hour,DEC);
  Serial.print(":");
  Serial.print(DateTime.Minute,DEC);
  Serial.print(":");
  Serial.print(DateTime.Second,DEC);

  // 10秒間の平均摂氏値
  Serial.print("  Temp / ");
  Serial.print( outputTerm );
  Serial.print("sec  |  Average : ");
  Serial.print( tempC );
  Serial.print(",  ");

  // 10秒ごとの最大摂氏値
  Serial.print("Max : ");
  Serial.print( maxTempC );
  Serial.print(",  ");

  // 10秒ごとの最小摂氏値
  Serial.print("Min : ");
  Serial.println( minTempC );

  // 次loop()ループの計測に影響を及ぼさないように変数の値を0に戻す
  tempCPlus = 0;
  maxTempC  = 0;
  minTempC  = 0;
}


DateTimeライブラリは、
// プログラム開始日付時刻
int year = 2010; // 年
int month = 1; // 月
int day = 25; // 日
int hour = 18; // 時
int minute = 3; // 分
int second = 0; // 秒

の部分で初期値をセットしなければなりません。スケッチを実行するごとに新しく日付を記述し直し、アップロードさせます。
また、Arduinoの水晶振動子で物理的にカウントしているため、一日に数秒のずれが出るそうです。


『作る・できる/基礎入門 電子工作の素』

電子工作に必要な電子回路と部品を網羅した本。これ一冊でだいたいの部品の使用方法がわかります。様々な電子部品の役割が一通り載っているので、困ったらとりあえず開く、といった使い方ができます。さらに、実用的に使えそうな実験用電源装置や太陽光発電パネルの作り方まで載っています。電子工作の初心者はとりあえず買っておいて損はないと思います。
『センサ活用の素』

上記の本の著者のセンサ本。記述がかぶるところがありますが、オペアンプについてはけっこうわかりやすく書いてあるのでおすすめ。PICで作る事を前提としていますが、Arduinoを使った回路に応用が効く記述が多いです。光・温度・湿度・超音波・ガス・圧力センサの実装回路が載っています。
『すぐに使える!オペアンプ回路図100』

オペアンプを使いこなしたい人は持っておいた方がいいと思われます。初心者向きではありませんが、覚えきれないほどのオペアンプ回路が載っています。センサ×Arduinoの回路を実装していくと、困った時に必ず役に立つ時が来ます。

2 コメント:

Nou junpei さんのコメント...

質問です。
回路図と写真では、少し違うように見えます。青のピンやLM35DZのGNDから伸びている謎のピンは何なのでしょうか?
教えてください。

Daisuke En さんのコメント...

これは、積層セラミックコンデンサですね。回路図には入れていません。
バイパス用途に組み込んでいるようですが、今回の回路にはあまり意味を持たないため、組み込みは必須ではありません。

コメントを投稿