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

SPIインターフェースの12ビット8チャネルA-DコンバータMCP3208

 前回まではI2CインターフェースのA-Dコンバータを利用しました。ここからはSPIを利用します。I2Cのデータ転送速度は100/400kHzおよび3.4MHzです(ラズパイは任意の速度を設定できる)。それに引き換え、SPIは数MHz以上(電源電圧に依存)と高速なので、メーカの製品リストを見ると、変換速度の速いA-Dコンバータに採用されています。I2Cは遅いが故、低消費電力を売りにしていることが多いようです。

 ラズパイでは、それほど高速にデータを取り込むことを考慮していません。それは、グラウンドや電源、信号を注意深く設計したプリント基板でないと、正しくデータを取り込めないからです。

 8チャネル入力のMCP3208以外に、チャネル数の少ないMCP3201(1チャネル)/3202(2チャネル)/3204(4チャネル)があります。形状はDIP(ディップ)と呼ばれ、ブレッドボードに挿しての利用が簡単です。

 1チャネル入力のMCP3201はチャネルを指定せずにデータを読み出せるので、ここで利用しているMCP3208とはタイミングが異なります。2チャネル入力のMCP3202はチャネルの指定方法がMCP3208とは異なります。4チャネル入力のMCP3204はMCP3208と同じタイミングで利用できます。

 変換方式はSAR(逐次比較)で、比較的高速です。I2CのA-DコンバータはΔΣ方式が多いです。

SPIの信号線は3本

 I2Cは、クロックSCLとデータSDAの2本の信号線でA-D変換したデータを送りました。SPIはMOSI、MISO、SCLK3本です。現実には、デバイスを選択するために、チップ・セレクト信号が必要です。I2Cのチップ・セレクトはアドレスを送ることで識別していましたが、SPIでは物理的信号線が必要です。

 ラズパイのGPIOにはCE0とCE1の2本のチップ・セレクト信号が出ているので、二つのデバイスを同時に利用できます。I2Cはスレーブ・アドレスが異なれば数個から数十個をつなげられるのに比べると、少ないかもしれません。

 SS(スレーブ・セレクト)、CS(チップ・セレクト)、CE(チップ・イネーブル)は、同じ信号で、呼び名が異なるだけです。信号の上にバー、もしくは前にスラッシュがあるのは、負論理を示しています。通常はHigh(3.3V)で、信号が有効になるときはLow(0V)です。スラッシュは省略されることがあります。

 ラズパイのマイコン自体にはSPIは複数ありますが、デフォルトでは一つだけが利用できます。また、チップ・セレクトも最大三つあるようですが、使っているのを見かけません。

MCP3208信号名 ラズパイのGPIO  
チップ・セレクト/CS(/SS) CE0が24番、CE1が26番 Chip Enable
 Din 19番 データ出力MOSI Master Out Slave In
 Dout 21番 データ入力MISO Master In Slave Out
 CLK 23番 クロックSCLK Serial CLocK
電源3.3V 17番が近い  
GND 25番が近い  

MCP3208の主なスペック

  •  ビット数;12
  •  基準電圧;外部から入力
  •  電源電圧; 2.7~5.5V
  •  入力;シングルエンド、疑似差動
  •  変換方式;SAR(逐次比較)方式
  •  サンプリング・レート;100ksps
  •  インターフェース;SPI

SPIをPythonで利用するには

 ラズパイ用に、spidevという名称のライブラリが3種類かそれ以上あるようです。パラメータが微妙に異なり、どれが標準的に使われるかがはっきりしません。

   https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md

によると、WiringpiのSPIを使うように説明しているように読み取れます。OSのカーネルが4.4から4.9に上がったころ(2017年4月)からトラブルが多かったので、今回は使いません。

  ここでは、2017/07/02バージョンに入っているgpiozeroを利用します。2017年8月現在、各種ソフトウェアのバージョンを確認します。

