RaspberryPiとADT7410で16bit解像度 I2C 温度測定をする

#概要
- RaspberryPiと秋月電子製のADT7410モジュールを使って温度測定をしました。デフォルト解像度は13bit幅ですが、Configレジスタ0x03の7ビット目に1を設定する事で16bitで計測してみました。

- 計測レンジは-55～150℃で、13bitだと0.0625℃きざみ、16bitだと0.0078℃きざみでより細かく計測できます。


#構成
![20191209_171135.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530581/5a62e3f0-cfe7-a4b5-a182-9642f42de1cb.jpeg)
RaspberryPiのSDA1⇔ADT7410のSDA、SCL1⇔SCL、3V3⇔VDD、GND⇔GNDのように接続します。

#ソース
```ADT7410_16bit.py
import smbus
from time import sleep

def read_adt7410():
    data = bus.read_word_data(adt7410,reg_val)
    temp = (data&0xff00)>>8 | (data&0x00ff)<<8
    if temp & 0x8000 == 0:
        temp2 = temp / float(128)
        #temp2 = temp * 0.0078125
    else:
        temp2 = (temp - 65536) / float(128)
        #temp2 = (~temp&0xffff+1) / float(128)

    return temp2


bus = smbus.SMBus(1)
adt7410 = 0x48
reg_val = 0x00
reg_conf = 0x03

try:
    bus.write_byte_data(adt7410,reg_conf,0x80)

    while True:
        val = read_adt7410()
        print ("{0:.5f}".format(val))
        sleep(0.5)

except KeyboardInterrupt:
    pass

```

###説明

- smbusライブラリをインポートして処理します。bus.write_byte_data(adt7410のアドレス0x48,configレジスタのアドレス0x03, configレジスタの7bit目だけ1にするため0x80 = 0b10000000 書き込み)　で解像度を13bitから16bitに変更を1回だけしています。

> ADT7410データシートP14抜粋
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530581/18fb9526-9c89-7cbb-0ab3-08025f331191.png)

- bus.read_word_dataで、wordつまり2bytes=16bitsのAD変換値を得ます。戻り値が[LSB 7:0][MSB 15:8]のようになっているのでMSB、LSBの順になるようにビット演算をします。
- 16bit目が1ならマイナス温度、0ならプラス温度になるので、それぞれ計算をしています。下記のPositive Temperatureで÷128としているのは×(1/128)をかけているのと同じ、128分の1は0.0078125、これだと理解しやすいです。例えばAD変換値tempが2560の場合、2560×0.0078125=20℃となります。やっかいなのは零下の計算で、AD変換値から65536を引いてから0.0078125をかけています。65536を引いているのはAD変換値の2の補数を得るのと等価だと理解しています。ビットで考えるとわかりやすく、まず65536(10進)=1 0000 0000 0000 0000(2進)です。これに対して-55℃の場合AD変換値が58496(10進)=1110 0100 1000 0000(2進)になることを例とし、式の通りに計算すると(58496-65536)*0.0078125 = -55となります。これを2進数で同じことをすると、(1110 0100 1000 0000)-(1 0000 0000 0000 0000) = (0001 1011 1000 0000)、計算結果は2の補数となっています(全てのビットを反転して+1)。__つまり例えば16bitデータに対しては17bit目が1で16bit以下が0の値(=今回の場合65536)をAD変換値から引くことで、2の補数(-値の絶対値)を得られます。__それに解像度をかければよいのです。わかりにくい説明ですみません。ちなみに負数計算式はもう一つ32768を引いているものがありますが、これは計測レンジがマイナス側は-55とプラスと比較して少ないことにより、16bitデータの場合負の数であれば16bitは符号ビットなのでもちろん、15bit目も必ず1になります。そのため15bit目を無視して14bitのデータで算出をしようとするもので、この場合14+1=15bit目だけ1の32768を引けば先の計算と同じように2の補数、マイナスの絶対値が出せるというものです。データシートにはありませんがたぶん14bit目も負の場合1固定なので15,14bit目を無視して16384を引くのでも算出できるのではないかと思います。

> データシートP12抜粋
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530581/b20f5541-fb64-0ae5-60b6-79c887c8ab2b.png)


#ロジアナ解析
![19.36719.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530581/31eafaa1-7990-8c4f-8cc0-0876714f9601.jpeg)

###説明
順に説明していきます。

1. SCLがHIGHの時にSDAがLOWになるとスタートです。
2. write_byte_dataメソッドで0x03Configレジスタに0x80を書き込んで16bitモードに設定変更しています。i2cは基本的に8bitデータをマスタあるいはスレーブから送信して、受信側デバイスがAck(Acknowledge)を出す、イコールLowにSDAを落とす事で一区切りとする仕様です。
3. SDAがLOWの時にSCLがHIGHになり、次にSDAがHIGHになるとストップです。通常はSCLがLOWの時のみSDAが変化し、スタート/ストップの特殊な時のみSCLがHIGHの時にSDAが変化する事で区別しています。
4. 二つ目のスタートはread_word_dataメソッドで、まずはAD変換された温度データが格納されている0x00(MSB)、0x01(LSB)レジスタからデータを読み取っています。対象アドレスは0x00を指定するだけで、wordなので自動インクリメントして16bit分のデータを読み取ってくれます。
5. このケースの場合、MSB 0x09、LSB 0xAFなので0000 1001 1010 1111 → 2479(10進)、2479*0.0078125≒19.36℃となります。デフォルトの13bitモードでは[LSB 2:0]は閾値Thigh、Tlow、Tcritを超えた場合のみ1がセットされるので0になっているはずですが16bitモードにしているのでAD変換値に利用されているのが分かりますね。

#参考

* [RaspberryPiで学ぶ電子工作](https://www.amazon.co.jp/%E3%82%AB%E3%83%A9%E3%83%BC%E5%9B%B3%E8%A7%A3-Raspberry-Pi%E3%81%A7%E5%AD%A6%E3%81%B6%E9%9B%BB%E5%AD%90%E5%B7%A5%E4%BD%9C-%E4%BD%9C%E3%81%A3%E3%81%A6%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E3%81%97%E3%81%8F%E3%81%BF%E3%81%8C%E3%82%8F%E3%81%8B%E3%82%8B-%E3%83%96%E3%83%AB%E3%83%BC%E3%83%90%E3%83%83%E3%82%AF%E3%82%B9/dp/4062579774)
* [picfun I2C通信の仕様](http://www.picfun.com/c15.html)
* [smbusライブラリメソッド仕様](https://www.denshi.club/pc/raspi/raspberry-pii2c2-python.html)
* [Pythonで整数を割り算したときに小数を切り捨てない方法](https://kaworu.jpn.org/python/Python%E3%81%A7%E6%95%B4%E6%95%B0%E3%82%92%E5%89%B2%E3%82%8A%E7%AE%97%E3%81%97%E3%81%9F%E3%81%A8%E3%81%8D%E3%81%AB%E5%B0%8F%E6%95%B0%E3%82%92%E5%88%87%E3%82%8A%E6%8D%A8%E3%81%A6%E3%81%AA%E3%81%84%E6%96%B9%E6%B3%95)
* [秋月電子 ADT7410モジュール](http://akizukidenshi.com/catalog/g/gM-06675/)