A-Dコンバータ その3 10ビットSPI MCP3008 -(2)

 前回、A-DコンバータMCP3008の任意のチャネルを読み出すスケッチを作りました。それを改良して、全チャネルを読み出します。変更はloop()の部分です。

#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();
}

int adcRead(byte ch) { // 0 .. 7
byte channelData = (ch+8 ) <<4;
SPI.beginTransaction(settings);
digitalWrite(SS, LOW);
SPI.transfer(0b00000001); // Start bit 1
byte highByte = SPI.transfer(channelData); // singleEnd
byte lowByte = SPI.transfer(0x00); // dummy
digitalWrite(SS, HIGH);
SPI.endTransaction();
return ((highByte & 0x03) << 8) + lowByte ;
}
void loop(){
for (byte channel = 0 ; channel <8 ; channel++ ) {
unsigned int dataCh = adcRead(channel) ;
float volts = dataCh*Vref /1024 ;
Serial.println("CH" + String(channel) +": " + String(volts,3) + "V") ;
}
delay(100);
}

 コンソールの出力です。

 波形です。6個の変換は連続して行われるのですが、7個目と8個目の変換間隔があきます。マスタかスレーブのどちらに原因があるかは不明です。コンソールに表示するのに時間差は出ません。

データ転送速度

 MCP3008のデータ変換は200kspsです。データシートに書かれているSPIバスのデータ転送速度の上限は3.6MHzです。いままで1MHzでテストをしていました。速度を上げてみます。3バイトで1回のデータ転送が終わります。SS端子のHIGH-LOWまでの期間はその5割増しだとすると、5バイト=40ビットと仮定して、1ビットのクロックが3.6MHzならば、クロックの幅は280ns、40倍で11usが1回のデータ転送時間です。

 データ変換200kspsは、5usで変換が終わることになります。したがって、データ転送が11usかかるなら、いつも最新のデータが得られることになります。

 クロックを3.6MHzに変更しました。コンソールに出力されるデータを見ると問題ないようです。実測したクロックは500nsでした。1回の転送時間は約20usで、先ほどの試算の倍の時間です。

SPISettings settings( 1000000 , MSBFIRST , SPI_MODE0 );

で設定する周波数は、実際には2分周されているのかもしれません。ATMega328の転送クロックには、CPUクロックの1/4以下(倍速クロックを選んだときは1/2と思われる)しか動作が保証されていないので、周波数の上限には限界があります。

 クロックを5MHz8MHzと上げていきましたが、読み出したデータは正常のようです。MCP3008もデータが間に合って出力しているようですが、データシートの上限を超えているので、温度を上げてみたり十分なテストが必要です。16MHzでは、時々正常な値が読み出せる状態なので使えません。

16チャネルに拡張

 MCP3008を2個使って16チャネルに拡張します。SPIバスは共通に配線できますが、SS端子はデバイスごとに用意します。SS信号はArduinoのSPIライブラリでは指定されたピン番号はありません。1個目は10番でしたので、2個目は9番を使うことにします。

スケッチ

 SS端子に10番を使ったときは、OUTPUTの指定をした後何もせずにHIGHになっていたようです。9番を二つ目のSS端子としてOUTPUTの指定した後何もしないと、読み出したデータが正常ではありませんでした。なので、setup()内で、どちらもHIGHに指定しました。

#include <SPI.h>
#define SS0 9
#define SS1 10
float Vref = 5.0 ;

SPISettings settings( 1000000 , MSBFIRST , SPI_MODE0 );

void setup() {
pinMode(SS0, OUTPUT);digitalWrite(SS0, HIGH);
pinMode(SS1, OUTPUT);digitalWrite(SS1, HIGH);
Serial.begin(9600);
SPI.begin();
}

int adcRead(byte ch) { // 0 .. 15
byte channelData ;
byte CS ;
if ( ch > 7 ) {
channelData = (ch ) <<4;
CS = SS1 ;
} else {
channelData = (ch+8 ) <<4;
CS = SS0 ;
}
SPI.beginTransaction(settings);
digitalWrite(CS, LOW);
SPI.transfer(0b00000001); // Start bit 1
byte highByte = SPI.transfer(channelData); // singleEnd
byte lowByte = SPI.transfer(0x00); // dummy
digitalWrite(CS, HIGH);
SPI.endTransaction();
return ((highByte & 0x03) << 8) + lowByte ;
}

void loop(){
for (byte channel = 0 ; channel <16 ; channel++ ) {
unsigned int dataCh = adcRead(channel) ;
float volts = dataCh*Vref /1024 ;
Serial.println("CH" + String(channel) +": " + String(volts,3) + "V") ;
}
delay(100);
}

 実行中の波形です。線が太いところは短時間に2回の読み取りができています。ほとんどがある程度の一定期間をとったインターバルで読み取っています。

 スケッチのadcRead()関数を8から16チャネル対応に変更しました。呼び出す側は、チャネル番号0~16を入れて値を読み出します。

前へ

A-Dコンバータ その2 10ビットSPI MCP3008 -(1)

次へ

A-Dコンバータ その4 12ビットSPI MCP3208-(1)