2017年6月9日金曜日

SPI ADCのMCP3208をArduinoとNucleo(mbed)で使ってみる。

SPI接続のA/DコンバーターのMCP3208をArduino UnoとNucleo F401RE(mbed)で使ってみた。

Arduino Uno


キム茶工房さんの記事を参考にさせていただきました。配線、スケッチ等そのままで特に問題なく動作。


気になった点

SPI通信は普通8bitごとなのに対し、MCP3208のデータシートによるとデータ構造が一見8bitごとになっていない。また、SPI通信は全二重(MISOとMOSIで送受信を同時に行う)

ちゃんと考える必要ありそうだったが、データシートにSPIでの使い方が説明されていた。


上図の下部のように、8bit×3のSPI送受信で1ch分のA/Dコンバーターの読み取り値を取得できる。

Nucleo F401RE


Coretex-M4で84MHz駆動のNucleo F401REで実験した。

mbedのライブラリが公開されていたので使わせていただきました。https://developer.mbed.org/users/Kemp/code/mcp3208/

ブレッドボード配線図


MCP3208を2個使って、合計16ch使えるようにしてみた。SPIのSCK、MOSI、MISOは共通バス、CSはそれぞれのデバイスに割り当てる。

<main.cpp>

#include "mbed.h"
#include "mcp3208.h"

int main()
{
    SPI spiM(SPI_MOSI, SPI_MISO, SPI_SCK);
    spiM.frequency(2000000);
    MCP3208 mcp3208_0(spiM, D10);
    MCP3208 mcp3208_1(spiM, D9);
    
    DigitalOut checkPin(D2);
    
    float v0[8];
    float v1[8];

    for (;;) {
        for (int i = 0; i < 8; i++) {
            checkPin = 1;
            v0[i] = mcp3208_0.read_input(i);
            checkPin = 0;
            wait_us(1);
        }        
        for (int i = 0; i < 8; i++) {
            checkPin = 1;
            v1[i] = mcp3208_1.read_input(i);
            checkPin = 0;
            wait_us(1);
        }
        
        printf("Device0\t");
        for (int i = 0; i < 8; i++) {
            printf("%.3f\t", v0[i]);
        }
        printf("\r\n");
        printf("Device1\t");
        for (int i = 0; i < 8; i++) {
            printf("%.3f\t", v1[i]);
        }
        printf("\r\n");
        
        wait(0.2);
    }
}

mbed Repository:
https://developer.mbed.org/users/ryood/code/Nucleo_MCP3208_Test/

MCP3208のSPIクロックは5V駆動で最大2MHz、2.7V駆動で1MHzとなっているが、3.3V駆動させて

spiM.frequency(2000000);

と、2MHzのクロックを指定してみたが、動作はするようだ。

DigitalOutにcheckPinを割り当ててSPI経由の読み取り時間を測定してみた。

SPI通信のようす


SCK、MOSI

ch1:MOSI ch2:SCK

SCK、MISO

ch1:MISO ch2:SCK

クロック速度は1.3MHz程度。MCP3208からの出力のMISOはLのときに変動がある。PullDownしておいたほうがいいかもしれない。また、出力波形の立ち上がり/立ち下がりも少し遅いようだ。

処理時間

ch1:checkPin ch2:SCK

1ch分、8bitx3の処理速度を測ると40us程度かかるようだ。周波数にすると25kHz程度なので高速動作させる場合はちゃんと考えておく必要がありそうだ。

Tickerのタイマー割り込みと同時に使ってみる。


96kHzの割り込み処理と同時に使うことを想定してテストしてみた。SPIクロックも4MHzにしてみた。(MCP3208の仕様外)

<main.cpp>

#include "mbed.h"
#include "mcp3208.h"

#define SAMPLING_RATE   (96000)
#define SAMPLING_PERIOD (1.0f/SAMPLING_RATE)

DigitalOut checkPin(D2);

void isr()
{
    checkPin = 1;
    wait_us(1);
    checkPin = 0;
}

int main()
{
    SPI spiM(SPI_MOSI, SPI_MISO, SPI_SCK);
    spiM.frequency(4000000);
    MCP3208 mcp3208_0(spiM, D10);
    MCP3208 mcp3208_1(spiM, D9);
   
    float v0[8];
    float v1[8];
   
    Ticker t;
    t.attach(&isr, SAMPLING_PERIOD);

    for (;;) {
        for (int i = 0; i < 8; i++) {
            v0[i] = mcp3208_0.read_input(i);
        }      
        for (int i = 0; i < 8; i++) {
            v1[i] = mcp3208_1.read_input(i);
        }
       
        printf("Device0\t");
        for (int i = 0; i < 8; i++) {
            printf("%.3f\t", v0[i]);
        }
        printf("\r\n");
        printf("Device1\t");
        for (int i = 0; i < 8; i++) {
            printf("%.3f\t", v1[i]);
        }
        printf("\r\n");
       
        wait(0.2);
    }
}

mbed Repository:
https://developer.mbed.org/users/ryood/code/Nucleo_MCP3208_Ticker_Test/

割り込みハンドラのisr()内で、checkPinをH→Lさせている。

SCK、MOSI

ch1:MOSI ch2:SCK

SCK、MISO

ch1:MISO ch2:SCK

クロック周波数は2.6MHz程度。

Ticker割り込み

ch1:checkPin ch2:SPI_SCK

Ticker割り込みが優先されてSPI通信(SCK)が遅れている様に見えるがTicker割り込みの周期が一定な確証はない。

MOSIをPullDown

MOSIを1kΩのRでPullDownしてみた。

SPI Clock 2MHz指定

ch1:MISO ch2:SCK

SPI Clock 4MHz指定

ch1:MISO ch2:SCK

メモ:

MCP3008は10bit精度になるが、5V駆動で最大3.6MHz。2.7V駆動で最大1.35MHz。