初めてのRaspberry Pi Pico ⑳ C言語 12ビットADC MCP3204


 Picoには12ビット4チャネルのA-Dコンバータが内蔵されています。もし4チャネル分足りなかったら、という想定で外部にICを追加します。

MCP3204のおもなスペック

  • ビット数 12
  • チャネル数 4(シングルエンド)、2(疑似差動)
  • 基準電圧 内蔵なし、端子あり
  • 変換速度 100ksps(5V時)
  • インターフェース SPI(モード0,0および1,1)、クロック1.6MHz(5V時)、0.8MHz(2.7V時)
  • 動作電圧 2.7~5.5V
  • ピン数 14ピンDIP

接続

MCP3204の端子 Picoの端子(GPIO) 名称
1 ch0 - -
2 ch1 - -
3 ch2 - -
4 ch3 - -
5 NC - -
6 NC - -
7 DGND GND GND
8 /CS GP5 SPI0 CSn
9 Din GP3 MOSI SPI0 Tx
10 Dout GP4 MISO SPI0 RX
11 CLK GP2 SPI0 SCK
12 AGND GND GND
13 Vref 3V3 3.3V
14 Vdd 3V3 3.3V

 入力のch0には、電池駆動の簡易電源TL431の出力をつないでいます。約2.5Vです。ch1はGND、ch2は3.3Vの電源、ch3はGNDにつないで結果をみます。

プログラムmcp3204.c

 フォルダはmcp3204としました。CMakeLists.txtは省略します。17回以前を参考にしてください。

 MCP3204データシート

 チャネルは、シングルエンドのch0、ch1、ch2、ch3、ch0-ch1の疑似差動のIN+/IN-、ch0-ch1の疑似差動のIN-/IN+、ch2-ch3の疑似差動のIN+/IN-、ch2-ch3の疑似差動のIN-/IN+から選択します。
 チャネルの指定は、下の表の左の4ビットで行います。

Single/Diff D2 D1 D0 入力のタイプ チャネル
1 x 0 0 シングルエンド ch0
1 x 0 1 シングルエンド ch1
1 x 1 0 シングルエンド ch2
1 x 1 1 シングルエンド ch3
0 x 0 0 疑似差動 ch0=IN+、ch1=IN-
0 x 0 1 疑似差動 ch0=IN-、ch1=IN+
0 x 1 0 疑似差動 ch2=IN+、ch3=IN-
0 x 1 1 疑似差動 ch2=IN-、ch3=IN+

 コマンドを送るフォマットは次のとおりです(送るのはPicoで、送られてくるのもPicoです)。
  Startbit チャネル指定4バイト
 例えば、シングルエンドch0であれば、
  1 1x00
 シングルエンドch1であれば、
  1 1x01
 です。最後のチャネル指定ビットD0がMCP3204へ送られるタイミングで、約1ビットおいて、NULLビットに引き続き、
  D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
のA-D変換結果データが、MCP3204からPicoへ送られてきます。
 SPIは基本8ビット単位なので、LSBのD0から前にさかのぼって、8ビット単位で送るべきデータを整理します。シングルエンドch0の場合です。
  x x x x x x 1 1 x 0 0 x x x x x x x x x x x x x x
 SPIはクロックを送り続けないとデータはやってきません。上記のxはダミーなので、0でも1でもかまいません。したがって、ここでは、ch0のデータは、次のようにしました。
  0b00000110 0x00 0xff

 本プログラムは、疑似差動入力に対応していません。したがって、チャネルの指定で変化するのは2バイト目だけです。

 3バイト送ったので、読み出したデータは3バイトです。1バイト目はごみなので捨てます。2バイト目は上位桁D11 D10 D9 D8が右詰めで入っています。3バイト目は、D7 D6 D5 D4 D3 D2 D1 D0です。
 12ビットのデータなので、Vrefの3.3Vをかけ、4096で割って電圧を求めます。Vrefは、できるだけ正確にテスタで測った値を記入しておきます。

/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"

#define PIN_MISO 4
#define PIN_CS   5
#define PIN_SCK  2
#define PIN_MOSI 3

#define SPI_PORT spi0
static float Vref = 3.30;


static inline void cs_select() {
    asm volatile("nop \n nop \n nop");
    gpio_put(PIN_CS, 0);  // Active low
    asm volatile("nop \n nop \n nop");
}

static inline void cs_deselect() {
    asm volatile("nop \n nop \n nop");
    gpio_put(PIN_CS, 1);
    asm volatile("nop \n nop \n nop");
}

void setup_SPI(){
    // This example will use SPI0 at 0.5MHz.
    spi_init(SPI_PORT, 500 * 1000);
    gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
    gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
    gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);

    // Chip select is active-low, so we'll initialise it to a driven-high state
    gpio_init(PIN_CS);
    gpio_set_dir(PIN_CS, GPIO_OUT);
    gpio_put(PIN_CS, 1);
}

int readADC(uint8_t ch){
    uint8_t writeData[] = {0b00000110, 0x00, 0xff};
    switch(ch){
      case 0:
        writeData[1] = 0b00000000; 
      break;
      case 1:
        writeData[1] = 0b01000000; 
      break;
      case 2:
        writeData[1] = 0b10000000; 
      break;
      case 3:
        writeData[1] = 0b11000000; 
    }
    // printf("\n %0b %0b %0b\n",writeData[0],writeData[1],writeData[2]);
    uint8_t buffer[3];
    cs_select();
    sleep_ms(1);
    spi_write_read_blocking(SPI_PORT, writeData, buffer, 3);
    sleep_ms(1);
    cs_deselect();

    return (buffer[1] & 0x0f) << 8 | buffer[2];
}

int main() {
    stdio_init_all();

    printf("\nHello, MCP3204 Reading raw data from registers via SPI...\n");

    setup_SPI();

    for (uint8_t i=0; i<4; i++){
        printf("ch%d is %.4fV\n", i, Vref * readADC(i) / 4096);
    }
    return 0;
}

 実行結果です。ch0はTL431出力、ch1はGND、ch2は3.3V、ch3はGNDにつないでいます。