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で使う

 マイクロチップのMCP342412ビットから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データ転送速度;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」