製作モジュール一覧

ラベル MCP3208 の投稿を表示しています。 すべての投稿を表示
ラベル MCP3208 の投稿を表示しています。 すべての投稿を表示

2019年3月26日火曜日

STM32: SPI ADCのMCP3208を使う(Nucleo-F446RE)

SPIのマスター側が読み取る必要があるA-DコンバーターのMCP3208を動作させました。

プロジェクト:
https://github.com/ryood/STM32Cube_Test/tree/master/SW4STM32/Nucleo-F446_MCP3028_Test1

実行環境

  • Nucleo-F446RE
  • STM32CubeMX Version 5.1.0
  • System Workbench for STM32 - C/C++ Embedded Development Tools for MCU Version: 2.8.1.201903050911

配線


STM32CubeMXの設定


CLOCK Confiiguration

[HCLK]: 128

プリスケーラ-で分周しやすいようにHCLKを128MHz。

[SPI1]-[Parameter Settings]

Clock Prameters
  Prescaler(for Baud Rate): 64

MCP3208はVdd=2.7Vのときクロック周波数は最大1.0MHzなのでそれに合わせてプリスケーラ-を設定。

GPIOの設定

SPI1のNSS(CS)をソフトウェア制御するために、GPIOで[SPI1_CS]を割り当て。

SW4STM32でコードを追加(一部抜粋)


/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define MCP3208_START_BIT   0x04
#define MCP3208_MODE_SINGLE 0x02    // Single-ended mode
#define MCP3208_MODE_DIFF   0x00    // Differential mode
/* USER CODE END PD */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
 HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
 return ch;
}

uint16_t MCP3208_read_u16(uint8_t channel)
{
  uint8_t aTxBuffer[3];
  uint8_t aRxBuffer[3];

  aTxBuffer[0] = MCP3208_START_BIT | MCP3208_MODE_SINGLE | ((channel & 0x04) >> 2);
  aTxBuffer[1] = (channel & 0x03) << 6;
  aTxBuffer[2] = 0;

  HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);

  if(HAL_SPI_TransmitReceive(&hspi1, aTxBuffer, aRxBuffer, 3, 100) != HAL_OK)
  {
    /* Transfer error in transmission process */
    Error_Handler();
  }

  HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);

  // RXデータの2バイト目の上位4ビットは値が不定のため0x0fでマスキング
  uint16_t conv_result = ((uint16_t)(aRxBuffer[1] & 0x0f) << 8) | (uint16_t)aRxBuffer[2];

  return conv_result;
}

/* USER CODE END 0 */

MCP3208のデータシートを見ると、SPIで8bit単位で送受信すると、RXデータ(12bit)の上位3ビットはHI-Zで不定になり、4ビット目は0になるようです。このためRXデータ(aRxBuffer[])の2バイト目の上位4ビットはプログラムで読み捨てています。


↑DOUTがNucleo-F446側から見るとRXデータになります。


  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    uint16_t adcv[3] = {0};
    for (int i = 0; i < 3; i++) {
      adcv[i] = MCP3208_read_u16(i);
    }
    printf("Read VAL:\t%d\t%d\t%d\r\n", adcv[0], adcv[1], adcv[2]);
  }
  /* USER CODE END 3 */

メインループでMCP3208をポーリングして、値をprintf(UART出力)しています。

SPI信号波形

MISO

ch1:MISO ch2:SCK

MOSI

ch1:MOSI ch2:SCK

<追記:2019.03.29>

