AVRtiny13Aでつくるシリーズ6 気圧センサとI2Cのリード
- 2016/04/05 09:44
- カテゴリー:make:
そろそろ面倒くさくなってきたtiny13を使った工作ですが,今回で一応一区切りです。I2Cについては,よく考えるとライトはやっていますが,リードはやっていません。センサなどを繋いだ場合はリードがメインになるだけに,これをやっていないというのは少々物足りません。
なにかないかなとジャンク箱を漁っていたら,随分昔に秋月で購入した気圧センサ,フリースケールのMPL115A2が出てきました。600円。今,同じ物が400円で売られているんですね。
I2Cで繋がる気圧センサとしては数年前からよく知られたメジャーなものです。デジタルで情報が飛んできますが,残念ながら値を結構複雑な計算で求めねばなりませんし,チップごとに校正された補正値を読み込んで,その係数を使う必要があったりとなかなか面倒です。
そのわりには,今ひとつ精度も良くないという事らしく,人気の方は今ひとつという感じです。
ですが,ちょうどI2Cのリードを試すにはちょうどいいですし,600円もしたんですから使ってみたいと思いました。しかし,この気圧センサはLGAというピンのないパッケージに入っており,小型である事もあいまって,実装が大変です。
変換基板などもありませんので細いワイヤーで信号を引っ張り出すことにしますが,電源とGNDとI2Cの信号線だけですから,4本だけです。それならなんとかなるでしょう。
ということで,信号線を引っ張り作業を始めたのですが,これがなかなか難しい。すぐに熱が回ってしまうので,一度付けたハンダがポロッと外れてしまうこともしばしばです。
そうこうしているうちに,なんと電源のランドがハンダゴテの熱で剥がれてしまいました。万事休す。もうダメだ。俺は寝る。
しかし,虫眼鏡でよく見ると,剥がれたランドのそばに,電源のパターンが走っています。ここにワイヤーをハンダ付けすればなんとかなるかも知れません。
慎重に他の信号も引っ張り出して,とにかく外部にピンを出す事に成功しました。
でも,これで動く保証はありません。というか,ほぼ壊れていると考えていいでしょう。そこで,mbedで動作を確認します。サンプルコードを探すと,あったあったありました。
さくっと実行すると,気圧と温度と高度がLCDに表示されます。それなりに意味のある数字が出ているようですので,とりあえずこの気圧センサは生きているようです。
で,あとはtiny13Aです。
I2Cのライブラリの中に,リードを行う関数が用意されてはいるのですが,これをコールすればそれでいいというような簡単な話ではありません。
MPL115A2の仕様書を読んでいると,リードの際にはまずコマンドを発行して読みたいレジスタを指定するとあります。それが済んだらリードをするという事です。
ここで注意すべきは,ホストがリードを行う場合には,アドレスに+1しないといけないということです。この+1は,R/Wビットといって,ホストがライトするときには0,リードするときには1を書きます。
これを0ビット目とし,あとはアドレスを1から7ビット目まで置いた合計8ビットを,スタートコンディションの次に送ります。
MPL115A2のI2Cアドレスは0x60なのですが,ライトの時には0xc0を,リードの時には0xc1を送る必要があるということですね。
ところが,スタートコンディションに続いてアドレスを送り,コマンドを送った後,どうやってR/Wビットを1にしてリードモードにすればいいのでしょう。
仕様書によると,ここはリピートスタートコンディションを送るとのこと。
べた書きすると,こんな感じになります。
I2C_Start();
I2C_write(0xc0); // addr + R/W bit = 0
I2C_write(0x04); // command 0x04(read add = 0x04)
I2C_Start(); // Repeat start condition(W/O Stop condition)
I2C_write(0xc1); // addr + R/W bit = 1
a0m = I2C_Read();
a0l = I2C_Read();
b1m = I2C_Read();
b1l = I2C_Read();
b2m = I2C_Read();
b2l = I2C_Read();
c12m = I2C_Read();
c12l = I2C_Read();
I2C_Stop();
これは8つの補正値を読み出すコードなのですが,I2Cではストップコンディションを発行する前にスタートコンディションを発行すると,それはリピートスタートコンディションと見なされて,バスが解放されないまま,アドレスを再送出来るのです。
ところが,変換開始から変換後の値を読み出すところは,またちょっと違うんです。これも仕様書によると,以下のようになります。
I2C_Start();
I2C_write(0xc0); // addr + R/Wbit = 0
I2C_write(0x12); // command = 0x12, 0x01(convert start)
I2C_write(0x01);
I2C_Stop();
_delay_ms(5);
I2C_Start();
I2C_write(0xc0); // addr + R/Wbit = 0
I2C_write(0x00); // command 0x00(read add=0x00)
I2C_Start(); // Repeat start condition
I2C_write(0xc1); // addr + R/Wbit = 1
bm = I2C_Read();
bl = I2C_Read();
tm = I2C_Read();
tl = I2C_Read();
I2C_Stop();
0x12というのは変換開始のコマンド,次の0x01はダミーです。ここで一度ストップコンディションを発行してバスを解放しています。そして数ms待った後で,改めてスタートコンディションを出しているんです。
あとはどこを読みたいか書き込んで,リピートスタートコンディションを出してから読むという流れなのですが,変換開始のコマンドのあと,わざわざバスを解放しないといけない理由がよく分かりません。
まあ,普通に考えたら5msも待つんですから,その間にバスを明け渡して他が使えるようにしないといけないわけですが,今回のようにバスを占有しても問題がない場合は,別にいいんじゃないかと思ったのです。
しかし,試してみたらダメでした。
やはり仕様書の通りに書かないとダメのようです。
で,あとは気圧と温度の表示なのですが,これがとても面倒くさい。とても1kバイトのメモリに入りそうにないと悟った私は,とりあえず気温だけ表示することにしました。
t = tm<<2 | tl>>6;
t = 250-(t-498)*1000/535;
lcd_cmd(0xc0);
lcd_data(t/100 + 0x30);
lcd_data(t/10%10 + 0x30);
lcd_data('.');
lcd_data(t%10 + 0x30);
lcd_data('C');
tはint型で,tmとtlはchar型です。tmを2ビットずらし,tlを6ビットずらして,トータル10ビットの値として,int型に格納します。出来るだけ小数点を使わないようにするために,補正式をちょっと修正しました。本当は,25-(t-498)/5.35で計算するんだそうです。でもおかしいんですよ。472で25度になるという話なのに,この式だと25度にならないですよね・・・まあいいか。
一応これで温度の表示は出来たのですが,値がとにかく数度単位でばらつくんです。それでももうやる気を失って,やめました。
ということで,なんとなくだらけたところで,このシリーズはちょっと休憩。小さい小さいマイコンを気軽に使いたいという話から,なぜか限界ギリギリを責める話になってしまったわけですが,これはこれでパズルを解くような面白さがあって,とても楽しかったです。
たまにはこういうこともやってみるものです。