5ドル!ラズパイ・ゼロ(Raspberry pi Zero)でIoT (33) A-Dコンバータの利用8 AD7991

4チャネル12ビットA-Dコンバータ

 AD7991は、アナログ・デバイセズのI2Cインターフェースをもつ12ビットA-Dコンバータです。DIP化ボードがマルツエレックから入手できます。基準電圧Vrefは電源Vddを用いる方法と専用外部端子の2種類が選べます。

 ボード上にはI2C信号のプルアップ抵抗2.2kΩが用意されていてデフォルトではつながっているので、ジャンパをカッタ・ナイフの刃先で切って使います。電源VddとGND間には0.1uFのコンデンサが入っています。

●AD7991の主なスペック

  •  ビット数;12
  •  チャネル数;4もしくは3チャネル
  •  基準電圧;電源端子と共通もしくはVin3端子がVrefとして利用できる
  •  電源電圧; 2.7~5.5V
  •  消費電流;動作時90uA(3.3V時)
  •  入力;シングルエンド
  •  変換方式;逐次比較型
  •  変換時間;1us
  •  インターフェース;I2C
  •  I2C転送速度;最大3.4MHz
  •  スレーブ・アドレス;0x28もしくは0x29

 ピン配置です。モジュールの基板上にはSDAやSCLのシルク印刷はありません。

AD7991 ラズパイのGPIO 接続先
Vin0 1番ピン 8 Vdd 1番 3.3V
Vin1 2 7 Vin3/Vref -
Vin2 3 6 SCL 5番 SCL
GND 4 5 SDA 3番 SDA

  ※AD7991の4番ピンGNDはラズパイの9番もしくは6番ピンへ接続。

基準電圧

 12ビットA-Dコンバータの基準電圧を3.3Vとすると、1 LSBは、次の値になります。

3.3/2^12 = 0.8mV

 ラズパイの3.3VやGNDは常に変動しています。したがって、基準電圧に電源電圧を使うより基準電圧発生ICを使ったほうが、変動や温度変化による誤差は減らせます。ポピュラで安価なTL431を使うのが簡単です。こちらやこちらの記事を参照ください。

配線

 ブレッドボード上では、3.3V-GNDに、0.1uFの積層セラミック・コンデンサと22uFの電解コンデンサを入れています。

 配線が終わったらラズパイの電源を入れます。確認のためにターミナルで i2cdetect -y 1 を実行すると0x28でした。

レジスタ

◆設定レジスタ(アドレスはない)

D7 D6 D5 D4 D3 D2 D1 D0
Ch3 Ch2 Ch1 Ch0 Vref選択。1:Ch3端子はVref入力になる I2Cのフィルタ。0:有効 データ変換中にI2Cのやり取りを挿せないようにして変換精度を上げるために使われる、サンプル遅延とビット判定遅延。0で有効

  パワーオン・リセット時には 0xf0に設定されている。利用する入力チャネルは一つだけしてもよいし、複数を指定してもよい。複数を指定すると順番に変換が行われる。

◆変換結果レジスタ(アドレスはない)

 上位バイト。チャンネル識別子の読み取り内容は、チャネル0=0、チャネル1=1、チャネル2=2、チャネル3=3。

D15 D14 D13 D12 D11 D10 D9 D8
0 0 チャンネル識別子 MSB b10 b9 b8

 下位バイト。データ自体は符号なしの12ビット。

 D7 D6  D5  D4  D3  D2  D1  D0 
 b7 b6 b5  b4  b3  b2  b1  b0 

プログラム

 基準電源に電源端子の電圧を使うプログラムです。 Vrefの電圧は6桁半のDMMでラズパイGPIOの1番ピンを測定しました。設定レジスタには、デフォルトと同じパラメータを書き込んでいます。四つのチャネルを全部計測します。 

 AD7991は常時スリープしており、マスタのラズパイからデータを読み出す最初の1バイト目が送られることで起き出し、変換を行ってデータを用意します。ラズパイが、最初のチャネル0のデータ2バイトを読み出した後9バイト目がACKであれば、次のチャネルのデータの変換が始まり、データが用意されます。

 read_word()で2バイトを読み出すと、最後はNACKなので、次の変換が始まりません。もう一度read_word()を実行しても、最初のチャネルしか読み出せません。したがって、4チャネルを読み出すためにsmbusライブラリのブロック読み出しを使いました。ライブラリの記述上最初にconfigの1バイトを送っていますが、AD7991は無視してくれるようです。

#!/usr/bin/env python
import smbus
import time
i2c = smbus.SMBus(1)
addr=0x28
Vref=3.28
config = 0b11110000
i2c.write_byte(addr, config)

#main
while 1:
data = i2c.read_i2c_block_data(addr,config,8)
ch0 = (data[0]<<8 | data[1])
ch1 = ((data[2] & 0x0f)<<8 | data[3])
ch2 = ((data[4] & 0x0f)<<8 | data[5])
ch3 = ((data[6] & 0x0f)<<8 | data[7])

volt_ch0 = round((Vref * ch0 / 4096),4)
volt_ch1 = round((Vref * ch1 / 4096),4)
volt_ch2 = round((Vref * ch2 / 4096),4)
volt_ch3 = round((Vref * ch3 / 4096),4)
print (volt_ch0,volt_ch1,volt_ch2,volt_ch3)
time.sleep(1)


(2020/05/09)4095は間違いなので、4096に変更した。下記のスケッチも修正したが、実行結果は変更していない

 すべてのチャネルをVddにつないだときのデータです。4チャネルとも同じデータを読み出していますが、上位にチャネル識別コードがあるので、値は異なります。

実行結果

 電圧発生器で小数点第4位まで出力した値を測っています。1.0000V出力時に、DMMではVin0の入力端子を測ると0.999902Vでした。最初に計算したように、LSBは0.8mVです

 I2Cの転送速度は、ラズパイ・ゼロでは1MHzまでは0.1秒ごとの連続読み出しでは正常でした。3.4MHzは1秒ごとの読み出しではほぼ正常でしたが、0.1秒ごとではデータの誤読やI/O ERRORが起こりました。

※執筆時点;2017-11-29版をダウンロードし、sudo apt-get update と sudo apt-get upgrade -y および sudo rpi-update で更新し、カーネルは、uname -a で確認。4.9.66でした。

※プログラムを仮にad7991.pyと/home/piに保存すると、sudo chmod 755 ad7991.py で実行権を付け、ターミナルから、python ad7991.pyで実行します。I2CやSPIのグループにpiユーザが属しているので、sudoは不要です。
 プログラム・リストは、表示の関係でTabキーが無視されるので、スペースに代えてあります。また、リスト中を2回クリックすると全選択になるので、CTRL-Cでコピーし、テキスト・エディタにCTRL-Vで貼り付けて利用してください。ラズパイに持っていくと、リターン・コードなどが化けていることがあるので、一度消して、ラズパイのテキスト・エディタで改行してください。

※I2Cの有効化は、この説明を参照ください。1-Wireと同じく、I2CやSPIもEnableにチェックを入れています。

※電圧発生器はアドバンテストTR6142、DMMはケースレー2000、シリアル・デコードはPicoScope 5242Bを使いました。