Raspberry Pi でI2C: 温度センサーを使う

Raspberry PiでI2Cデバイスをいくつか使ってみたので、何回かに分けて紹介したいと思います。

まずは秋月のADT7410を使用した温度センサーモジュールを接続し、温度データを読めるようになるまでの道のりを紹介します。
ただしこのデバイスRaspberry Piとは相性が悪く、ネイティブのI2Cドライバを使用したアクセスにはかなりの制限が避けられませんでした。
しかし回避策も一応存在しますのでそれも合わせて紹介します。

準備

バイス接続

GND, VDD(3.3V), SCL, SDA を接続します。Raspberry Pi のピンは以下のものを使用します。

とりあえず接続だけした状態です。
配線については追々きれいにしていきます。
下の方に伸びて緑色のスイッチにつながっている2本の線はシャットダウンスイッチなので今回は関係ありません。

センサーモジュールのボード上でSCL, SCAのプルアップができますが、
プロセッサ内でプルアップされているようなので(データシート確認していませんが)
バイス側では不要のようです。

また同様にI2Cスレーブアドレス(デフォルト0x48)の変更もできますので必要に応じてパッドをショートさせて設定してください。

カーネルモジュール

i2c-dev と、依存するモジュールとして i2c-bcm2708 が必要ですが、
i2c-bcm2708 はblacklist.confに入っていて自動ではロードしないようになっていますので、これを解除します。

/etc/modprobe.d/raspi-blacklist.conf
blacklist i2c-bcm2708 をコメントアウト

# blacklist spi and i2c by default (many users don't need them)

blacklist spi-bcm2708
#blacklist i2c-bcm2708

その上で、i2c-devをロードします。

% sudo modprobe i2c-dev

起動時に自動でロードさせたい場合は /etc/modules に i2c-dev を追加します。

必要パッケージのインストール

i2c-toolsをインストールします。

% sudo apt-get install i2c-tools

以上で準備は完了です。

バイスへのアクセス確認

バイス検出

i2cdetectでデバイスを検出してみます。コマンドラインオプション最後の'1'はI2Cバス番号ですが、
Raspberry Piのハードウェアリビジョンによって0か1か変わりますので注意してください。
(R1:0, R2:1)

% sudo /usr/sbin/i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 

I2Cアドレス0x48になにかいることがわかります。

レジスタread

ADT7410のデータシートによると、温度データは2byteでレジスタアドレス0x00がMSB, 0x01がLSB(Big Endian的な配置)です。
i2cgetを使ってアドレス0x00からwordアクセスしてみます。(後述しますが、0x00, 0x01へのバイトアクセス2回では動きませんのでご注意)

% sudo /usr/sbin/i2cget -y 1 0x48 0x00 w
0x180d

これをバイトスワップして(9.7)フォーマット(signed)で評価すると温度データ(摂氏)になります。
バイトスワップ→0x0d18
(9.7)で評価→26.1875(度C)

問題点

「0x00, 0x01へのバイトアクセス2回では動きません」と書きましたが、実際それを行うと以下のように0x00,0x01で全く同じデータが観測されてしまいます。

% sudo /usr/sbin/i2cget -y 1 0x48 0x00 b
0x0d
% sudo /usr/sbin/i2cget -y 1 0x48 0x01 b
0x0d

またi2cdumpで全レジスタを読み出してみても、以下のように明らかにおかしなデータとなります。

% sudo /usr/sbin/i2cdump -y 1 0x48 b
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c    ????????????????
10: 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c    ????????????????
20: 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 0c 00    ???????????????.
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 0c 0c    .............???

読み出し単位を変えると少し変化しますが、最大でも4byteの繰り返しです。

% sudo /usr/sbin/i2cdump -y 1 0x48 i
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
10: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
20: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
30: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
40: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
50: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
60: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
70: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
80: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
90: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
a0: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
b0: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
c0: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
d0: 0c 58 80 00 0c 58 80 00 0c 58 80 00 0c 58 80 00    ?X?.?X?.?X?.?X?.
e0: 0c 50 80 00 0c 50 80 00 0c 50 80 00 0c 50 80 00    ?P?.?P?.?P?.?P?.
f0: 0c 50 80 00 0c 50 80 00 0c 50 80 00 0c 50 80 00    ?P?.?P?.?P?.?P?.

これはどうやらADT7410がI2CのRepeated Start Conditionという動作を要求する一方で、
Raspberry PiのプロセッサBCM2835のI2Cモジュールがそれに非対応ということが原因のようです。

きちんと理解していませんが、Stop Conditionを挟むとアドレスがリセットされてしまう、とかそんな感じ?でしょうか

これはもうハードウェアの問題なのでソフトウェアではどうしようもありません。
以下のサイトでは外付けロジックでSCLの論理を変えちゃおうという試みについて書かれていますのでどうしてもという方は参照してください。
http://www.circuitwizard.de/raspi-i2c-fix/raspi-i2c-fix.html

HiPi (I2C bit banging)

上記問題の1つの解決方法が、perlのHiPiというモジュールを使う方法です。
これはハードウェアのI2C機能を使わず、SCL,SDAピンをGPIOとして扱って全てソフトウェア的にロジックをコントロールしてしまおうというものです(この手のものをbit bangingと呼ぶようです)。
当然のことながらハードウェアコントロールに比べるとむちゃくちゃ遅いのですが、
それでもこちらはRepeated Start Conditionに対応しているのでとりあえず任意のアドレスにアクセスできます。

以下の手順でインストールします。

% perl -MCPAN -e 'install "LWP:Simple"'
% wget http://raspberry.znix.com/hipifiles/hipi-install
% perl hipi-install

アドレス0x00,0x01を読んでみます。

% sudo hipi-i2c r 1 0x48 0x00 1
12
% sudo hipi-i2c r 1 0x48 0x01 1
96

それらしい値が取得できました。(繰り返しますが、むちゃくちゃ遅いです)

またi2c-toolsではどうやっても読めなかった、アドレス0x0b(チップのID)を読んでみます。

% sudo hipi-i2c r 1 0x48 0x0b 1
203

データシートによると 0b11001xxx(0xc8-0xcf)が読めるはずですので、203=0xcbということでOKです。

幸いなことに、頻繁にアクセスする温度データは0x00-0x01にあるため
i2c-toolsのwordアクセス(ひいてはnativeドライバのAPI)で読み出せますし、
設定を変えるため諸々のレジスタにアクセスする場合はHiPiを使用することができるため、
結果的には一応フル機能使えるということになりそうです。

まとめ

以上でI2C温度センサーが使用できるようになりました。
一口にI2Cと言っても本デバイスのように相性が悪いものもあるみたいですが、この手の工作は多少の困難があった方が楽しいですよね (^^;

おまけ

ADT7410は温度スレッショルドを設定して割り込みを出す機能がありますので、
チップからRaspberry PiのGPIOに配線すればそのような機能も使うことができると思います。