2019年3月21日木曜日

STM32: ADCをDMAで使う(Nucleo-F446RE)

ADCの使い方の本丸、DMA経由でA-D Convertするテストをしました。

参考にした記事
STM32CubeのExample「ADC_RegularConversion_DMA」<STM32Cube>\Repository\STM32Cube_FW_F4_V1.24.0\Projects\STM32446E_EVAL\Examples\ADC\ADC_RegularConversion_DMA
「ガレスタさんのDIY日記」さんの「STM32でADCをやってみる2(DMAを使ったレギュラ変換)
「JP7FKFの備忘録」さんの「STM32マイコンでADCを使ってみる話

実行環境

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

DMAシングルチャネル


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

DMAを使った1チャネルのみのA-D Convertです。

STM32CubeMXの設定


[Analog]-[ADC1]で、[Mode]の[IN0]をチェックします。PA0がADC1-IN0に割り当てられます。これはNucleoボードのArduinoヘッダのA0に接続されています。


[Parameter Settings]
Continuous Conversion Mode: Enable
DMA Continuous Requests: Enable
End Of Conversion Selection: EOC flag at the end of single channel conversion
ADC_Regular_ConvesionMode
  Rank:1
    Sampling Time: 480Cycles
Sampling Timeはデフォルトでは3 Cyclesになっていますが、3 CyclesのままだとDMAの割り込み処理でCPU時間を消費しつくしてしまい、他の処理が行えません。一番大きい480 Cyclesを指定しました。実用的には、DMAの処理と他の処理との兼ね合いで値を調整することになると思います。


[DMA Settings]
[Add]ボタンを押して[DMA Request]でADC1を選択
Mode: Circular

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


  /* USER CODE BEGIN 2 */

  printf("before HAL_ADC_Start_DMA()\r\n");

  /*##-3- Start the conversion process #######################################*/
  /* Note: Considering IT occurring after each number of ADC conversions      */
  /*       (IT by DMA end of transfer), select sampling time and ADC clock    */
  /*       with sufficient duration to not create an overhead situation in    */
  /*        IRQHandler. */
  if(HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&uhADCxConvertedValue, 1) != HAL_OK)
  {
    /* Start Conversation Error */
    Error_Handler();
  }
  printf("after HAL_ADC_Start_DMA()\r\n");
  /* USER CODE END 2 */

前述の[Sampling Time]が3 Cyclesだと、DMA初期化後
    printf("after HAL_ADC_Start_DMA()\r\n");
に処理が移らず、メッセージが表示されません。

/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
  /* Turn LED2 on: Transfer process is correct */
  HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
/* USER CODE END 4 */

ADC変換後のDMA転送完了時の割り込み処理です。Nucleoボード上のLEDを点滅させています。点滅の間隔が短すぎて視認できないのでオシロで測定しました。


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

    /* USER CODE BEGIN 3 */
 HAL_GPIO_WritePin(CHK1_GPIO_Port, CHK1_Pin, GPIO_PIN_SET);
 printf("ADC Value: %d\r\n", uhADCxConvertedValue);
 HAL_GPIO_WritePin(CHK1_GPIO_Port, CHK1_Pin, GPIO_PIN_RESET);
  }
  /* USER CODE END 3 */

メインループ内で、printf()でADCの読み取り値をUART送信し、その前後で処理時間計測用にGPIOをH/Lさせています。


ch1:D13(LD2) ch2:D2(CHK1)

メインループ処理中(黄色)にも非同期にADCのDMA転送割り込み(赤色)が行われています。

Puttyで受信しているようす


マルチチャンネル


2ch分ADC読み取りを行いました。

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

STM32CubeMXの設定


[Analog]-[ADC1]で、[Mode]の[IN0]、[IN1]をチェックします。PA0がADC1-IN0、PA1がADC1-IN1に割り当てられます。これはNucleoボードのArduinoヘッダのA0、A1に接続されています。

[Parameter Settings]
Continuous Conversion Mode: Enable
DMA Continuous Requests: Enable
End Of Conversion Selection: EOC flag at the end of single channel conversion
Scanmode: Enable
ADC_Regular_ConversionMode
  Number Of Conversion: 2
Number Of Conversionで2以上を指定すると、[Scan Conversion Mode]は[Enable]しか選べなくなります。
Rank 1
  Channel: Channel 0
  Sampling Time: 480Cycles
Rank 2
  Channel: Channel 1
  Sampling Time: 480Cycles
Sampling Timeはひとまず480Cyclesを指定しました。
DMA Settings
  DMA Request: ADC1
  Mode: Circular

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

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define ADC_CHANNEL_N (2)
/* USER CODE END PD */

/* USER CODE BEGIN PV */
__IO uint16_t uhADCxConvertedValue[ADC_CHANNEL_N];
/* USER CODE END PV *

2ch分のバッファを確保しています。

/* USER CODE BEGIN 2 */
  printf("before HAL_ADC_Start_DMA()\r\n");

  /*##-3- Start the conversion process #######################################*/
  /* Note: Considering IT occurring after each number of ADC conversions      */
  /*       (IT by DMA end of transfer), select sampling time and ADC clock    */
  /*       with sufficient duration to not create an overhead situation in    */
  /*        IRQHandler. */
  if(HAL_ADC_Start_DMA(&hadc1, (uint32_t*)uhADCxConvertedValue, ADC_CHANNEL_N) != HAL_OK)
  {
    /* Start Conversation Error */
    Error_Handler();
  }

  printf("after HAL_ADC_Start_DMA()\r\n");
  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
 HAL_GPIO_WritePin(CHK1_GPIO_Port, CHK1_Pin, GPIO_PIN_SET);
 printf("ADC Value[0]: %d\t",   uhADCxConvertedValue[0]);
 printf("ADC Value[1]: %d\r\n", uhADCxConvertedValue[1]);
 HAL_GPIO_WritePin(CHK1_GPIO_Port, CHK1_Pin, GPIO_PIN_RESET);
  }
  /* USER CODE END 3 */

メインループでは、ADCの読み取り値をUART送信しています。


/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* AdcHandle)
{
  /* Turn LED2 on: Transfer process is correct */
  HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
}
/* USER CODE END 4 */

DMA転送割り込みハンドラではLEDを点滅させています。


ch1:D13(LD2) ch2:D2(CHK1)

Puttyで受信しているようす


0 件のコメント:

コメントを投稿