温度センサLMT01を使う [Raspberry Pi]
去年暮れから1月中ごろにかけて2ヶ月ほどずっと計算して過ごしたあと、原理試作にGOが出て評価装置の設計をしていた。それが2月初めひと段落してそれに使う部品、ほとんどが汎用品だけど合計600点ほどをThorlabsシグマ光機Edmund中央精機をはじめ小物はAmazonまで9社に一気に発注した。汎用品なのに納期に2ヶ月かかるようなものもあるし、原理試作レンズが上がってくるのが4月中ごろなので、こんどは回路図を描いて半田付けを始めた。
ずっとスイッチが入ったままの状態で4ヶ月目に突入する。やはりときどき役に立つ夢を見た。メカ設計をしていたときは光軸高さを低くするアイデアを思いついたし、つい先日回路図を描いていたときはオペアンプの数を減らす工夫を夢で見た。専門分野でなくてもこういうことがあるというのは面白い。ずっと同じことを考え続けてるわりに寝不足感や疲労をあまり感じない。残業はせずにうちに帰れば酒飲んでさっさと寝る、というのがたまたまいい方向になってる、ということだという気もする。
ここ何日か出社したらずっと半田ごてを握っていて、今日は副社長に「そこまでしなくても」と言われた。ようするにそんな誰でもできるようなことをしていないで、何かもっと意義のあることをしろ、と言うのである。しかし「妊婦が10人いても赤ん坊が1ヶ月でできるわけではない」ということわざ(違うか)の通り、律速段階を早める手段は今時点ではないので、全く他のことをするしかない。
もちろんしなければいけないことはあるけど、それこそ僕じゃなくてもできることで、たまたま今の会社では僕しかいないだけ。僕としてはそんなことをするくらいなら、いまやってることを細部まで詰めておきたい。巨費を投じた大企業の研究開発でも半田付けが一箇所ヘボだったせいで何ヶ月も遅れる、ということは有りえる。他所に投げると見えなくなるし、もし問題が出たらそのときは責任追及はできても時間を取り返すことはできない。
そもそもが、そんな大プロジェクトではなく、単なる思い付きから始まったものなので、それに見合った進め方をさせてほしい、というのが僕の気持ち。まあ、そうは言わなかったけど。
いやいや、今日はそんなことをここに書きたかったのではない。評価系を設計する中で温度制御の必要性が出てきて、どうしたものか、と考えていたときに出会った素子について。これ、結構面白いよ....
評価系を設計していて、その一部に温度を検出して制御する必要が出てきた。専用の装置はあるけど大げさで高価なものになる。温度制御の対象は片手に握れば隠れてしまうくらいのアルミの塊なので、熱容量は大したことはない。そこでペルチェと温度センサとRaspberry Piで制御することにした。
ペルチェはPWMで制御するのが一番簡単だけど、その直近に温度センサが来る必要があって、センサにとってはペルチェがノイズ源になってしまう。 最初はサーミスタとA/Dと、みたいなことを考えていたんだけど、探しているうちに面白いものを見つけた。 TI社のLMT01というセンサで、A/Dを内蔵していながら小電流用のトランジスタみたいなパッケージになっている。しかもサーミスタの数倍程度の非常に安価なものである。さらに2値電流出力で、サーミスタの信号をシールドで引き回すことに比べたら配線はずっと楽なはずである。
これを試さない手はない、と思って手配した。
LMT01はパッケージが小さいのはいいとして、ピンが二本しか出ていない(真ん中の短いのは僕が切ったのではなくて、3ピンのトランジスタのパッケージを流用したせいだろう)。仕様では正の電圧をかけるほうをVP、負の電圧になるほうをVNと呼んでいる。それらを電源とグランドにするとデータはどこからくるのか?
ピンの両端に2〜5.5Vの電圧をかけると内部回路が起動してそのまま温度測定が始まる。そしてデータはその両端を流れる電流で表される。LOWがtypical 34$\mu$A、HIGHがtypical 125$\mu$Aになっていて、そのパルス数でデータが表されている。
従って電流の流れ出し側のピンであるVNに抵抗をつないでその反対をグランドに落とすと、抵抗両端の電圧が変化することでパルスを受け取ることができる。
電流レベルなので、引き回してもシールドする必要はない。パルス数が多いと温度が高いことを表していて、仕様上0℃で808$\pm$8パルスだそうである。
他の温度では、温度を$T$℃、パルス数を$P$とすると \begin{equation} T \approx \frac{256}{4096}P-50 \label{tempeq} \end{equation} だそうである(これだと800パルスで0℃になる。ずいぶん適当)。
温度の精度は仕様では-20℃〜90℃の範囲で$\pm$0.5℃となっているのでそれほど高くはない。しかも印加電圧によって誤差の分布が変化する(もちろん仕様の範囲で)らしいので、較正してもどのくらいの精度が出るかはわからない。分解能は式-1から1/16℃あるということなので、温度測定用というよりは、温度を一定に保つためのフィードバック用という感じである。しかもそれを何日も、ではなくせいぜい数分から数十分程度使うだけ、という感じだろう。
ピンに印加する電圧が0.4V以下だと電流は数nAになって、内部回路はスリープするようになっている。従って1mA程度流せるTTLの出力を、流し込み側のピンのVPにつないで、HIGHにすれば動き出してデータを出力し、LOWにすればスリープする、というふうな使い方ができる。と言っても1mWもないので、よほどの低消費電力のデバイスでないと効いてこない。
VPに電圧がかかると内部回路が動き出してすぐA/D変換が始まる。変換は最大で54m秒かかって、そのあと温度に従った数のパルスが送り出される。
パルスは最大で50m秒となっていて、従ってパルス一発は最大で12$\mu$秒、周波数で100kHz弱、ということになる。
仕様書ではパルスを出し終わると次の変換に入るらしい。従って1回の測定で最大104m秒かかることになる。つまり約0.1秒に一回データが来る、ということになる。
なぜか仕様書には最短の変換時間がないので、パルスとパルスの間隔がどのくらい開いたら別のデータだとみなせばいいか、というのは仕様書からはわからない。TIにしてはちょっとイマイチだけど、A/D変換は$\Sigma \Delta$方式らしいので、データによって変換時間が変わるということはないと考えていいだろう。
ホストにRaspberry Piを使う場合、Raspberry PiのGPIOでは他が電流を食っていなければピン一本あたり16mA流すことができるので、余り遠くなければLMT01のVPを直接つないでもじゅうぶん動作する。
電流検出の手段として、仕様書にはVPに3.3Vをつないで、VN側に6.8kの抵抗を入れてグランドに落として、抵抗両端の電位をコンパレートするように描かれている。
Raspberry PiのGPIOはコンパレータの機能はなくて、TTL互換ということになっているので、外部にコンパレータが必要である。
仕様書の推奨回路に従ってRaspberry PiのGPIOに繋ぐような回路を描いてみるとこのようになる。 コンパレータというかレベル変換というか。トランジスタのコレクタ側の抵抗は仕様書では100k$\Omega$が入っているけど、トランジスタによってはあまり電流が少ないと遅くなるので、小さめの抵抗にしてある。これはトランジスタによって変えないといけない。
抵抗のLMT01側をRaspberry PiのGPIOに直接つないで、抵抗値を変えながらデータが取れるかどうかみてみると33k$\Omega$前後で読むことができた。しかしそこから数k$\Omega$違うだけで読めたり読めなかったりするので、やっぱりトランジスタは入れたほうがいいようである。
pigpioのalertとwatchdogの機能を使えばいい。alertはピンの状態を監視して変化があればコールバックを呼ぶ、watchdogは変化が一定期間なければ、コールバックを呼ぶ、という機能である。
パルスの立ち上がり(トランジスタで反転しているので、立ち下がりが本来は正しい。けど実際は同じ)があるとそれをカウントして、次の変換の間のパルスのない期間を検出して、その時のカウント数をデータだとすればいい。
例えば
そしてコールバックでは
あとは本体で
実際に試してみると、デフォルトのままだと定常的にカウント数が小さくて値がフラフラする。室温だと1200カウント前後のはずが、800〜1000ぐらいにバラつく。
パルスをデューティ50%の周波数90kHzの方形波だとすると、デフォルトの5$\mu$秒でもいいはずだけど、安定しない。その原因として
ただしcfgMicrosには1、2、4、5、8、10$\mu$秒の6通り以外選べない。また当然サンプリング間隔を短くすると負荷は増える。
僕の環境では4$\mu$秒以上では足りなくて、2$\mu$秒で安定するようになった。
室温放置状態で、1200$\pm$1ビットふらつく程度で、データとしては安定している。
CPUの負荷はtopコマンドでみるとデフォルトでは10%ぐらいで、2$\mu$秒だと14〜15%ぐらいになった。これはgpioCfgClockのドキュメントにある数字とほとんど同じである。コールバックは軽くてmainもほとんど何もしていないので、実質的にpigpioライブラリ本体の負荷だけだということである。
コールバックに作業を詰め込まない限り(pigpioのポーリングスレッドから呼ばれるかもしれないので、コールバック内でprintfなんかは呼ばないほうがいい)実用的にも問題ない範囲だろう。
ほんとはこれでPID制御のコードを書いて試してみたいんだけど、ちょうどいいサイズと電圧のペルチェがすぐ手に入らなくて、実験できるのは来月中ごろになってしまう。なんのために慌ててやったんだか....
ずっとスイッチが入ったままの状態で4ヶ月目に突入する。やはりときどき役に立つ夢を見た。メカ設計をしていたときは光軸高さを低くするアイデアを思いついたし、つい先日回路図を描いていたときはオペアンプの数を減らす工夫を夢で見た。専門分野でなくてもこういうことがあるというのは面白い。ずっと同じことを考え続けてるわりに寝不足感や疲労をあまり感じない。残業はせずにうちに帰れば酒飲んでさっさと寝る、というのがたまたまいい方向になってる、ということだという気もする。
ここ何日か出社したらずっと半田ごてを握っていて、今日は副社長に「そこまでしなくても」と言われた。ようするにそんな誰でもできるようなことをしていないで、何かもっと意義のあることをしろ、と言うのである。しかし「妊婦が10人いても赤ん坊が1ヶ月でできるわけではない」ということわざ(違うか)の通り、律速段階を早める手段は今時点ではないので、全く他のことをするしかない。
もちろんしなければいけないことはあるけど、それこそ僕じゃなくてもできることで、たまたま今の会社では僕しかいないだけ。僕としてはそんなことをするくらいなら、いまやってることを細部まで詰めておきたい。巨費を投じた大企業の研究開発でも半田付けが一箇所ヘボだったせいで何ヶ月も遅れる、ということは有りえる。他所に投げると見えなくなるし、もし問題が出たらそのときは責任追及はできても時間を取り返すことはできない。
そもそもが、そんな大プロジェクトではなく、単なる思い付きから始まったものなので、それに見合った進め方をさせてほしい、というのが僕の気持ち。まあ、そうは言わなかったけど。
いやいや、今日はそんなことをここに書きたかったのではない。評価系を設計する中で温度制御の必要性が出てきて、どうしたものか、と考えていたときに出会った素子について。これ、結構面白いよ....
評価系を設計していて、その一部に温度を検出して制御する必要が出てきた。専用の装置はあるけど大げさで高価なものになる。温度制御の対象は片手に握れば隠れてしまうくらいのアルミの塊なので、熱容量は大したことはない。そこでペルチェと温度センサとRaspberry Piで制御することにした。
ペルチェはPWMで制御するのが一番簡単だけど、その直近に温度センサが来る必要があって、センサにとってはペルチェがノイズ源になってしまう。 最初はサーミスタとA/Dと、みたいなことを考えていたんだけど、探しているうちに面白いものを見つけた。 TI社のLMT01というセンサで、A/Dを内蔵していながら小電流用のトランジスタみたいなパッケージになっている。しかもサーミスタの数倍程度の非常に安価なものである。さらに2値電流出力で、サーミスタの信号をシールドで引き回すことに比べたら配線はずっと楽なはずである。
これを試さない手はない、と思って手配した。
2 LMT01
ということで手に入った。こんなの。 すごく小さくてカメラで撮るのに苦労した。LMT01はパッケージが小さいのはいいとして、ピンが二本しか出ていない(真ん中の短いのは僕が切ったのではなくて、3ピンのトランジスタのパッケージを流用したせいだろう)。仕様では正の電圧をかけるほうをVP、負の電圧になるほうをVNと呼んでいる。それらを電源とグランドにするとデータはどこからくるのか?
ピンの両端に2〜5.5Vの電圧をかけると内部回路が起動してそのまま温度測定が始まる。そしてデータはその両端を流れる電流で表される。LOWがtypical 34$\mu$A、HIGHがtypical 125$\mu$Aになっていて、そのパルス数でデータが表されている。
従って電流の流れ出し側のピンであるVNに抵抗をつないでその反対をグランドに落とすと、抵抗両端の電圧が変化することでパルスを受け取ることができる。
電流レベルなので、引き回してもシールドする必要はない。パルス数が多いと温度が高いことを表していて、仕様上0℃で808$\pm$8パルスだそうである。
他の温度では、温度を$T$℃、パルス数を$P$とすると \begin{equation} T \approx \frac{256}{4096}P-50 \label{tempeq} \end{equation} だそうである(これだと800パルスで0℃になる。ずいぶん適当)。
温度の精度は仕様では-20℃〜90℃の範囲で$\pm$0.5℃となっているのでそれほど高くはない。しかも印加電圧によって誤差の分布が変化する(もちろん仕様の範囲で)らしいので、較正してもどのくらいの精度が出るかはわからない。分解能は式-1から1/16℃あるということなので、温度測定用というよりは、温度を一定に保つためのフィードバック用という感じである。しかもそれを何日も、ではなくせいぜい数分から数十分程度使うだけ、という感じだろう。
ピンに印加する電圧が0.4V以下だと電流は数nAになって、内部回路はスリープするようになっている。従って1mA程度流せるTTLの出力を、流し込み側のピンのVPにつないで、HIGHにすれば動き出してデータを出力し、LOWにすればスリープする、というふうな使い方ができる。と言っても1mWもないので、よほどの低消費電力のデバイスでないと効いてこない。
2.1 パルス列の仕様
パルスはほぼ一定の周波数で送られてくる。VPに電圧がかかると内部回路が動き出してすぐA/D変換が始まる。変換は最大で54m秒かかって、そのあと温度に従った数のパルスが送り出される。
パルスは最大で50m秒となっていて、従ってパルス一発は最大で12$\mu$秒、周波数で100kHz弱、ということになる。
仕様書ではパルスを出し終わると次の変換に入るらしい。従って1回の測定で最大104m秒かかることになる。つまり約0.1秒に一回データが来る、ということになる。
なぜか仕様書には最短の変換時間がないので、パルスとパルスの間隔がどのくらい開いたら別のデータだとみなせばいいか、というのは仕様書からはわからない。TIにしてはちょっとイマイチだけど、A/D変換は$\Sigma \Delta$方式らしいので、データによって変換時間が変わるということはないと考えていいだろう。
2.2 電気仕様
普通に使う場合、センサはその測定対象のすぐ近くで、検出はホストの近く、あるいは恒温槽の内と外や別の部屋、なんてことがある。LMT01は電流出力なので、シールドではなくツィステドペアで引っ張ればいい。仕様書では2mまでとなってるけど、実際にはもっと伸ばせるだろう。ホストにRaspberry Piを使う場合、Raspberry PiのGPIOでは他が電流を食っていなければピン一本あたり16mA流すことができるので、余り遠くなければLMT01のVPを直接つないでもじゅうぶん動作する。
電流検出の手段として、仕様書にはVPに3.3Vをつないで、VN側に6.8kの抵抗を入れてグランドに落として、抵抗両端の電位をコンパレートするように描かれている。
Raspberry PiのGPIOはコンパレータの機能はなくて、TTL互換ということになっているので、外部にコンパレータが必要である。
仕様書の推奨回路に従ってRaspberry PiのGPIOに繋ぐような回路を描いてみるとこのようになる。 コンパレータというかレベル変換というか。トランジスタのコレクタ側の抵抗は仕様書では100k$\Omega$が入っているけど、トランジスタによってはあまり電流が少ないと遅くなるので、小さめの抵抗にしてある。これはトランジスタによって変えないといけない。
2.2.1 コンパレータは必要か?
Raspberry PiのGPIOはTTL互換ということになってるけど、内部回路はC-MOSなので、LMT01の電流変化を検出する抵抗の値をうまく選んでやれば、コンパレータ用のトランジスタはいらなくなるはずである。抵抗のLMT01側をRaspberry PiのGPIOに直接つないで、抵抗値を変えながらデータが取れるかどうかみてみると33k$\Omega$前後で読むことができた。しかしそこから数k$\Omega$違うだけで読めたり読めなかったりするので、やっぱりトランジスタは入れたほうがいいようである。
3 ソフトウェア
Raspberry Piでどうやってこれを読むか、だけど、pigpioライブラリを使えばすごく簡単にできる。pigpioのalertとwatchdogの機能を使えばいい。alertはピンの状態を監視して変化があればコールバックを呼ぶ、watchdogは変化が一定期間なければ、コールバックを呼ぶ、という機能である。
パルスの立ち上がり(トランジスタで反転しているので、立ち下がりが本来は正しい。けど実際は同じ)があるとそれをカウントして、次の変換の間のパルスのない期間を検出して、その時のカウント数をデータだとすればいい。
例えば
static void callbackFunc(int gpio, int level, uint32_t tick);というようなコールバックを定義して、mainで
gpioSetAlertFunc(vnPin, callbackFunc); gpioSetWatchdog(vnPin, 10);としてやる。gpioSetAlertFuncはvnPin(pigpioではBCMピン番号)に変化があるとcallbakFuncが呼ばれるようにする。gpioSetWatchdogはさらにwatchdogタイマを設定する。この例では10m秒何も起こらないとcallbakFuncが呼ばれる。
そしてコールバックでは
static int count = 0; static int lastCount; static unsigned lastTick; static int nummes = 0; static void callbackFunc(int gpio, int level, uint32_t tick) { if (level == 0) count ++; else if ((level == 2) && (count > 0)) { nummes ++; lastCount = count; lastTick = tick; count = 0; } }levelが0、すなわち立ち下がりだと、countを一つ増やして、levelが2、つまりwatchdogタイムアウトだと、それまでのカウントを保存して、計数をクリアする(立ち上がりでの呼び出しは捨てる)。
あとは本体で
int lastnum = 0; unsigned startTick = gpioTick(); while (lastnum < 100) { if (lastnum != nummes) { double sec = (lastTick - startTick) * 0.000001; lastnum = nummes; printf("%4d(%6.3f):%4d\r", nummes, sec, lastCount); fflush(stdout); } usleep(50 * 1000); }というふうに、スリープしながら変化があれば値を調べる、ということをすればいい。これですべて。これを実行するとほぼ10秒でループを抜けて終了するので、細かく見なくても仕様書通りになっていることがわかった。
3.1 pigpioサンプリング間隔との兼ね合い
ところが残念ながらpigpioのデフォルトのサンプリング間隔は5$\mu$秒で、パルス一発は最大で12$\mu$秒だったので、パルス幅次第では見逃す可能性がある。実際に試してみると、デフォルトのままだと定常的にカウント数が小さくて値がフラフラする。室温だと1200カウント前後のはずが、800〜1000ぐらいにバラつく。
パルスをデューティ50%の周波数90kHzの方形波だとすると、デフォルトの5$\mu$秒でもいいはずだけど、安定しない。その原因として
- パルスのデューティが50%付近ではない
- コンパレータのトランジスタが十分速くなくて、パルスが鈍っている
3.2 サンプリング間隔の調整
pigpioではそのサンプリング間隔はgpioCfgClockという関数を使えば、ある程度調整できる。それはgpioCfgClock(unsigned cfgMicros, unsigned cfgPeripheral, unsigned cfgSource);をライブラリ初期化前(gpioInitialiseを呼ぶ前)に呼ぶ。cfgMicrosは$\mu$秒単位のサンプリング間隔、cfgPeriphralはタイミングソースをBCM2835のハードウェアPWMか、I2C用のPCMかどちらか(どちらでも性能には影響はない)を設定する(cfgSourceはdeprecatedで無視される)。
ただしcfgMicrosには1、2、4、5、8、10$\mu$秒の6通り以外選べない。また当然サンプリング間隔を短くすると負荷は増える。
僕の環境では4$\mu$秒以上では足りなくて、2$\mu$秒で安定するようになった。
室温放置状態で、1200$\pm$1ビットふらつく程度で、データとしては安定している。
CPUの負荷はtopコマンドでみるとデフォルトでは10%ぐらいで、2$\mu$秒だと14〜15%ぐらいになった。これはgpioCfgClockのドキュメントにある数字とほとんど同じである。コールバックは軽くてmainもほとんど何もしていないので、実質的にpigpioライブラリ本体の負荷だけだということである。
コールバックに作業を詰め込まない限り(pigpioのポーリングスレッドから呼ばれるかもしれないので、コールバック内でprintfなんかは呼ばないほうがいい)実用的にも問題ない範囲だろう。
ほんとはこれでPID制御のコードを書いて試してみたいんだけど、ちょうどいいサイズと電圧のペルチェがすぐ手に入らなくて、実験できるのは来月中ごろになってしまう。なんのために慌ててやったんだか....
2018-02-20 23:00
nice!(0)
コメント(0)
コメント 0