uname -a
4.9.41
python -V
2.7.9
pip gpiozero
1.4.0

 インストールされているPythonのプログラムを表示するpip listを実行すると、pigpiospidev 3.0などもインストールされていて、使えそうです。

 gpiozeroを使う理由は、A-DコンバータMCP3xxx(MCP3001/3002/3004/3008、MCP3201/3202/3204/3208、MCP3301/3302/3304)をサポートしているからです。

 I2Cと同様に、SPIの利用は、次のように設定します。メニューのPreferencesから、Raspberry Pi Configurationを開きます。

 SPIのEnabledをチェックし、リブートします。この設定は一度行えば、次回からは、SPIが利用できます。しかし、大きなバージョンアップがあると、この設定が消えることがあるので、確認します。

ピン配置

 MCP3208のピン配置は、同社の10ビットA-DコンバータMCP3008と同じです。ICの半円形にへこんでいたり、小さなぽっちがあるところを目印に、1番ピンを知ります。反時計回りで、ピン番号が振られています。1番ピン側が入力端子です。

CH0 1番ピン 16 Vdd 3.3V電源
CH1 2 15 Vref 基準電圧入力
CH2 3 14 AGND アナログ・グラウンド
CH3 4 13 CLK
CH4 5 12 Dout
CH5 6 11 Din
CH6 7 10 /CS・SHDN
CH7 8 9 DGND ディジタル・グラウンド

配線にはブレッドボードを利用

 ブレッドボードは、動作確認をするときに使われる部品を指して配線ができる便利な試作ボードです。本来ソルダーレス・ブレッドボードという名称ですが、省略して呼ばれます。様々な形状の製品が入手できます。形状がコンパクトだと部品が載りませんが、大きすぎると取り回しが面倒です。

 よく使われるのが、上記の写真の形状の製品です。秋月電子通商にもEIC-801BB-801がありますが、どちらでもかまいません。左右に電源用のラインが2列ずつあります。中央は溝で区切られていて、左右のブロックはつながっていません。同じ会社の製品は連結して使える場合が多いです。

 DIPの形状のICは、溝をまたぐように挿し込んで使います。購入直後のDIPの足(リード)は、開き気味です。そのまま挿そうとしても失敗します。失敗すると、曲がったリードを直すのが面倒です。片側のリードを挿し込んで、少し力を入れて外側にずらしながら、反対側のリード全部がブレッドボードの穴に引っかかることを確かめ、DIP自体を押し込みます。

 接続は、0.3~0.5Φの単線もしくはジャンパ線という名称の10cmもしくは15cm程度の色分けされたケーブルを使います。

 ラズパイのGPIOはオスのピンヘッダで、ブレッドボード側は挿し込むので、ジャンパ線はオス-メス・タイプを入手します。ブレッドボード上ではオス-オス・タイプを使います。

 配線のセオリは、電源の3.3Vは赤色GNDは黒色、信号線は色を使い分けることです。これにより、トラブルが少なくなります。人は必ず勘違いをしますから、色によってチェックできるようにします。

 入力は、CHxとAGNDのピンにつなぎます。AGNDとDGNDは電源のラズパイのGNDにつなぎます。ラズパイのGNDは複数の端子があります。信号線に近いGNDを利用します。

 電源には、0.1uF、16V耐圧の積層セラミック・コンデンサ、22uF前後、16V耐圧の電解コンデンサを取り付けています。動作確認だけなら、なくても動きます。コンデンサがあるほうが、正しいA-D変換を期待できます。

 ラズパイとMCP3208の配線は、3.3VとGNDをブレッドボードの電源ラインにつなぎます。SPIの3本の信号MOSI、MISO、SCLKをDin、Dout、CLKにつなぎます。チップ・セレクトのCE0をCSにつなぎます。

 MCP3208のVddとVrefを電源ラインのプラス(赤色)へつなぎ、DGNDとAGNDを電源ラインのマイナス(青色)につなぎます。