MOSIの3バイト目(aTxBuffer[2])は「0」のハズなのに、値が出力されてますね(@@? バグがありそうです。

</追記>

CS

ch1:CS ch2:SCK

Puttyで受信しているようす

メモ

3連POTのch0(黄色)が動作不良。

2017年6月9日金曜日

SPI ADCのMCP3208をArduinoとNucleo(mbed)で使ってみる。

SPI接続のA/DコンバーターのMCP3208をArduino UnoとNucleo F401RE(mbed)で使ってみた。

Arduino Uno


キム茶工房さんの記事を参考にさせていただきました。配線、スケッチ等そのままで特に問題なく動作。


気になった点

SPI通信は普通8bitごとなのに対し、MCP3208のデータシートによるとデータ構造が一見8bitごとになっていない。また、SPI通信は全二重(MISOとMOSIで送受信を同時に行う)

ちゃんと考える必要ありそうだったが、データシートにSPIでの使い方が説明されていた。


上図の下部のように、8bit×3のSPI送受信で1ch分のA/Dコンバーターの読み取り値を取得できる。

Nucleo F401RE


Coretex-M4で84MHz駆動のNucleo F401REで実験した。

mbedのライブラリが公開されていたので使わせていただきました。https://developer.mbed.org/users/Kemp/code/mcp3208/

ブレッドボード配線図


MCP3208を2個使って、合計16ch使えるようにしてみた。SPIのSCK、MOSI、MISOは共通バス、CSはそれぞれのデバイスに割り当てる。

<main.cpp>

#include "mbed.h"
#include "mcp3208.h"

int main()
{
    SPI spiM(SPI_MOSI, SPI_MISO, SPI_SCK);
    spiM.frequency(2000000);
    MCP3208 mcp3208_0(spiM, D10);
    MCP3208 mcp3208_1(spiM, D9);
    
    DigitalOut checkPin(D2);
    
    float v0[8];
    float v1[8];

    for (;;) {
        for (int i = 0; i < 8; i++) {
            checkPin = 1;
            v0[i] = mcp3208_0.read_input(i);
            checkPin = 0;
            wait_us(1);
        }        
        for (int i = 0; i < 8; i++) {
            checkPin = 1;
            v1[i] = mcp3208_1.read_input(i);
            checkPin = 0;
            wait_us(1);
        }
        
        printf("Device0\t");
        for (int i = 0; i < 8; i++) {
            printf("%.3f\t", v0[i]);
        }
        printf("\r\n");
        printf("Device1\t");
        for (int i = 0; i < 8; i++) {
            printf("%.3f\t", v1[i]);
        }
        printf("\r\n");
        
        wait(0.2);
    }
}

mbed Repository:
https://developer.mbed.org/users/ryood/code/Nucleo_MCP3208_Test/

MCP3208のSPIクロックは5V駆動で最大2MHz、2.7V駆動で1MHzとなっているが、3.3V駆動させて

spiM.frequency(2000000);

と、2MHzのクロックを指定してみたが、動作はするようだ。

DigitalOutにcheckPinを割り当ててSPI経由の読み取り時間を測定してみた。

SPI通信のようす


SCK、MOSI

ch1:MOSI ch2:SCK

SCK、MISO

ch1:MISO ch2:SCK

クロック速度は1.3MHz程度。MCP3208からの出力のMISOはLのときに変動がある。PullDownしておいたほうがいいかもしれない。また、出力波形の立ち上がり/立ち下がりも少し遅いようだ。

処理時間

ch1:checkPin ch2:SCK

1ch分、8bitx3の処理速度を測ると40us程度かかるようだ。周波数にすると25kHz程度なので高速動作させる場合はちゃんと考えておく必要がありそうだ。

Tickerのタイマー割り込みと同時に使ってみる。


96kHzの割り込み処理と同時に使うことを想定してテストしてみた。SPIクロックも4MHzにしてみた。(MCP3208の仕様外)

<main.cpp>

#include "mbed.h"
#include "mcp3208.h"

#define SAMPLING_RATE   (96000)
#define SAMPLING_PERIOD (1.0f/SAMPLING_RATE)

DigitalOut checkPin(D2);

void isr()
{
    checkPin = 1;
    wait_us(1);
    checkPin = 0;
}

int main()
{
    SPI spiM(SPI_MOSI, SPI_MISO, SPI_SCK);
    spiM.frequency(4000000);
    MCP3208 mcp3208_0(spiM, D10);
    MCP3208 mcp3208_1(spiM, D9);
   
    float v0[8];
    float v1[8];
   
    Ticker t;
    t.attach(&isr, SAMPLING_PERIOD);

    for (;;) {
        for (int i = 0; i < 8; i++) {
            v0[i] = mcp3208_0.read_input(i);
        }      
        for (int i = 0; i < 8; i++) {
            v1[i] = mcp3208_1.read_input(i);
        }
       
        printf("Device0\t");
        for (int i = 0; i < 8; i++) {
            printf("%.3f\t", v0[i]);
        }
        printf("\r\n");
        printf("Device1\t");
        for (int i = 0; i < 8; i++) {
            printf("%.3f\t", v1[i]);
        }
        printf("\r\n");
       
        wait(0.2);
    }
}

mbed Repository:
https://developer.mbed.org/users/ryood/code/Nucleo_MCP3208_Ticker_Test/

割り込みハンドラのisr()内で、checkPinをH→Lさせている。

SCK、MOSI

ch1:MOSI ch2:SCK

SCK、MISO

ch1:MISO ch2:SCK

クロック周波数は2.6MHz程度。

Ticker割り込み

ch1:checkPin ch2:SPI_SCK

Ticker割り込みが優先されてSPI通信(SCK)が遅れている様に見えるがTicker割り込みの周期が一定な確証はない。

MOSIをPullDown

MOSIを1kΩのRでPullDownしてみた。

SPI Clock 2MHz指定

ch1:MISO ch2:SCK

SPI Clock 4MHz指定

ch1:MISO ch2:SCK

メモ:

MCP3008は10bit精度になるが、5V駆動で最大3.6MHz。2.7V駆動で最大1.35MHz。