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(黄色)が動作不良。

0 件のコメント:

コメントを投稿