A-Dコンバータ その1 10ビットSPI MCP3002
10ビットA-DコンバータはArduino UNOに内蔵されています。ここでは、外部に10ビットA-DコンバータのMCP3002をつなぎます。MCP3xxxはマイクロチップのA-Dコンバータ・シリーズです。内部に基準電源を搭載していないモデルは、通常電源電圧を利用します。端子に基準電源Vrefがあるモデルでは、安定な基準電圧ICをつなぎます。
型番 | ビット数 | チャネル数 | 基準電圧[V] | 変換速度[sps] | インターフェース |
---|---|---|---|---|---|
MCP3001 | 10 | 差動1 | - | 200k | SPI |
MCP3002 | 10 | 2もしくは差動1 | - | 200k | SPI |
MCP3004 | 10 | 4 | - | 200k | SPI |
MCP3008 | 10 | 8 | - | 200k | SPI |
MCP3021 | 10 | 1 | - | 22.3k | I2C |
MCP3201 | 12 | 差動1 | - | 100k | SPI |
MCP3202 | 12 | 2 | - | 100k | SPI |
MCP3204 | 12 | 4 | - | 100k | SPI |
MCP3208 | 12 | 8 | - | 100k | SPI |
MCP3221 | 12 | 1 | - | 22.3k | I2C |
MCP3301 | 13 | 差動1 | - | 100k | SPI |
MCP3302 | 13 | 4 | - | 100k | SPI |
MCP3304 | 13 | 8 | - | 100k | SPI |
MCP3421 | 18 | 差動1 | 2.048 | 3.75 | I2C |
MCP3422 | 18 | 差動2 | 2.048 | 3.75 | I2C |
MCP3423 | 18 | 差動2 | 2.048 | 3.75 | I2C |
MCP3424 | 18 | 差動4 | 2.048 | 3.75 | I2C |
MCP3425 | 16 | 差動1 | 2.048 | 15 | I2C |
MCP3426 | 16 | 2 | 2.048 | 15 | I2C |
MCP3427 | 16 | 2 | 2.048 | 15 | I2C |
MCP3428 | 16 | 4 | 2.048 | 15 | I2C |
MCP3550 | 22 | 差動1 | - | 12.5/15 | SPI |
MCP3551 | 22 | 差動1 | - | 13.75 | SPI |
MCP3553 | 22 | 差動1 | - | 60 | SPI |
●マイコンのSPIインターフェース
SPIではマイコン側がマスタ、センサなど(ここではA-Dコンバータ)がスレーブという役割名を使います。
組み込み用マイコンでは、なにもかもCPUが仕事をするのではなく、周辺モジュールが搭載され効率のよい処理を行います。SPIバスの通信は、Arduino UNOで使われているATMega328ではSPI(Serial Peripheral Interface)モジュールが担当します。次のブロックから構成されています。
- クロック作成部
- 8ビットのシフト・レジスタ
- 制御部、フラグ
マスタがSPIのデータを送るときにスタート信号として使われるSS信号はこのモジュールにはないので、任意のI/Oポートを利用してソフトウェアでタイミングを作成します。通常のプログラムではマスタがスレーブにコマンドを送って、データを要求します。そのため、SS信号はマスタが出力レベルを変更します。ATMega328自体は入力にも対応できます。
8ビット・レジスタを扱うので、データは8ビット単位です。
SPIのクロックは、マイコンのクロックを分周して作成されます。分周というのは割り算することです。Arduino UNOは16MHzのクロックで動いているので、÷2であれば、SCKは8MHzです。分周比は2、4、8、16、32、64、128が使えます。
データの送る順番に二通りあります。データの先頭(MSB)から、もしくはしっぽ(LSB)から送ることを指定できます。
データが確定する(読み取る)タイミングが4パターンあります。マスタは、クロック信号が立ち上がったときもしくは立ち下がったときのどちらかのタイミングでデータを読み込みます。クロックには正論理と負論理があります。
●MCP3002のデータ出力のタイミング
次の図は、MSBからデータをマスタへ送るときのフォーマットです。マスタからは、Startビット=1、SGL/DIFFとODD/SIGNでシングル入力か疑似差動入力かを選択し、MSBFビットを1にすれば、MSBファーストでデータを送ってくるのをDinで受けます。8ビット単位になるように先頭に0を追加します。マスタからのMSBF信号をもらったのち、A-D変換したデータをその順番でスレーブはDoutから送り出します。スレーブがデータを送っている最中も、マスタはクロックを出し続けなければなりません。スレーブ側は、マスタが送ってくるMSBF以降のデータはDon't Care=無視するよと言っているので、0でもよいし1でもかまいません。
SGL/ DIFF |
ODD / SIGN |
入力方法 | チャネル |
---|---|---|---|
0 | 0 | 疑似差動入力 | CH0→IN+ CH1→IN- |
0 | 1 | 疑似差動入力 | CH0→ IN- CH1→IN+ |
1 | 0 | シングルエンド | CH0-GND |
1 | 1 | シングルエンド | CH1-GND |
(※) MCP3xxxシリーズの差動入力IN+とIN-は、疑似差動入力(pseudo-differential mode)です。これは、回路図中のグラウンドから浮いた電圧を測るような用途には適していません。グラウンド・ループやノイズ耐性を上げるために用意されています。したがって、マイナスの電圧が測れるわけではありません。
前半の8ビットは、次のデータになります。
- シングルエンドでチャネル0のときは、01101000
- シングルエンドでチャネル1のときは、01111000
後半のスレーブからのデータを受けるとき、クロックを作るためだけなのでダミーの0000000を送ります。0以外の何を送ってもかまいません。
●接続
MCP3002のパッケージには半円、ぽっちの1番ピンを表す印がついています。1番ピンから反時計回りにピン番号が振られています。基準電圧入力は電源と同じ8ピンです。消費電流は0.5mAと大変少ないので、安定化電源ICの出力である3.3V端子をつなげられます。しかし、Arduino UNOは5Vなので、ロジック・レベルが合いません。
5V端子は、USBケーブルで電源を配給している場合、いつも同じ電圧にならないです。DCジャックから電源を取っている場合は、5Vの安定化電源ICが入るので、安定した5Vが得られます。
入力電圧範囲は、0Vから基準電圧までの範囲です。8番に3.3Vをつないだときは0~3.3V、8番に5Vをつないだときは0~5Vが入力範囲です。A-Dコンバータに何らかのセンサ出力をつなぐとき、3.3Vまでの電圧しか出力されないときは、8番ピンを3.3Vにつなぐと、分解能を最大限に生かせます。MCP3001/3004/3008はVrefがVddから独立した端子になっています。
安定化された3.3Vと5VはArduinoの製品すべてが異なった電圧が出ているので、DMMで電圧を測り、スケッチの中に記入します。
●スケッチ
SPIライブラリを使います。チップ・セレクト信号SSは任意のポートに割り付けられます。10番にしました。11番はMOSI、12番はMISO、13番はSCKです。GNDと5Vの電源も配線します。
SPIのデータ転送速度は1MHz=1000000に、MSBから送ってくるようにMSBFIRST、データ・モードはMCP3002のデータシートから、正論理のクロック立ち上がりなのでSPI_MODE0を指定します。
#include <SPI.h>
#define SS 10
float Vref = 5.0 ;
SPISettings settings(1000000,MSBFIRST,SPI_MODE0);
void setup() {
pinMode(SS, OUTPUT);
Serial.begin(9600);
SPI.begin();
}
void loop(){
SPI.beginTransaction(settings);
digitalWrite(SS, LOW);
byte highByte = SPI.transfer(0b01101000); // CH0 singleEnd
byte lowByte = SPI.transfer(0x00); // dummy
digitalWrite(SS, HIGH);
SPI.endTransaction();
unsigned int dataCh0 = ((highByte & 0x03) << 8) + lowByte;
float volts = dataCh0*Vref /1024;
Serial.println("highByte= " + String(highByte,DEC) + "lowByte= " + String(lowByte,DEC) );
Serial.println("CH0 " + String(volts,3) + "V");
}
測定結果の電圧は、何桁まで表示させればよいのでしょうか。1LSBは5.0/2^10=0.00488Vです。A-Dコンバータの非直線性は±1LSBです。つまり、測定電圧は、xx.xxx±0.005が有効桁数?なので、小数点第4位を四捨五入します。実際は、温度による値の変化、DMMの読み取りの誤差、5Vを作っている安定化電源の温度特性など、誤差の要因はたくさんあるので、もっと有効桁数は少ないかもしれません。
実行結果です。CH0には、電圧発生器の1.0000Vを入力しています。
●データ転送の波形を観測
最初に、クロックとMISO(Dout)を見ます。Doutはオシロスコープのシリアル・デコード機能でデータの値を表示させます。204もしくは205ですから、スケッチの実行結果と一致します。
クロックの周波数は指定通り約1MHzです。
4信号を同時に見ます。SPI.endTransaction()のあとにMISOとMOSIがLOWになっていません。ハイ・インピーダンスになったので、電圧が残っているものと思われます。
●コラム Serial.print
改行が必要なときは、Serial.println もしくはSerial.print(\n)を使います。文字と数値を混在するときは次のように書けます。
Serial.print("電圧 = ");Serial.print(volts);Serial.print("V");
Serial.println("電圧 = "+String(volts)+"V");
小数点第2位までの電圧であれば、
Serial.println("電圧 = "+String(volts,2)+"V");