本HP(Arduino D.I.Y.)は、「部屋の気温と湿度のログを取りたい」から始まりました。先日、「#19-3 バンドギャップ基準電圧」の記事を書いた後、「#19-4 BGR回路の温度補償」で温度補償を実施した時、これを逆向きに応用すると、温度を推定することができるのではないか?と、考えました。
今回はSiバンドギャップに起因する一定の電圧を出力する、バンドギャップリファレンス回路(以下BGR回路)を応用した温度センサの設計について考察したいと思います。
温度センサの検討に入る前に、まず、BGR回路の復習は以下ページで済ませてください。
「#19-3 バンドギャップ基準電圧」
「#19-4 BGR回路の温度補償」
上の記事では、トランジスタのベース-エミッタ間電圧Vbeが、およそ0.6〜0.7[V]で一定となることを利用したBGR回路(Widlar回路)を使いました(下図)。
トランジスタ×3個をはじめとして部品数が多く、少し複雑なため、今回はもう少し簡単な回路を使いたいと思います。
いろいろと調べた中で一番簡単だったのは、以下図に示す、ダイオード×2個のBGR回路でした。
ずいぶんとスッキリして、簡易な回路です。
2個のダイオード(D1,D2)は、NPNトランジスタのダイオード接続(ベースとコレクタをショート)でも良いです。Widlar回路では、トランジスタのVbeを使いました。ベース⇔エミッタ間は、半導体のP型⇔N型接続となっているので、PN接合ダイオードに似た特性を持っています。
※バンドギャップ電圧を使うので、ダイオードはシリコンPN接合タイプを使ってください。ショットキダイオードとかではダメです。
トランジスタのベース-エミッタ間電圧Vbeを、ダイオードの順方向電圧Vfに置き換えたのが上図の回路と言う事みたいです。
Vref値を計算しておきましょう。
ダイオードを使ったBGR回路をLTspiceでシミュレーションしてみると、以下図のようになりました。
横軸に温度[℃]、縦軸に電圧値[V]をとり、Vref(緑色)、Vf1(青色)、ΔVf(DVf、赤色)をプロットしました。ダイオード1N4007を使ったシミュレーションでは、Vref≒1.15Vでした。
Vfの温度依存性傾きSa≒−1.92mV/℃、ΔVfの温度依存性傾きSb≒0.358mV/℃と読み取ることができます。
ダイオードの理想係数はPN接合界面の結晶性等によってn>1となる係数で、ダイオードによって異なります。上で求めたn≒1.80は、シミュレーションに使ったダイオード1N4007のシミュレーションモデルに設定された理想係数だと思います。理想的にはn=1なのですが、実在のダイオードの場合、n=1〜2程度になる様です。
ダイオードに固有な理想係数nが入ってしまう分、Vref値に対する不確定性が増加してしまい、トランジスタによるBGR回路に比べると使い難いかもしれません。
※なお、このBGR回路では、Vrefがオペアンプ(LM358)のVOHを超えられないため、Vref<VOH=Vcc−1.5VとなるVccまで電源電圧を上げないと、正しいVrefが出力されません。Vcc>Vref+1.5V≒2.65Vですね(低電圧駆動には不向き)。Widlar回路だと、電源電圧Vcc>1.5Vくらい(ギリギリ乾電池電圧)でVrefが一定値を出力できました。
BGR回路は、電源電圧だけでなく、温度にも依存せずに一定の電圧を出力する回路として知られています。
その様な回路とするため、「#19-4 BGR回路の温度補償」ではSiバンドギャップに起因するベース-エミッタ間電圧Vbeが持つ温度依存性を打ち消すような回路としました。
温度センサとして使う時は、逆にVbeが温度によって変動するので、その変化を読み取って、温度に換算すれば良いのではないか?と考えました。
今回使うBGR回路では、Vfの温度依存性を使います。Vfの温度依存性傾き📐Sa≒−2mV/℃です。Vfの温度に対する変化量Saは微小なので、増幅してからAD変換します。
さもないと、AD変換器の変換誤差に埋もれてしまいます。
例えば、Arduino UNOの10ビットAD変換器では、絶対精度が±2LSB(TYP)です。Vcc=AVcc=5Vで使う場合、電圧値換算では約±9.8mVの精度となります。これでは、Sa≒−2mV/℃に対して、約±4.9℃の精度なので、室温25℃が20〜30℃に読めてしまいます。
以下に回路図を示して解説を続けます。
右上に配置したのがダイオードを使ったBGR回路そのものです。BGR回路の中から、ダイオードD1の順方向電圧Vf1を引き出し、下段の非反転増幅回路で×5倍に増幅(GND基準、Vf15)、更に反転増幅回路を使って×2倍に増幅(Vb=2.5V基準、Vtmp)しています。
以下、LTspiceによるシミュレーション結果を示します。
横軸に温度[℃]、縦軸に電圧[V]を取って、ダイオードの順方向電圧Vf1、非反転増幅後Vf15=5×Vf1、反転増幅後Vtmp=2×Vf15をプロットしました。
まず、Vf1をGND基準で×5倍に増幅してVf15としました。これ以上増幅率を上げようとすると、オペアンプLM358のVOH規定(<Vcc−1.5V)に引っかかって、波形が歪んでしまいます。
そこで今度はVb=2.5Vを中心に折り返しながら(反転増幅)、×2倍に増幅してVtmpとしました。
これで、VtmpはVf1を×10倍に増幅したことになります。温度依存性傾きSa≒−2mV/℃も、×10倍に増幅されて、約20mV/℃程になっています。あ、一回、反転増幅しているので、傾きが正になった事にも注目です。
本来は校正が必要なのかもしれませんが、一旦、シミュレーションを正として、電圧Vtmp→温度Tjの変換式を作って見ましょう。
前節のシミュレーション結果より、以下式を導出しました。
Tj=(Vtmp[V]−0.9064)/1.935×100−25[℃]
dVtmp/dTj=19.35[mV/℃]
シミュレーション結果に基づいた式なので、今回使ったダイオード1N4007の換算式です。回路素子や定数を変えて使う場合は、各自シミュレーションして変換式を見出す必要があります。
このTjとVtmpの変換式で、シミュレーション条件の温度tempとの差を取ったところ、最大で約0.6℃の誤差がありました(下図)。
横軸に温度[℃]、縦軸にTjと期待温度tempとの差分[℃]を取った図です。
Tj−temp(水色プロット)を見ると、最大で約−0.6℃程の誤差がありました。どうやら、最初のVf1自体が完全なリニアではなくて、少し歪んでいるようです。変換式は、両端(−25℃と75℃)で値を合わせたため、誤差が最大となるのは、中央付近Tj=25℃のポイントであり、一番使用頻度の高い温度となります。
下に凸のおわん型なので、sin(0〜π)を使って誤差を補正してみましょう。
Tj計算後の非直線性誤差補正式:
Tj2[℃]=Tj+0.6117×sin (π×(Tj+25)/100)
補正後のTj2と温度tempとの差分をプロットしたのが上図紫色プロットです。25℃付近での誤差はほぼ0となり、最大誤差も0.05℃未満にすることができました。
さて、説明なしでちょっと微妙な書き方をしてきたのですが、前節で求めた温度Tjはジャンクション温度と言います。ダイオードPN接合面の温度です。もし、気温Taを知りたいのであれば、厳密にはTa≠Tjです。
TaとTjの関係式は以下式で知られています。
消費電力Pdは、ダイオードに掛かる電圧Vfと流れる電流Ifより、Pd=Vf×Ifで求められます。前節のシミュレーションで求めたところ、最大でもPd≒0.617mW(MAX)でした。
ダイオード1N4007の熱抵抗θjaはデータシートに記載があって、ざっくりθja≒50℃/Wです。
以上より、TjからTaを求めると、以下となります。
Ta=Tj−Pd×θja≒Tj−0.031[℃]
今回の回路では、ダイオードの消費電力Pdが微小なため、気温Taとの差分はわずか0.031[℃]です。求めたTjから引き算して補正しても良し、無視しても大した誤差ではありません。
以下、蛇足です。
実験します。気温のリファレンスとして、気温・湿度センサDHT11を使います(下図)。
温度センサ回路をブレッドボードなどに構築して、Arduino UNOと以下の様に接続します。
DHT11 | 接続 | Arduino UNO | 接続 | 温度センサ回路 |
VCC | ← | 5V | → | Vcc |
GND | ⇔ | GND | ⇔ | GND |
SDA | ⇔ | D2 | ||
A0 | ← | Vtmp | ||
A1 | ← | Vref |
以下図に実験中の様子を示します。
DHT11から温度を読み取り、アナログ入力からAD変換した値を使って温度センサ回路の温度TjとBGR回路出力Vrefを表示するだけのプログラムを使います(以下にリンクしておきます)。
実験プログラム:temp_sens.ino
以下の様に実験します。
実験サンプル:【 】内はシリアルモニタからの送信データ
【tmp,1】
OK: tmp.interval=1000[ms]
TMP: dht11=268 Tj=260 [degC/10] Vref=1171 [mV]
TMP: dht11=318 Tj=227 [degC/10] Vref=1157 [mV] ……↓ドライヤで加熱
TMP: dht11=327 Tj=482 [degC/10] Vref=1176 [mV]
TMP: dht11=338 Tj=386 [degC/10] Vref=1142 [mV]
TMP: dht11=341 Tj=525 [degC/10] Vref=1123 [mV]
TMP: dht11=360 Tj=538 [degC/10] Vref=1176 [mV]
TMP: dht11=377 Tj=1106 [degC/10] Vref=1171 [mV]
TMP: dht11=391 Tj=1053 [degC/10] Vref=43 [mV] ……↓Vrefが外れた
TMP: dht11=403 Tj=1027 [degC/10] Vref=34 [mV]
TMP: dht11=417 Tj=1037 [degC/10] Vref=39 [mV]
結論から申しますと……、全然ダメ、ですm(1x_x;)m。
一応、Vref≒1.17Vと、シミュレーション通りの値を観測することができました。BGR回路としては機能しているようです。
ですが、指示温度Tj≒23〜111[℃]と、DHT11の指示値27〜38℃が全く合わず、でした。まあ、そこは、校正が必要なのかもしれないので、無視して先に進みます。
※実験サンプルのログでは、×10倍した温度が表示されているので、Tj=482は48.2℃と読んでください。……UNOで整数処理する都合です。
ドライヤを使い、測定系の温度を上げて行ったところ、DHT11の指示値はどんどん上がったのに、Tjは連動した変化がほとんど見られませんでした。
そもそも、ドライヤであおると、BGR回路出力Vref≒0Vに落ちてしまうことがありました。確かに、今回の温度センサ回路はVref≒0Vにも安定点があるので、何らかの拍子にVref≒0Vの安定点に落ち着いてしまうと、期待の動作となりません。
その点、Vref≒0V近辺の安定点を回避する回路を盛り込んであった、トランジスタによるBGR回路の方が良かったのかもしれません。……Vref以前に、温度変化に対してVtmpの変化が連動しなかったので、回路を作り直してまで実験はしませんでした。
※なお、手元のDHT11は、出力値が60℃で頭打ちとなり、それ以上の温度を計測できませんでした。
ブレッドボードに回路を構築するのに使った金属皮膜抵抗は、温度が上昇すると抵抗値が増大してしまいます。ブレッドボード上の回路なので、ドライヤであおると、ダイオードだけでなく、抵抗など周りの回路も一緒に加熱されて、値が変動してしまいます。
※しかも、ブレッドボード上の実装位置がある程度範囲を持っているので、熱した時の温度変化は均一ではありません。素子によって、少しづつ異なる温度変化があるので、シミュレーションとは異なる挙動を示します。
信号成分(温度)を×10倍に増幅したということは、ノイズ成分も×10倍だし、回路定数の変動(公差含む)による出力電圧の変動も×10倍になっているので、単純に電圧増幅で決着しようとした考え方自体も正しくなかったのかもしれません。
DHT11等市販の温度センサは、どうやって温度を計測しているのでしょうね?
Siバンドギャップを使っているのなら、こちらの考え方で概ね合っていると思われますが、微小な電圧-温度変化傾きを、うまく温度に換算(変換)できるテクニックがあるのだと思います。
失敗した実験を踏まえて、改定案を考えてみました(以下)。
アイディアとしては:
シミュレーションで決着しておけばカッコ良かったのですが、本HPは「Arduino D.I.Y」と、電子工作のHPですので、工作して(実物の動きを確かめて)なんぼです。うまく行かなかった実験の様子もご覧いただきました……。こんな日もあります。