1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-02-21

 Picoには12ビット4チャネルのA-Dコンバータが内蔵されています。もし16チャネル分足りなかったら、という想定で外部に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を2個使って16チャネルにします。PicoのSPIバスとは共通の配線です。ただし、2個目のチップ・セレクト信号のCSは別途GP6に配線します。1個目のチップ・セレクト信号のCSはGP5に配線します。

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

プログラムmcp3208x2.c

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

 MCP3208データシート

 入力チャネルの指定は、表の左の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は、できるだけ正確にテスタで測った値を記入しておきます。

 前回のreadADC()関数の引数に、チップ・セレクト信号CSを加えました。0は1個目、0以外は2個目です。

/**
 * 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_CS2  6
# 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");
}

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

static inline void cs2_deselect() {
    asm volatile("nop \n nop \n nop");
    gpio_put(PIN_CS2, 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);

    gpio_init(PIN_CS2);
    gpio_set_dir(PIN_CS2, GPIO_OUT);
    gpio_put(PIN_CS2, 1);
}

int readADC(uint8_t ch, uint8_t CS){  // cs 0 or 2
    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 == 0 ? cs_select() : cs2_select();
    sleep_ms(1);
    spi_write_read_blocking(SPI_PORT, writeData, buffer, 3);
    sleep_ms(1);
    CS == 0 ? cs_deselect() : cs2_deselect();

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

int main() {
    stdio_init_all();

    printf("\nHello, MCP3208x2 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, 0) / 4096);
    }
    printf("-----\n");
    for (uint8_t i=0; i<8; i++){
        printf("ch%d is %.4fV\n", i, Vref * readADC(i, 2) / 4096);
    }
    return 0;
}

 実行結果です。1個目のch0はTL431出力、ch7はGNDに、2個目のch0は3.3V、ch7はGNDにつないでいます。どちらもch1~ch6は何もつないでいないので不定です。
adc501.png

IMGP0258.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?