5ドル!ラズパイ・ゼロ(Raspberry pi Zero)でIoT (4) A-Dコンバータの利用1 MCP3424
■アナログ入力が扱えると便利
温度や湿度を測るとき、専用のセンサをつないで利用します。電圧を測るためにA-Dコンバータが利用できると、あらゆるアナログ・データを読み込めるので汎用性が上がります。
ラズパイにはA-D変換機能はありません。GPIOにA-Dコンバータ(ADC)をつなぎます。A-Dコンバータには、3種類のインターフェースがあります。
- I2C
- SPI
- 独自シリアル通信
A-Dコンバータは次の項目に注目して選択します。
- ビット数
- 変換時間
手軽に使えるのは10ビットですが、計測などでは12ビットから16ビットが欲しいところです。24ビットもありますが、ユニバーサル基板に実装して利用しても、その精度は得られません。10ビットのA-Dコンバータは、変換時の基準電圧が3.3Vとした場合、一番細かな数字LSBは、
3.3V / 2^10 = 3.2mV |
同様にほかのビット数では次のような数字になります。ビット数が大きいほど、細かい単位でアナログをディジタルに変換できます。しかし、温度変化やノイズが原因で、12ビット以上のデータを正確にラズパイに取り込むのは難しいです。
- 12ビット 800uV
- 16ビット 50uV
- 18ビット 12.6uV
- 24ビット 0.20uV
A-Dコンバータには、内部の構成によっていくつかに分類されます。
- 二重積分方式。古くから精度の良い方式として使われていたが今は主流ではない
- ΔΣ方式。中低速用としては現在主流の方式。製造コストが安い
- 逐次比較型。中速の変換方式の主流
- 並列型。高速だが、ビット数を増やすには回路が大きくなり発熱が大きい
●MCP3424をPythonで使う
マイクロチップのMCP3424は12ビットから18ビットが選択できる、4チャネル入力のA-Dコンバータです。インターフェースはI2Cです。DIPタイプの形状の製品がないので、ブレッドボードで利用する場合には、変化基板に実装された製品を購入します。同じシリーズに、MCP3425があり、こちらは16ビットで1チャネル入力です。秋月電子通商でDIP化基板に搭載された製品があります。
4チャネルというのは、入力端子が4組あり、ソフトウェアで切り替えて使えるので、複数のセンサなどをつなげられます。変換ユニット自体は一組あるだけなので、高速に変換はできません。
●MCP3424の主なスペック
- ビット数;12、14、16、18から選択
- 基準電圧;2.048V内蔵
- 電源電圧; 2.7~5.5V
- 入力;差動
- 増幅器;プラグラマブル前置アンプ内蔵(PGA)。1倍、2倍、4倍、8倍から選択
- 変換方式;ΔΣ(デルタ・シグマ)方式
- 変換速度;3.75 SPS (18ビット)、15 SPS (16ビット)、60 SPS (14ビット)、240 SPS (12ビット)から選択
- インターフェース;I2C
- I2Cデータ転送速度;standard (100 kHz)、fast (400kHz)、high-speed (3.4 MHz)モードのいずれか
- I2Cのスレーブ・アドレス;Adr0、Adr1ピンをそれぞれ、フロート、Low、Highに設定することで、0x68から0x6Fまで設定できる(7ビット)。Adr0、Adr1の両ピンをフロート、もしくは両ピンをLowにすると、0x68
●コンフィギュレーション・レジスタ
プログラムの最初に、コンフィギュレーション・レジスタを設定します。
- bit7 /RDY。'1'で変換開始
- bit6、bi5 チャネル・セレクト。00;チャネル1(デフォルト)、01;チャネル2、10;チャネル3、11;チャネル4
- bit4 変換モード。1;ワンショット、0;連続
- bit3、bit2 サンプルレート。11;3.75 SPS (18ビット)、10;15 SPS (16ビット)、01;60 SPS (14ビット)、00;240 SPS (12ビット)
- bit1、bit0 PGA。00 = 1倍(デフォルト)、01 = 2倍、10 = 4倍、11 = 8倍
例;チャネル1、16ビット、アンプの増幅度1;10011000 チャネル1、18ビット、アンプの増幅度1;10011100 |
●読みだしたコードの順番
最上位ビットが符号です。
- 18ビット;3バイト、MMMMMMD17D16 (1st data byte) - D15 ~ D8 (2nd data byte) - D7 ~ D0 (3rd data byte) 。Mは最上位ビットがコピーされる
- 16ビット;2バイト、 D15 ~ D8 (1st data byte) - D7 ~ D0 (2nd data byte)
- 14ビット;MMD13D ~ D8 (1st data byte) - D7 ~ D0 (2nd data byte)
- 12ビット;MMMMD11 ~ D8 (1st data byte) - D7 ~ D0 (2nd data byte)
データシートによれば、データのあとに2バイトのConfiguration byteが送られていますが、実測しても見つかりません。
●プログラム例 16ビット
2017年8月現在、RASPBIAN JESSIE WITH DESKTOPのカーネルを確認します。
uname -a
4.9.40
インストールされているPythonのバージョンを調べます。
python -V
2.7.9
インストールされているPython用I2Cライブラリを調べます。smbusはintelの呼び名で、Philipsの呼び名はI2Cです。
dpkg -l
python-smbus 3.1.1+svn-2
i2c-toolsも入っています。最初は、このツールを使います。ラズパイとMCP3424をI2Cで接続します。
利用したボードは現在入手できませんが、同等品は、ABElectronicsのADC Differential Piと思われます。
Raspberry Piは、最初に、I2Cを利用する設定が必要です。メニューのPreferencesから、Raspberry Pi Configurationを開きます。
I2CのEnabledにチェックを入れ、再起動します。
ターミナルから、i2cdetectコマンドで、スレーブ・アドレスを確認します。A-D変換基板上には2個のMCP3424が搭載されています。
i2cdetect -y 1
swap16関数は、read_word_data()で読んだデータの上位と下位バイトが逆に収納されているので、入れ替えます。sign16関数は、読み出したデータが2の補数形式のため、符号付き16ビットを10進数に変換します。差動入力の状態で入力信号を入れると、負の電圧も正常に計算します。
#!/usr/bin/env python
import smbus
import time
i2c = smbus.SMBus(1)
addr=0x68
Vref=2.048
def swap16(x):
return (((x << 8) & 0xFF00) |
((x >> 8) & 0x00FF))
def sign16(x):
return ( -(x & 0b1000000000000000) |
(x & 0b0111111111111111) )
#main
while 1:
i2c.write_byte(addr, 0b10011000) #16bit
time.sleep(0.2)
data = i2c.read_word_data(addr,0x00)
raw = swap16(int(hex(data),16))
raw_s = sign16(int(hex(raw),16))
volts = round((Vref * raw_s / 32767),5)
print (str(volts) +"V")
time.sleep(1)
◆実行例
プログラムをmcp3424-16.pyとして保存します。sudo chmod 755 mcp3424-16.py で実行権を付けます。python mcp3424-16.py で実行します。
電圧発生器(Takedariken TR6142)にA-Dコンバータの入力につなぎ、電圧を変化させます。
18ビットの実行結果です。
ここに掲載したプログラムは動作検証用です。エラー処理や、精度は考慮していません。
●プログラム例 18ビット
18ビットのデータは3バイトになるので、read_i2c_block_data()を用いました。データは上位バイトから読み出されるので、read_word_data()のように逆順にする必要はありません。
#!/usr/bin/env python
import smbus
import time
i2c = smbus.SMBus(1)
addr=0x68
Vref=2.048
config = 0b10011100 #18bit
i2c.write_byte(addr, config)
time.sleep(0.2)
def sign18(x):
return ( -(x & 0b100000000000000000) |
(x & 0b011111111111111111) )
while 1:
data = i2c.read_i2c_block_data(addr, config, 3) #18bit
raw = ((data[0] & 0b00000011) << 16) | (data[1] << 8) | (data[2])
raw_s = sign18(int(hex(raw),16))
volts = round((Vref * raw_s / 131071),7)
print (str(volts) +"V")
time.sleep(1)
(2017/08/10) 訂正;16ビット用のプログラムで、 「i2c.write_byte(addr, 0b100011000) #16bit」0が一つ多かったので削除。18ビット用も同様。「config = 0b100011100 #18bit」