ADCをもう少しだけ深く使ってみます。
以前複数チャンネルADCで苦労した記憶がありますので,自分のまとめとして書いておきます。
2つのチャンネルを一つずつ読む
テストのため,PA3とPA4 PA5とPA1を接続し,DAの出力をADで読みます。
PIN Arduino STM32
7 A5 PA6 DAC2_OUT1 UnBuffer
8 A2 PA5 DAC1_OUT2 UnBuffer --+
9 A3 PA4 DAC1_OUT1 --+ |
10 A2 PA3 ADC1_IN4 --+ |
11 A1 PA1 ADC1_IN2 -----------+
...
ADC1 IN2 と IN4 を有効にします。
- Scan Conversion 複数チャンネルをADすることのようです。Enable
- Continuous は連続してADする場合。Disable
- なぜか Discontinuous の設定項目もある。とりあえず Disable
- DMA まずは Disable
- End Of Conversion Selection いつ EOCフラグを立てるかの設定のようですね。1チャンネルごとにしたいので
End of single conversion
- Regular と Injection 2つの ADC があって,とにかく普通のは Regular です。
- Number Of Conversion を 2 にすると,その下でやっと Channel 2 と 4 が出てきます。わかりにくいですが仕方ないですね。
/* USER CODE BEGIN 2 */
HAL_DAC_Init(&hdac1);
HAL_DAC_SetValue(&hdac1, DAC1_CHANNEL_1, DAC_ALIGN_12B_R, 0x800);
HAL_DAC_Start(&hdac1, DAC1_CHANNEL_1);
HAL_DAC_SetValue(&hdac1, DAC1_CHANNEL_2, DAC_ALIGN_12B_R, 0x80);
HAL_DAC_Start(&hdac1, DAC1_CHANNEL_2);
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
uint16_t result[8]= {0};
HAL_ADC_Start(&hadc1);
while ( !(ADC1->ISR & (1<<2)) ) {} // wait until EOC is 1
result[0]=HAL_ADC_GetValue(&hadc1); // ADC1 channel 4 = DAC1 channel 1
while ( !(ADC1->ISR & (1<<2)) ) {} // wait until EOC is 1
result[1]=HAL_ADC_GetValue(&hadc1); // ADC1 channel 2 = DAC1 channel 2
HAL_ADC_Start(&hadc1);
while ( !(ADC1->ISR & (1<<2)) ) {} // wait until EOC is 1
result[2]=HAL_ADC_GetValue(&hadc1); // ADC1 channel 4 = DAC1 channel 1
while ( !(ADC1->ISR & (1<<2)) ) {} // wait until EOC is 1
result[3]=HAL_ADC_GetValue(&hadc1); // ADC1 channel 2 = DAC1 channel 2
/* USER CODE END 2 */
実行結果
DMAを使う場合
EOC設定をEnd of sequence of conversion
にします。すべてのチャンネルを変換し終わったら,という感じですね。
DMA設定を追加します。
8要素の配列を用意しましたが,HAL_ADC_PollForConversion
のあと,最初の2個に結果が入ります。
uint16_t result[8]= {0};
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&result, 2);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
その後,再度 HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&result, 2);
して結果を待てば,再び result[0] result[1] に結果が入りました。
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&result, 2);
では 2ch分読むように指定しているのですが,最後の引数を4にしたらどうなるでしょうか。結果,2ch分だけ,result[0]
とresult[1]
が更新されて戻ってきていました。
HAL_ADC_PollForConversion
しない場合はどうなるのでしょうか。深くは検証できませんでしたが,DMAが終わっていれば結果が正しく入っていました。
たとえばAD前に準備が必要で,変換のタイミングを自分で決めたい場合にこの方法は有効ですね。
連続で変換する
要するにほったらかしでDMA結果を参照すればその時までに変換転送された値が読めるというものです。
Continous と DMA Continuous を Enable にします。EOCは single conversion にします。
Sampling Time を大きくします。ここでは61.5 Cycles
にしました。(理由はあとで)
DMA Mode を変更します。
/* USER CODE BEGIN 2 */
// DAC設定1 省略
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
uint16_t result[8] = {0};
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)&result, 2);
// break 1 DAC設定1 が読める
// DAC 設定 2 省略
HAL_Delay(10);
// break 2 DAC設定2 が読める
// DAC 設定 3 省略
HAL_Delay(10);
// break 3 DAC設定3 が読める
/* USER CODE END 2 */
result[0]
と result[1]
を参照すればその時までにDMAされた変換値が入ります。
ADC の Sampling Time をあえて大きくしたのは,DMAが終了する前に次のADCが終了してしまい,フリーズのような状況に陥るのを防ぐためです。本当はきちんと計算しなくてはいけないのですが,システム 8MHz ADC は div 4 で設定した本件では 61.5 Cycles 必要だったということになります。
参考