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回以前を参考にしてください。
入力チャネルの指定は、表の左の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はダミーなので、0でも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は、できるだけ正確にテスタで測った値を記入しておきます。
/**
* 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につないでいます。