Raspberry Pi Picoでプログラミング ⑭ spi A-DコンバータMCP3208
前回、10ビットA-DコンバータMCP3008を利用しました。ここでは12ビットA-DコンバータMCP3208を使います。ビット数が増えると、より細かくアナログ電圧を扱えます。言い換えれば、量子化誤差が小さくなります。
●A-DコンバータMCP3208
Picoには12ビットA-Dコンバータが内蔵されています。ここでは、12ビットA-DコンバータMCP3208を外付けして、同じ電圧を読み込んで比較します。
●MCP3208のおもなスペック
- ビット数 12
- チャネル数 8(シングルエンド)、4(疑似差動)
- 基準電圧 内蔵なし、端子あり
- 変換速度 100ksps(5V時)
- インターフェース SPI(モード0,0および1,1)、クロック1.6MHz(5V時)、0.8MHz(2.7V時)
- 動作電圧 2.7~5.5V
- ピン数 16ピンDIP
基準電圧Vref入力、アナログ・グラウンドAGNDとディジタル・グラウンドDGNDが独立した端子で用意されています。MCP3008とピンの並びは同じです。
●接続
●ソースの作成
pico/worksフォルダにあるmcp3008フォルダをコピーしてmcp3208フォルダを作ります。
pico/worksフォルダのCMakeLists.txtの内容です。
cmake_minimum_required(VERSION 3.12)
# Pull in SDK (must be before project)
include(pico_sdk_import.cmake)
project(pico_examples C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})
# Initialize the SDK
pico_sdk_init()
# Add blink example
add_subdirectory(cmake)
add_subdirectory(blink)
add_subdirectory(serial)
add_subdirectory(clock)
add_subdirectory(i2cscanner)
add_subdirectory(lps25hb)
add_subdirectory(tmp117)
add_subdirectory(aht20)
add_subdirectory(mcp3008)
add_subdirectory(mcp3208)
pico/works/mcp3208フォルダのCMakeLists.txtの内容です。
add_executable(mcp3208
mcp3208.c
)
# Pull in our pico_stdlib which pulls in commonly used features
target_link_libraries(mcp3208 pico_stdlib
hardware_spi hardware_gpio hardware_adc)
# create map/bin/hex file etc.
pico_add_extra_outputs(mcp3208)
mcp3208.cの内容です。Vrefは岩通VOAC7602の読み取り値です。
チャネルのデータの作り方は、
A-Dコンバータ その4 12ビットSPI MCP3208 -(1)
に詳しい解説があるので参照してください。
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/gpio.h"
#include "hardware/adc.h"
#define PIN_SCK 2
#define PIN_MOSI 3
#define PIN_MISO 4
#define PIN_CS 5
#define SPI_PORT spi0
static float Vref = 3.2562;
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 2MHz.
spi_init(SPI_PORT, 2 * 1000 * 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, 0x00};
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();
adc_init();
adc_gpio_init(26);
adc_select_input(0);
const float conversion_factor = Vref / (1 << 12);
printf("\nHello, MCP3208 Reading data from registers via SPI...\n");
setup_SPI();
while (1) {
for (uint8_t i=0; i<8; i++){
printf("ch%d is %.4fV\n", i, Vref * readADC(i) / 4096);
sleep_ms(10);
}
uint16_t result = adc_read();
printf("ADC voltage: %f V\n", result * conversion_factor);
sleep_ms(5000);
}
return 0;
}
ターミナルで、pico/works/buildにおります。
cmake ..
pico/works/build/mcp3208におります。
make -j4
Resetボタンを押したまま、BOOTSELボタンを押し、Resetボタンを離してから、BOOTSELボタンを離します。RPI-RP2ドライブがマウントされました。
RPI-RP2ドライブへ、mcp3208.uf2をドラッグします。
ch0には、Analog Discovery Pro ADP3450のSupplies出力をつないでいます。ch1からch7は何もつないでいません。Supplies出力をDMMで測った電圧は1.79221Vです。
実行中の様子です。
●基準電圧Vrefに安定した電圧を供給
TL431は基準電圧源ICでポピュラです。MCP3208のVref端子とPicoのADC_VREF端子にこの電圧を入力して、測定します。DMMで測ると2.54802Vでした。これをプログラムのVrefに記入します。
入力電圧は、DMMで1.79220Vでした。
Vref=電源電圧のとき、 ADCは0.013963V、MCP3208は0.00591Vの誤差。 Vref=TL431のとき、ADCは0.049767V、MCP3208は0.0454Vの誤差。 |
電源電圧を基準電圧源として使ったほうが、誤差が少ない結果になりました。TL431の出力を直接、両方のVrefに入力したのが原因かもしれません。通常、OPアンプを使ったボルテージフォロワを入れることが多いです。 ただ、PicoのADC_VREF端子の動作がよくわかっていません。入力端子と思えるのですが、実際は電圧が出ています。ADCのAPIファンクションには、外部基準電圧の入力切り替えというような関数はないです。 |