JP7FKFの備忘録

ヒトは,忘れる生き物だから.

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
}
if(HAL_ADCEx_Calibration_Start(&hadc) != HAL_OK)
    Error_Handler();
  • タイマ割り込みにする場合

    • CubeMXでADCのconfigを下記のようにする.ADCのチャネル,DMA転送など基本的なconfigはできている前提で
      • ADC_Regular_ConversionMode->External Trigger Conversion SourceTimer1 Trigger Out eventに. f:id:jp7fkf:20200603210138p:plain
    • さらにCubeMXでTIM Timer1のconfigをする.プリスケーラやカウンタ値等のタイマ割り込みを発生させるconfigは書けている前提で
      • Trigger Output(TRGO) Parameters -> Trigger Event SelectionUpdate Eventに. f:id:jp7fkf:20200603210133p:plain これでタイマ割り込みでADCがトリガされるはず.
  • 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)