次は複数のポートに繋がっているセンサ電圧をADCで変換してUARTでデータ出力してみます。
参考にしたブログ一覧です。
回路図
バッテリ電圧計測用分圧抵抗回路は前回を参照してください。
フォトトランジスタを使った光量型距離センサの受光部です。壁のありなしと壁との距離を計測するため、Front Left、Left、Right、Front Rightと4つあります。
それぞれの電圧出力はSTM32F446のPC0~3のポートに接続しています。
STM32CubeMX
CubeMXの設定です。前回の設定からADC_IN10~13を追加します。
ポーリングによる変換は、以下を変更します。
- Scan Conversion ModeをEnableにする
- Discontinuous Conversion ModeをEnableにする
- Number Of Conversionに1ループで変換したい数(今回は5ポート分なので5)を入れる
- Rank1~5(Number Of Conversionで入れた数)にChannel番号とSampling Timeを入れる
ここでSTM32F446の独自だったのが、
- End Of Conversion Selectionに「EOC flag at the end of all conversion」と指定する
というところみたいです。
さらに、フォトトランジスタの回路の場合はSampling Timeを15 cyclesにします。
Sampling Time
ここでADCのパラメータとしてでてくるSampling Timeとはなんぞやという解説をします。
マイコン内部の周辺回路であるADC回路には抵抗RadcとコンデンサCadcで構成されたサンプルホールド回路が内蔵されています。下記はデータシートにあるADCのダイアグラムです。
画像引用:STM32F446データシート
抵抗とコンデンサがRC回路としてローパスフィルタの役割を果たすので入力される電圧変動に遅れが発生します。それぞれの数値は、Table 74に載っているとのことで、見てみるとそれぞれ6kΩと4~7pFのようです。
画像引用:STM32F446データシート
6kΩと7pFで構成されるローパスフィルタの、遮断周波数はfc = 3.789[MHz]、STEP入力したときに99.98%(12bit分解能)まで立ち上がる時間は357.7ナノ秒となります。
計算ツール:CRローパス・フィルタ数計算ツール
ADCクロック周波数(fadc)は、VDDAが3.3V入力のとき最大36MHzのようです。つまり、極端な例としてステップ入力3.3Vが入力される場合、3.3V(ビットでいうと4095)であると認識するためには、最低13クロック(36[MHz]x357.7[ns])分はコンデンサがチャージされるまで待つ必要があるというのがわかります。
今回は、動作周波数が最大になるように設定したので、PCLK2は90[MHz]になっています。CubeMXで、Clock PrescalerをPCLK2 divided by 4にしているので、22.5[MHz](90[MHz]÷4)がADCクロック周波数(fadc)となります。
そうすると、最低8クロック(22.5[MHz]x357.7[ns])以上待てばいい気がします。そうなると、選択肢が限られているので、次に遅い15クロックを選択することになります。
ここからややこしくなるのですが、ADCにつながっているセンサ回路は、上記の回路図を見てみるとQ5のフォトトランジスタ(電流出力)とR26の抵抗(I→V変換)で構成しています。
暗いときはフォトトランジスタの電流出力は小さくなり、明るいときはフォトトランジスタの電流出力は大きくなるため、明るさによってCadcのコンデンサに電荷がたまる時間が変動します。つまり、ADCからみた、マイクロマウスの受光回路のインピーダンスは、明るさによって変動する、待つ必要がある最低クロック数も変動するということです。
ただし、今回は制御周期1kHzの中で点灯時と消灯時の2回サンプリングする程度(usecオーダー)で、高速なADCを行う必要がほとんどなく、インピーダンスが最も大きいときのクロック数に合わせておけばいい気がします。ここは競技者によってフィルタ処理でサンプリング数が増えるので、処理方法を変更する場合は見直す必要があります。
また、高い電圧のポートから低い電圧のポートへポーリングするときは、コンデンサCadcに溜まった電荷を放電しないと前回の値が残っている状態になるので、電荷を下げきるまでSamping Timeの時間をとる必要あります。今回の回路はIV変換に使っている抵抗1kΩがついているので、抵抗1kΩ+Radc6kΩ=7kΩになり、極端な例として3.3V→0Vで0.02%(12bit分解能)まで立ち下がる時間は417.3ナノ秒→約9クロックかかります。選択肢の15 Cyclesにしておけば、十分余裕があります。
前回のバッテリ電圧計測回路の方は、20kΩ+6kΩ=26kΩ→1.55μ秒→約35クロックかかります。ただし、アナログフィルタとして搭載しているチャージ済みのC5のコンデンサから電荷がRadc経由でCadcに流れ込むため、一段早めた選択肢の28 Cyclesにしておきました。
プログラム
プログラムです。HAL_ADC_PollForConversion関数を使います。
setbuf(stdout, NULL); setbuf(stdin, NULL ); printf("Started!\n\r"); uint16_t adc_value = 0; float a[5]; /* USER CODE END 2 */
/* Infinite loop */ /* USER CODE BEGIN WHILE */ // ADC sequence for(int i=0; i<5; i++){ HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 1); adc_value = HAL_ADC_GetValue(&hadc1); a[i] = (float)adc_value * 3.3 / 4096; } // Voltage divider resistor Vbatt -> 20kΩ -> ADport -> 6.8kΩ -> GND a[0] = a[0] * (20+6.8) / 6.8; printf("adc_volt0 =%.3f, adc_volt1 =%.3f, adc_volt2 =%.3f, adc_volt3 =%.3f, adc_volt4 =%.3f\n\r", a[0], a[1], a[2], a[3], a[4]); HAL_Delay(500); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }
実行
変換して出力できました。
センシングの途中で、Leftのフォトトランジスタにスマホのフラッシュライトをかざしてみました。電圧が上がっています。