STM32 HAL ADCの基礎の基礎
おことわり・前提
- STM32CubeIDEを使った話をします.
- あくまで自分用のメモという目的が主.
- 逐次updateしたり追記したりがあるかもしれません.
本題
DMAで連続変換するときの鍵
- clockはsystem clock devidedなものを入れると楽.Async clockは別途記述が必要.
- continuous conversion enableにしてDMA continuous request enableにしておくとDMA割り込みが入りまくることに注意.ADCが終わるたびにDMA走る.
- これを防ぐにはcontinuous conversion disableとして普通にタイマ割り込みでADCを回すのが良さそう.DMA continuous requestはenableのまま.
- DMAはcircularを選ぶ
- DMAのmemory はincrementにしておく
- DMA continuous request enable
- 上記をやっておかないと連続変換されなかったり,DMAに連続的に変換結果が入らずにハマる.
- もちろん理解して変更すればいいが,基本上記でいいかなぁという気持ち.
code examples
constexpr adcChannels = 3; //number of using ADC channels static uint16_t adcData[adcChannels]; if (HAL_ADC_Start_DMA(&hadc,(uint32_t *)adcData, adcChannels) != HAL_OK) Error_Handler();
これで逐次clockに応じてADCが動作しDMAで adcData[]
に変換結果が転送される.
変換完了後にcallbackしたい場合は下記等が便利.変換完了時にcallbackされる.
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *AdcHandle){ //do something }
- キャリブレーションが必要な場合は下記を呼び出すことでADCのキャリブレーションが可能らしい. しかしこれは通常起動時に実施済みであるため明示的には不要の場合が多いが,長時間駆動する機器の場合定期的にキャリブレーションを実施することにより精度の高い測定が可能となるよう.
if(HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK)
Error_Handler();
タイマ割り込みにする場合
- CubeMXでADCのconfigを下記のようにする.ADCのチャネル,DMA転送など基本的なconfigはできている前提で
ADC_Regular_ConversionMode
->External Trigger Conversion Source
をTimer1 Trigger Out event
に.
- さらにCubeMXでTIM Timer1のconfigをする.プリスケーラやカウンタ値等のタイマ割り込みを発生させるconfigは書けている前提で
Trigger Output(TRGO) Parameters
->Trigger Event Selection
をUpdate Event
に. これでタイマ割り込みでADCがトリガされるはず.
- CubeMXでADCのconfigを下記のようにする.ADCのチャネル,DMA転送など基本的なconfigはできている前提で
ADCのキャネルとデータの入り方
ch1, ch2, ch3, ch4, ch5, ch6, ch7
を有効にしていたとして
constexpr adcChannels = 8; //number of using ADC channels static uint16_t adcData[adcChannels]; if (HAL_ADC_Start_DMA(&hadc,(uint32_t *)adcData, adcChannels) != HAL_OK) Error_Handler();
このようなコードを書いた場合,変換結果は
ch1: adcData[0]; ch2: adcData[1]; ch3: adcData[2]; ch4: adcData[3]; ch5: adcData[4]; ch6: adcData[5]; ch7: adcData[6]; ch8: adcData[7];
に対応して格納される.
ch1
, ch3
, ch5
, ch7
を有効(ch2
, ch4
, ch6
は無効)にしていたとして
constexpr adcChannels = 4; //number of using ADC channels static uint16_t adcData[adcChannels]; if (HAL_ADC_Start_DMA(&hadc,(uint32_t *)adcData, adcChannels) != HAL_OK) Error_Handler();
このようなコードを書くと
ch1: adcData[0]; ch3: adcData[1]; ch5: adcData[2]; ch7: adcData[3];
仮にここで
constexpr adcChannels = 3; //number of using ADC channels
としてしまった場合,書くchの変換結果が同一の添字(配列index)で取得できなくなる.
具体的にはリングバッファのようにずれてしまう.
したがってHAL_ADC_Start_DMA()
に渡すbufはchに対し十分余裕を持ちlengthは有効化しているadcチャネルと同一値とすることが大変重要である.
有効化しているadcチャネル数よりも少なくても多くてもずれが生じる.
この状態で気づかずに変換結果を取得すると逐次値が変動してハマる.ノイズのよう見見えてしまうことがあるかもしれない.
念の為adcの各chが同一の添字で同じものが取り続けられているかをserial(uart)等を利用し事前に確認しておくことが望ましい.
- ちなみにプロトタイプはこんな感じになっている:
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length)