ADCのシングルモード(仮称:スキャンしないモード)
F303K8のADCは、変換結果を出力するレジスタが一つしかないです。
そのため、スキャンモードで複数チャンネルを取得する場合、各チャンネルの変換終了時に、結果をDMAとか割込でメモリに待避しないと、以前のチャンネルの結果が上書きされてしまいます。スキャンモードを使う場合はDMAか割込を使え、ということだと思います。
一方、シングルモードで一回ごとにチャンネルを切り替えてデータを取得する方法もあります。チャンネル決め撃ちシングルショットの設定はCubeMXが吐いてくれますが、どうも後からチャンネルだけ変更するHAL関数は用意されていないように見えます。
シングルモードでサンプリングチャンネルを切り替える
電力削減とかテストの都合で、シングルモードでチャンネルだけ切り替えて再サンプリングしたいケースはあるかと思います。自分は下記の手順でチャンネルを切り替えることができました。
ADC レギュラシーケンスレジスタ 1
SQR1レジスタの一番目に、変換したいチャンネルを書きます(下記はch2の場合)。
WRITE_REG(hadc->Instance->SQR1, ADC_SQR1_RK(2,1) );
ADCサンプル時間レジスタを指定する。
サンプル時間レジスタ"SMPRx"は、チャンネルの範囲によって1(1〜9)または2(10〜16)を指定します。サンプリング周期はCubeMXで指定した値を使うのがいいと思います。この値はCubeMXが生成したMX_ADC1_Init()の中で
sConfig.SamplingTime = ADC_SAMPLETIME_601CYCLES_5;
と指定されています。601.5サイクルを指定した場合は、
WRITE_REG(hadc->Instance->SMPR1,ADC_SMPR1(ADC_SAMPLETIME_601CYCLES_5, 2) );
となります。
取得
以上のWRITE_REG()2行で、CubeMXが生成した初期化処理のうちチャンネルだけこっそり変更できるので、
// CH1
WRITE_REG(hadc->Instance->SQR1, ADC_SQR1_RK(1,1) );
WRITE_REG(hadc->Instance->SMPR1,ADC_SMPR1(ADC_SAMPLETIME_601CYCLES_5, 1) );
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1,1000);
lvdt1_a = (float) HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
// CH2
WRITE_REG(hadc->Instance->SQR1, ADC_SQR1_RK(2,1) );
WRITE_REG(hadc->Instance->SMPR1,ADC_SMPR1(ADC_SAMPLETIME_601CYCLES_5, 2) );
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1,1000);
lvdt1_b = (float) HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
とかすると、シングルモードでチャンネルを切り替えてデータが取得できます。チャンネル10以上のサンプリング周期はSMPR2に指定しないといけないので、実際はべた書きより関数にしたほうがいいと思います。
補足:使っているマクロについて
SQR1用のマクロはstm32f3xx_hal_adc_ex.hで定義されていて、サンプリング時間をレジスタの指定位置に詰め込むのが役目です。
#define ADC_SQR1_RK(_CHANNELNB_, _RANKNB_) ((_CHANNELNB_) << (6U * (_RANKNB_)))
ADC_SQR1_RKの二番目の引数は変換する数なので、今回は1で固定になります。
複数チャンネルをスキャンする場合は、SQR1のLの部分にスキャンしたい数を書いて、
SRR1~SQR4にチャンネルを書いていくことになりますが、1チャンネルだけ変換するに場合はSQR1だけで足ります。
SMPRx用のマクロもstm32f3xx_hal_adc_ex.hで定義されていて、シーケンスの順番(Rank)の位置にチャンネル番号を押し込むのが役目です。
#define ADC_SMPR1(_SAMPLETIME_, _CHANNELNB_) ((_SAMPLETIME_) << (3U * (_CHANNELNB_)))