IoTで使うPython入門Step1-I2C LM75Bで温度測定 (1) smbusライブラリ
ラズパイにはアナログ入力がありません。温度を測定するにはアナログ温度センサとA-Dコンバータを利用するのではなく、ディジタル出力が直接得られる温度デバイスがお勧めです。
- SPI;信号線は3本もしくは4本
- I2C;信号線は2本
- 1-Wire;信号線は1本
これ以外にも独自のインターフェースのセンサはありますが、上記のいずれかが使いやすいです。
●I2CのLM75Bのおもな特徴
LM75は、確度が±2℃と実用的な温度センサです。複数の会社で製造されています。入手したのはDigi-Keyからで、NXPの製品です。メーカによってデータ長が異なり、NXPのLM75Bは11ビットです。データはtwo’s complement format =2の補数形式で、最上位ビットが符号を表します。11ビットなので読み出すのは2バイトのデータになります。
- インターフェース;I2C。スレーブ・アドレスの設定には三つのピンが用意されている(8アドレス)
- 温度データ・フォーマット;11ビット。データの最上位ビットは符号で、左詰め。2の補数形式
- 測定温度範囲;-55~125℃
- 温度の確度;±2℃(-25~+100℃)
- 最小分解能;0.125℃
- サーモスタットとして利用できる温度設定と出力端子
- 電源電圧;2.8~5.5V
- データ転送速度;400kHz
電源に3.3Vが使えるので、ラズパイとはI2Cバスで直結できます。A0、A1、A2端子はVccもしくはGNDに使いでスレーブ・アドレスを設定します。OS端子は設定した上限温度を超えるとLowになるオープン・ドレイン出力です。
ラズパイのI2Cバス1は、1.8kΩで3.3Vへプルアップされています。
●I2Cバスの特徴
ラズパイのGPIOには二つのI2Cバスが用意されています。通常バス1を使いますが、バス0も利用できます(※)。I2Cの信号は2本です。
- SDA データのやり取り、データは双方向
- SCL クロック。クロックはマスタのラズパイからスレーブのLM75Bへ送られる
二つの信号でデータをやり取りするためのI2Cプロトコルは、PCで利用できるSMBUSとほとんど同じです。規格書はNXPから日本語の資料が入手できますが、分量が多いです。普通の用途で利用されるのはその中のベーシックな部分だけです。
細かい動きの中で、基本的な部分はこちらの記事「マスタリングWireライブラリ その1 スタートとストップ・コンディション」を参照してください。ArduinoではWireライブラリを使います。ラズパイのpythonではsmbusライブラリを利用します。現在のラズパイにはインストールされています(※2)。
●smbusライブラリ
◆Read
8ビット単位で行われます。常に最上位ビットMSBから送られます。
①マスタが通信を始める最初にバスをスタート・コンディションにする ②マスタのラズパイが「スレーブ・アドレス7ビット+Readの1ビット」をバスに出す ③自分のアドレスがバスに現れるのを監視しているスレーブ・デバイスは、自分宛であったら存在を示すのにAckの1ビット(Low)をバスに出す。Ack=Acknowlefgementは、マスタに対して続いてデータを送ってもよいという通知 ④スレーブはデータ8ビットを送り出す。 ⑤そのデータを受け取ったマスタはNAckを出す。複数バイトのときはAckを出して④に戻る ⑥マスタはストップ・コンディションにする |
smbusライブラリの書式です。アドレスは7ビットのスレーブ・アドレス、コマンドはレジスタの値などを入れます。
1バイトだけ;read_byte_data(アドレス, コマンド) 2バイト;read_word_data(アドレス, コマンド) 2バイト以上;read_i2c_block_data(アドレス, コマンド, バイト数) read_block_data(アドレス, コマンド) があるが、使い方が不明。 |
◆Write
デバイスによっては機能を設定するために、スレーブ・デバイスの内部レジスタに書き込みを行ってからReadすることがあります。LCD表示器などの出力に使います。Writeのやり取りです。
①マスタが通信を始める最初にバスをスタート・コンディションにする ②マスタのラズパイが「スレーブ・アドレス7ビット+Writeの1ビット」をバスに出す ③自分のアドレスがバスに現れるのを監視しているスレーブ・デバイスは、Ackを出す ④マスタはそのAckを認識したら、続いてコマンド(書き込むレジスタのアドレスやポインタ・アドレス)をバスに送る ⑤スレーブがAckを出す ⑥マスタはAckを認識すると、引く続き設定データをバスに送る ⑦スレーブがAckを出す ⑥マスタはストップ・コンディションにする |
smbusライブラリの書式です。
1バイトだけ;write_byte_data(アドレス, コマンド) 2バイト;write_word_data(アドレス, コマンド) 2バイト以上;write_block_data(アドレス, コマンド, バイト数) もしくは、write_i2c_block_data(アドレス, コマンド, バイト数) |
◆レジスタ指定のRead
読み出しのときに、読み出すポインタやレジスタを指定する方法があります。書式はReadと同じですが、ポインタやレジスタを指定するので、一度それらをスレーブに対してWriteしたのちReadします。したがって、Write+Readの二つのシーケンスが必要です。
しかし、Writeした直後ストップ・コンディションになるとバスが解放されているので、ほかの通信が始まることがあり得ます(ラズパイでは発生しない)。それを避けるためにWriteの最後でストップ・コンディションにせず、リピーテッド・スタート・コンディションを作りReadします。
ArduinoのWireライブラリでは、Wire.endTransmission(false); と明示的に示します。ラズパイは2016年ごろまで、combined=1という指定を書きましたが、2017年中ごろから、その記述をしなくても、リピーテッド・スタート・コンディションが有効になっているようです(※3)。
①マスタが通信を始める最初にバスをスタート・コンディションにする ②マスタのラズパイが「スレーブ・アドレス7ビット+Writeの1ビット」をバスに出す ③自分のアドレスがバスに現れるのを監視しているスレーブ・デバイスは、自分宛であったら存在を示すのにAckの1ビット(Low)をバスに出す ④マスタはそのAckを認識したら、続いてコマンド(書き込むレジスタのアドレスやポインタ・アドレス)をバスに送る ⑤マスタはリピーテッド・スタート・コンディションにする ⑥マスタのラズパイが「スレーブ・アドレス7ビット+Readの1ビット」をバスに出す ⑦スレーブ・デバイスは、Ackの1ビット(Low)をバスに出す ⑧スレーブはデータ8ビットを送り出す ⑨そのデータを受け取ったマスタはNAckを出す。2バイト以上のときはAckを出して⑧に戻る ⑩マスタはストップ・コンディションにする |
(※)2018/10/30 ⑥と⑦は不要でした。
リピーテッド・スタート・コンディションはリピート・スタート・コンディションと表記されることもあります。smbusライブラリの書式はReadと同じです。
●データ転送速度
Standard-mode では最大100kbit/s、Fast-mode では最大400kbit/s、Fast-mode Plus では最大 1Mbit/s、High-speedでは最大 3.4Mbit/s のデータ転送が可能です。
smbusライブラリのデフォルトは100kbit/sです。デバイスの多くは400kbit/sをサポートしています。それ以上のスピードは、I2Cバス自体の駆動能力とのかねあいでプルアップ抵抗値を低い値に変更する場合があります。
100kbit/sは100kHzとも表記されます。
smbusライブラリでスピードを変更するときは、/boot/config.txtに、
dtparam=i2c_baudrate=400000 |
を追加し、rebootします(※4)。
●I2Cの有効化
ラズパイのラズビアンをインストールした直後では、I2Cバスのアクセスはできません。
lsmodを実行すると、いろいろな周辺機器のドライバの利用状況が表示できます。
I2Cバスの有効化は、メイン・メニューの設定-Raspberry Piの設定から行います。
I2Cが無効になっています。
有効にチェックします。OKを押した後rebootすると有効になります。
lsmodを実行します。SPIも有効にした状態です。i2c_bcm2835 spi_bcm2835が追加されています。
●プログラム
下のプログラムは温度を読み取って表示しています。
import smbus
import time
i2c = smbus.SMBus(1)
addr = 0x4c # LM75B NXP. TI is 9bit
def sign16(x):
return (-(x & 0b1000000000000000) | (x & 0b0111111111111111))
# main
while 1:
data = i2c.read_i2c_block_data(addr, 0)
raw = ((data[0]) << 8) | (data[1])
print(hex(data[0])),
print(hex(data[1])),
print(hex(raw))
raw_s = sign16(int(hex(raw), 16))
temp = (raw_s >> 5) * 0.125
print(str(temp) + "C")
time.sleep(1)
LM75Bは、温度レジスタからデータを読み出すのに、リピート・スタートを利用します。温度ポインタは0です。読み出すのは2バイトです。
data = i2c.read_i2c_block_data(addr, 0) |
読み出すバイト数を指定していないのですが、なんとうまいことに上位バイトがdata[0]に、下位バイトがdata[1]に入っています。上位バイトを8ビット左にシフトし、下位バイトとビット演算子でorを取ると16ビットのデータになります。I2Cは8ビット単位のやり取りなので、16ビットを一度に取り出せないのです。
16ビットのうち温度データの11ビットが左詰めで入っているので、5ビット右にシフトすると11ビットの温度データだけになりました。シフトすると上位の5ビットは0が入ります。最上位ビットが符号であることを無視すれば、このデータに0.125を掛けると温度になります。2の補数の形式はとても便利なフォーマットです。
ここで疑問がありますね。
- 2バイトならread_word_data()が使える
- 都合がよいことに読み出したデータは何も宣言していないのに配列data[]に入っている(※5)
- 上位と下位データの合成に|を使っているがそれでいいのか
- 読み出しのときのパラメータにバイト数を忘れている
次回以降、実験しながら、謎や間違いを解き明かしていきます。
●接続
一番大きなパッケージがSO8、今回利用したのが少しコンパクトなTSOOP8、そのほかにリード線が外側に出ていないよりコンパクトなパッケージもあります。入手したTSOOP8をそのままでは利用できないので、変換ボードで普通のDIP(ピンピッチが2.54mm)に乗せ換えます。通常DIP化と呼びます。DIP化したボードは、ブレッドボードに挿しこんで利用できます。ここでは、秋月電子通商の「MSOPパッケージ(8ピン 0.65mmピッチ)用DIP変換基板 金フラッシュ (10枚入)」を利用しました。
スレーブ・アドレスは、b1001A2A1A0で設定します。A0=A1=GND、A2=Vccにつなぐとb1001100=0x4cです。
●I2Cツールでデバイスの存在を確認
ラズパイにはi2c-toolsがインストールされています。
- i2cdetect
- i2cset
- i2cget
- i2cdump
一番よく使う i2cdetect -y 1は、バス1の接続状況を表示します。三つのデバイスを見つけました。LM75BはA1、A2、A3のピンでアドレスを変更できます。0x48と0x4cがLM74Bです。ここでは利用していませんが、温湿度センサSHT31が0x44です。SHT31はaddrピンの接続で0x44もしくは0x45にスレーブ・アドレスを設定できます。
(※1)ターミナルで、ls /dev/i2c*と入力すると、バス1のi2c-1だけが見つかります。 /boot/config.txtに、dtparam=i2c_vc=onを追加してrebootすると、i2c-0も見つかります。
(※2)Pythonのツールpip listではsmbusライブラリは表示されません。dpkg -l | grep smbus*もしくはapt list | grep smbus*で2018年10月現在、Python2と3の3.1.2-3がインストールされていることがわかります。パッケージはpython-smbusです。
(※3)リピート・スタート・コンディションが有効だと、通常の通信時にトラブルが起こったデバイスがあったためだという記事がありました。Linuxのドライバ・レベルの対応なので、ある時期からカーネルのバージョンで、リピート・スタート・コンディションが有効でも普通の通信に支障がなくなったのかもしれません。
(※4)/boot/config.txtの変更は、root権限が必要です。piでログインしているときは、sudo nano /boot/config.txtでファイルをオープンし、修正を行い、CTRL-Oで上書き書き込み、CTRL-Xで終了します。
(※5)配列ではなくリストであることは、次回判明します。
(※)ブレッドボードに挿して利用できるボードはブレークアウト・ボードとも呼ばれます。例えばAdafruitで販売していてライブラリもありますが、現在は商品が見つかりません。スイッチサイエンスが同様なボードを作っています。