初めてのRaspberry Pi Pico ㉑ C言語 12ビットADC MCP3208

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


###MCP3208のおもなスペック

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

###接続

|MCP3208の端子|	Picoの端子(GPIO)	|名称|
|------------|:-------------:|:------|
|1 ch0|	-|	-|
|2 ch1|	-|	-|
|3 ch2|	-|	-|
|4 ch3|	-|	-|
|5 ch4|	-|	-|
|6 ch5|	-|	-|
|7 ch6|	-|	-|
|8 ch7|	-|	-|
|9 DGND|	GND|	GND|
|10 /CS|	GP5|	SPI0 CSn|
|11 Din|	GP3|	MOSI SPI0 Tx|
|12 Dout|	GP4|	MISO SPI0 RX|
|13 CLK|	GP2|	SPI0 SCK|
|14 AGND|	GND|	GND|
|15 Vref|	3V3|	3.3V|
|16 Vdd|	3V3|	3.3V|
　入力のch0には、電池駆動の簡易電源TL431の出力をつないでいます。約2.5Vです。ch1～ch7はGNDもしくは3.3Vにつなぎ、結果をみます。

###プログラムmcp3208.c
　フォルダ名はmcp3208としました。CMakeLists.txtは省略します。17回以前を参考にしてください。

　[MCP3208データシート](https://ww1.microchip.com/downloads/en/DeviceDoc/21298e.pdf)

　入力チャネルの指定は、表の左の4ビットで行います。

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

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

　本プログラムは、疑似差動入力に対応していません。

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


```cpp
/**
 * 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[0] = 0b00000110;
        writeData[1] = 0b00000000; 
      break;
      case 1:
        writeData[0] = 0b00000110;
        writeData[1] = 0b01000000; 
      break;
      case 2:
        writeData[0] = 0b00000110;
        writeData[1] = 0b10000000; 
      break;
      case 3:
        writeData[0] = 0b00000110;
        writeData[1] = 0b11000000; 
      break;
      case 4:
        writeData[0] = 0b00000111;
        writeData[1] = 0b00000000; 
      break;
      case 5:
        writeData[0] = 0b00000111;
        writeData[1] = 0b01000000; 
      break;
      case 6:
        writeData[0] = 0b00000111;
        writeData[1] = 0b10000000; 
      break;
      case 7:
        writeData[0] = 0b00000111;
        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, MCP3208 Reading raw data from registers via SPI...\n");

    setup_SPI();

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

　実行結果です。ch0はTL431出力、ch1はGND、ch2は3.3V、ch3はGND、ch4は3.3V、ch5はGND、ch6は3.3V、ch7はGNDにつないでいます。
![adc401.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/2cc5a909-2549-79d8-ea9e-92932d470ade.png)