読者です 読者をやめる 読者になる 読者になる

ぷろろろぐ

制御系学生の雑記

放射温度計MLX90614をmbedで使った話 と I2Cの解説のようなメモ

こんにちは.春休みに入った瞬間に生活リズムの位相がπ/2ほど遅れました.直していきたいです.

最近,友人にとあるロボットの大会に誘われたので,機体の設計を友人に任せ,電装系(かっこいい響きですね)を色々と作ったりしています.

というわけで,このロボットに使用するつもりの放射温度計MLX90614をmbedで使った話です.ちなみに使用するマイコンはみんな大好きLPC1114です.

もともとは,秋月で売っている焦電センサ D203Bを用いて,自分で放射温度計を作成するつもりだったのですが,色々と厳しい感じになったので,少々高いですが市販されている放射温度計モジュールを使用することにしました.

赤外線温度センサ MLX90614 - MLX90614BAA - ネット販売

こちらのセンサですが,I2C(厳密にはSMBus)インターフェイスで通信を行える大変便利な物となっております.ただ,日本語の情報があまり存在しないんですよね.センサを動かすにあたって,こちらの記事を参考にさせていただきました.

温度センサMLX90614を使ってみる : Hoge-Puge-Foo
情報が少ない放射温度計センサMLX90614を使ってみた
Sensor de temperatura infrarrojo Melexis | Espacio de Victor Pasilla

まず,データシートも英語だしよく分からんからとりあえずi2c.read()で読むゾ とやっても読めなかったりします.そりゃそうだ.

というわけでデータシートを見ると,通信のタイミングは次のようになっております.

f:id:protocol1964:20170220221818p:plain

まず,スタートコンディションを発行し,続いて7bitのスレーブアドレス(このセンサのアドレスは0x5aです),書き込みを示すWr(0)を送信します.ちゃんと接続されていればスレーブからアクノリッジ(ACK)が送られてくるので,続いて8bitのコマンドを送信します.再びACKがスレーブから帰ってくるので,リピートスタートコンディション(Sr)を発行します.再び7bitのスレーブアドレスと,今度は読み取りを示すRd(1)を送信し,ACKが返ってくると,スレーブから3バイトのデータが送られてきます.3バイト目を受信し,ACKを返してからストップコンディションを発行して,一連の通信は終了します.

…と,図に書いてあることをつらつらと文章に起こしましたが,ここで用語の解説を挟みます.自分も最近勉強したばかりなので間違えていたら教えてください…


まずスタートコンディションとは,マスタが「今から俺がマスタになって通信するゾ」といった感じで,バスに接続されているスレーブ全体に対して送信するメッセージです.実際の信号では,SCLが上がっている時にSDAを下げることを示します.

オシロスコープで見るとこんな感じ.上がSCL,下がSDAです.
f:id:protocol1964:20170220223754j:plain

最初,SCLが上がっている時にSDAが下がり,SCLが動き出してデータのやり取りをしているのが分かりますね.


次にアクノリッジ
簡単に言うと,受信側がしっかりとデータを受信できているか,その確認のために発行されます.
上の図で言うと,一瞬だけピョコッと出ている信号に相当します.送信側は受信側のACKを受信してから次の1バイトを送信するので,ACKが帰ってこないと通信が進みません.


続いてリピートスタートコンディション.
I2Cの通信は,前述のスタートコンディションから始まり,スレーブアドレスとR/W情報の送信,データのやり取りの後,ストップコンディションという,スタートコンディションとは反対にSCLが上がっている時にSDAを上げることで,「通信終了ォ!」とマスタが宣言し,終了します.
この一連のやりとりの中で,最初にスレーブアドレスと同時にR/W情報も送信しているので,一連の過程において送信者,受信者の立場は固定されています.ここで,通信中に立場を変えたい場合(最初に読みたいアドレスを送り,帰ってくる情報を見たい等)があります.
この時は,ストップコンディションを発行せずに,再びスタートコンディションを発行し,R/Wビットを変更することで実現できます.この時に発行するスタートコンディションの事を,リピートスタートコンディションと呼びます.

赤枠の部分,SCLが上がっているタイミングでSDAが下がり,スタートコンディションが発行されていますね.

f:id:protocol1964:20170220231727j:plain


I2Cよく知らんけどとりあえず使ってるという人でも,これで前述の通信タイミングの説明を理解できるかと思います.

さて,まずは実際に動いたソースがこちら

#include "mbed.h"

Serial pc(dp16, dp15);
I2C i2c(dp5, dp27);

int main() {
    i2c.frequency(20000);
    while(1){
        char recieve[3];
        i2c.start();
        if(i2c.write(0xb4)){
            if(i2c.write(0x07)){
                i2c.read(0xb5, recieve, 3);
            }
        }
        i2c.stop();
        int h = recieve[1] << 8;
        int l = recieve[0];
        int tmp = h+l;
        float temp = (((float)tmp * 2.0) - 27315.0)/100.0;
        printf("temp:%f\n\r", temp);
        wait(0.5);
    }
}

ifの中でwriteを実行していますが,多分ちょっと違うと思うのであまり気にしないでください.実行順序の参考程度に.

まず,I2Cのクロック周波数について,データシートにはこう書いてあります.

f:id:protocol1964:20170220231138p:plain

100kHz〜10kHzで使ってね!とのことですが,10kHzだと通信がうまく行きませんでした.適当に設定しましょう.

あとは指示されたタイミング通りに関数を叩くだけです.


まずスタートコンディションを発行するために,i2c.start()を実行.

次にスレーブアドレス0x5aと,書き込みを示す0を送信するため,i2c.write(0xb4)を実行します.0xb4とは,要はこういうことです.

f:id:protocol1964:20170220233546p:plain

次に,アドレス0x07を送信します.詳細はデータシート8.3.4項まで.


ここで,i2c.write()の連続送信を使って0xb4と0x07を送ればええやん!と言われる気がします.
i2c.write()の連続送信を用いると,スタートコンディションとストップコンディションが自動発行され,送信するだけならこの関数1つ叩けば終わります.ただ,送信後にリセットスタートコンディションを発行したいので,ストップコンディションが自動発行されるのはマズイ.

i2c.write()の4つ目の引数をtrueにすれば,連続送信後にストップコンディションが自動発行されずに済むハズなのですが,オシロで見てみると何故かストップコンディションが発行されている…ので,このような形になりました.もしかしたら,ここらで悩んでいた時に設定していたSCLの周波数が遅すぎて不具合があったのかもしれません.


さて,アドレス,データの送信まで終わったので,再びスタートコンディションを発行して,今度は読み取りです.
3バイトが連続で送られてくるので,i2c.read()の連続受信を使用します.アドレスは上の図と同様に,Readの1を当てて0xb5です.
i2c.read()の連続受信ではスタートコンディションが自動発行されるので,i2c.start()は省略しています.

3バイト受信し,最後にi2c.stop()でストップコンディションを発行して終了です.記事書きながら思ったのですが,i2c.read()の連続受信はストップコンディションを自動発行するはずなので,i2c.stop()はやらなくても良い気がします.

最後に,受信したデータを計算することで摂氏温度へ変換します.


…と,このような感じで動かせました.

センサの特性上,物体から離れるほど検知する温度は低くなっていき,対象物の材料によって放射率も変わってくるので,そこらへんは要調整ですね.

大会まで1ヶ月ちょい.この調子で大丈夫なんだろうか…


[追記]
ググったらmbedライブラリが出てきた.なんで調べなかったんだろう… mbed恐るべし
MLX90614 I2C Infrared Thermometer | mbed