プログラム例 ライブラリはgpiozero

 A-D変換時に基準となる電圧はVref端子から入力します。簡易的に電源の3.3Vをつなぎますが、電圧の精度、温度ドリフトなどが欠点になります。したがって、12ビットのディジタル・データは得られますが、12ビットの精度は得られません。

 プログラムの中では、DMM(Keithley 2000)でA-D変換をしていないときに測定した電圧を変数Vrefに代入しています。ラズパイが動作すると、3.3Vは常に変化します。ラズパイの基板上にはUSBの5Vから安定化電源ICを使って3.3Vを作っていますが、10ビットや12ビットのA-Dコンバータを使う上では、変動する3.3Vといえます。

 温度に対して安定な出力を得るには、基準電圧発生ICを利用し、その出力をVref端子につなぎます。使用例はこちらのページを参照してください。

 実験用に、基準電圧発生器の出力をCH0(1番ピン)につなぎ、次のプログラムを動かします。

#!/usr/bin/env python
from gpiozero import MCP3208
Vref = 3.29476
pot = MCP3208(channel=0)
print(str(pot.value * Vref) + "V")

 実行結果です。

プログラム例 ライブラリはspidev

 SPIはI2Cと同様にシリアル通信です。データの読み出しは、MCP3208に「スタート、入力チャネル番号」をDinへ送った1.5クロック後からDoutから12ビットのデータが送られてきます。SPIは、MOSIからDin、DoutからMISOと数珠つなぎになって、シフトレジスタのように、データがクロックに合わせてシフトして出てくるようなイメージの動作をします。

 したがって、チャネル設定が3ビットしか必要がなくとも、ここでは2バイトのデータがスレーブのMCP3208から転送されてくるまでクロックが必要なので、ダミーデータを送ります。MCP3208はダミーデータを無視します。

 チップ・セレクトのCE0(CS)はリード/ライトの一連の動作中はLowです。何もデータが送られていないときは、Highです。ラズパイのGPIOの多くは、初期設定後Lowになっている場合がほとんどですが、CE0とCE1はHighです。

 マスタのラズパイは、スレーブのMCP3208へ設定データを送ります。スタートは'1'、シングルエンド入力は'1'、チャネル0はD2、D1、D0='000'なので、b11000です。D0のあとのビットはダミーなので'0'とするとb11000000=0xc0です。spidevでは、8ビット=1バイト単位でデータを送るので、データを送り終わるまでの合計は3バイトです。24ビット分になるように先頭に'0'を五つ補完したのが下記のタイミングです。チャネル0のときの最初のバイトはb00000110=0x06です。

 プログラムです。mcp3208.pyという名前で保存し、sudo chmod 755 mcp3208.py で実行権を付け、python mcp3208.pyで実行します。 

 チャネルの指定が泣き別れになっているので、ブロック転送関数xfer2()の最初の2バイトは次の値になります。3バイト目はダミーの0x00のままです。

チャネル 1st byte 2nd byte
0 0x06 0x00
1 0x06 0x40
2 0x06 0x80
3 0x06 0xc0
4 0x07 0x00
5 0x07 0x40
6 0x07 0x80
7 0x07 0xc0

#!/usr/bin/env python
# channel 0 pin1-GND
import spidev
Vref = 3.29476
spi = spidev.SpiDev()
spi.open(0,0) #port 0,cs 0

adc = spi.xfer2([0x06,0x00,0x00])
data = ((adc[1] & 0x0f) << 8) | adc[2]
print (str(Vref*data/4096) + "V")
spi.close()


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

(※)2018年後半から、speedの指定が必須になっています。open後に記述します。
spi.open(0, 0) # port 0,cs 0
spi.max_speed_hz = 1000000 # 1MHz

 実行結果です。電圧の表示は計算上の桁数であり、有効桁数で丸めていません。

 オシロスコープPicoScopeで、実際に入力電圧1.0V時のクロック(青色)とMISOデータ(赤色)の受信信号を見ます。期待通りに動いていることがわかります。

疑似差動入力

 MCP3xxxシリーズの差動入力IN+IN-は、疑似差動入力(pseudo-differential mode)です。これは、回路図中のグラウンドから浮いた電圧を測るような用途には適していません。グラウンド・ループやノイズ耐性を上げるために用意されています。したがって、マイナスの電圧が測れるわけではありません。

 参考;http://digital.ni.com/public.nsf/allkb/649202233E3EF06286257408002FDDBD

    http://www.tij.co.jp/lsds/ti_ja/general/maruwakari/16_sar_adc.page