2024年4月3日水曜日

STM32F303K8でピークホールド&時間決定回路

 STM32F303K8にはアナログ系の機能がいくつか乗っているので、それを使用してピークホールド回路を作ってみた。


 まず、テスト用の信号として乱雑な波形を作成する。Xorshiftで乱数を生成し、CICでインターポレーション(R16, N6, LPF特性)を行い、DACから1kspsで出力。

 とりあえず狙い通りに動いている感じ。

 R16, N6のインターポレーションでゲインが2^20だから、12bitの乱数を突っ込んでやれば32bit変数で計算できる。適当に右シフトしてDACへ出力。ある程度最適化したCコードを書いてやれば2048ptsの波形の生成に753us(@72MHz)くらい。1サンプルあたり26.5サイクルで処理できる。

 実数だけのCICはレジスタの使用量が半分で済むからARMの少ないレジスタでも最適化しやすい。/* 複素数でも結局は実部・虚部それぞれで和差を取るだけだから、複素CICフィルタでも実部・虚部に分けて処理すれば早くなりそう。メモリアクセスでペナルティありそうだけど、どうせレジスタに乗り切らないならレジスタをメモリに退避させなきゃいけないんだし */


 手始めに、コンパレータ、ダイオード(1N4148)、コンデンサ(0.1uF)でピークホールド

 電圧落ちるのが結構早い。プローブを2本あてると2倍近い速さで落ちるから、プローブ(10MΩ)で放電してるんだろう。定抵抗負荷でコンデンサの放電時間を計算してみると0.09秒くらいだから、抵抗とかコンデンサの特性を合わせれば十分な精度で一致していると言える。10MΩって数字だけ見るとかなり巨大な抵抗値だけど、サブ秒オーダーで見るとだいぶ影響力があるな。


 ダイオードを2本直列に使って、中間点をオペアンプ(フォロア)でドライブ

 全く電圧が落ちてこない。DC付近で簡易的とはいえ、パッシブプローブとアクティブプローブの差を実感できる。ただ、ダイオードを直列で2本使っているから、VDD近くまで引っ張れない。

 ダイオード2本直列で中間点をオペアンプで駆動するのは、漏れ電流対策らしい。コンパレータはHigh/Lowの2値だから、Lowに引っ張っていると電圧差が大きく漏れ電流が発生する。フォロアで中間点を引っ張っておけば、2本目のダイオードは電圧差がほぼゼロになるから、漏れ電流を防げる、というような理論らしい。実際の効果はよくわからないけど。


 タイマのPWMでピークホールドのリセット

 ODで引っ張っている。あと、別のチャンネルでコンパレータのブランキングも出している(コンパレータからのHigh出力をLowに引っ張るのを防ぐため)。


 タイマからADCをトリガして、結果をDAC(テスト信号用とは別)へ転送

 紫がフォロアの出力、黄色がDACの出力。リセット直前にサンプリングしてDACへ転送しているので、1周期(100ms)分遅れる。


 コンパレータの出力をタイマのカウンタリセットに使用して、ピーク時からの経過時間を計測

 アナログ波形はオシロから取り込んでいる。

 コンパレータとタイマの外部入力を直結できないので、GPIO経由で接続する必要がある。ADCのトリガとタイマカウンタの読み出しがずれるとジッタになるので、ADCと同時にDMAをトリガして、カウンタをRAMに読み出している。

 飽和するとコンパレータがHighで固定されるので、ピーク値のタイミングがズレる(ピーク値の値も正しくない)。今回の回路の場合、正しく計測できるのは2.3Vあたりまでかな。

 似たようなことは、例えばコンパレータの出力でコンデンサを充電し抵抗で放電して、電圧を測ることで経過時間を推定するみたいなアナログ回路も考えられるけど、この場合はアナログ素子の特性(個体差や温度特性)に影響を受けるので、あまり正確な計測はできない。タイマを使えばマイコンのクロックの精度で時刻を計測できる。今回は32bitのカウンタを72MHzで走らせているので、時間分解能約14nsで最大59秒くらいまで計測できる。時間の計測はデジタルだけど、ピーク値の保持はアナログ回路だから、あまりサンプリング頻度が低くても精度が劣化するだけだし、最大でも32秒に1回とかで使えば良い。ピークの高さにはあまり関心がなくて、ピークの位置だけしっかり測りたい(しかしピークの高さも一応ほしい)みたいな用途には良さそう。必要な時間分解能でADCで計測して最大値の位置を探す、みたいな総当たりで処理するより、ピークの高さの計測精度は若干劣るとはいえ、ものすごく少ない計算リソースで計測できる(ハードウェアリソースは若干必要だけど)。

 ただ、全体のタイミングの管理を16bitタイマで行っているので、ブランキング期間の分解能があまり良くない。例えばプリスケーラを36000に設定すると最大で32.768秒間隔でサンプリングできるけど、この場合はブランキング幅が1.8ms以上になる。この間に細いパルスが入っても検出できない。


 乱数波形でなく、Sincでテスト

 帯域幅2.5kHzのSincだと、1発目では最大値まで上がりきらない。


 750Hzでもまだ上がりきらない。かなり遅い信号じゃないとホールドしてくれない。


 ダイオード2本分の電圧降下かと思って、試しに2.5kHz幅を下側にオフセット

 やっぱり1発では上がりきらないから、帯域幅の問題だろう。コンデンサの誘電体吸収みたいな効果かな?


 ピンアサイン

 PA0とPA4からPB1までの7本を使っている(PA5はテスト信号生成用)。

 未使用ピンで2x UART、I2C、SPI、CANが使用できる。あとPA1のADCやPA5のDACも使用可能。UARTでGPSモジュールを接続したり、I2Cで他のセンサを接続したり、SPIでMMCにデータを保存したり、DACで副変調波を出して無線機でデータを飛ばしたり、いろいろできる。計算リソースはCortex-M4F@72MHzが丸ごと使えるから、結構複雑なこともできる。もっとも、RAMが16kBしかないから、あまりに複雑な処理(大規模な相関処理とか)は厳しいけど。


 回路図

 テスト信号をTIM7, DAC, DMAで生成している。コンパレータ4からダイオード2本を経由してコンデンサを充電してピークホールド、オペアンプ2でダイオードの漏れ電流を対策しつつインピーダンスを下げてからADCへ。GPIOのNchでリセットを行っている。ADCのトリガ、コンデンサのリセット、リセット中のコンパレータのブランキングはTIM3で管理している。それと、コンパレータでTIM2をクリアして、TIM3からDMAを蹴って、ADCサンプリング時のピークからの経過時間も計測している。


 ある程度作り込んだところで、不注意でソースコードを全部消してしまった。久々にやらかした。。。

1) STM32CubeMXは新しいプロジェクトを作るとそのディレクトリにある全部のファイルを削除(ゴミ箱でなく、完全に削除)するから気をつけよう。

2) 確認ダイアログはちゃんと読もう(← !!!超重要!!!)。

 コードはgitで管理してるから、とか油断してるとgitリポジトリ事まるごと削除されて詰む。


 ということで、折角の機会なので、COMP4で作っていたピークホールドをCOMP2で作り変えてみた。COMP2を使うとタイミング管理にTIM2(32bitカウンタ)を使えるから、リセットパルスを狭く設定できる。ただしピーク値の時間分解能がだいぶ悪くなる。


 ピンアサイン

 必要なGPIOが1本増える(リセットのピンが共用できない)。あとUART2が使えなくなる。それ以外はあまり違いはない。タンパーピンが使えるようになるから、改造されると困るような用途に使えるようになる。


 回路図

 ADCトリガ、ブランク、リセットに対して個別にパルスを出すから、タイミング管理の自由度が高い。


 COMP2とCOMP4、どっちを使っても機能的には大差ない。16bitのタイマと32bitのタイマの使い分けの違いでしかない。時刻決定の精度が欲しいならCOMP4、ブランク期間(パルスを検出できない)を狭くしたいならCOMP2、という感じ。ソフトやハードが変わるから、どちらを重視するか(どっちのコンパレータを使うか)はあらかじめ決めておく必要がある。


 ということで、STM32F303K8のワンチップで、ピークホールド回路、ピーク値の計測、ピークの時刻決定(ピーク間隔の計測とか)、といったことができるのは確認できた。外部に能動素子は不要で、ダイオード2本、コンデンサ1個、抵抗1個、くらいの受動素子で構成できる(時刻精度が必要なら外部クロックも必要)。あと、もちろん電源周りにいくつかの受動素子と、少なくとも3端子レギュレータ程度は必要だけど。

 ソフトウェア的にはほとんど無負荷(ADCと時刻の読み出しだけ)だから、並行して重い処理も走らせられる。重い処理が不要ならコアは大部分の時間をスリープに入れられるけど、とはいえCortexってただでさえ消費電力が低いから、スリープに入れてもあんまり消費電力下がらないんだよな。タイマがあるからクロックを止められなくて、そこでだいぶ消費電力が大きい。時間分解能を多少犠牲にしていいならコアクロックを下げてだいぶ省電力になるけど。まあ、数十秒周期で1発のくらいのイベントを想定しているし、時間差で入射方向を決定したいみたいなことをやらない限りは、時間分解能はあんまり必要ないだろうし。ただ、GPIOの消費量がかなり多いので、外部接続が必要な用途にはちょっと厳しい。せいぜいGPSで位置や時刻を得て外付けストレージにCSVファイルを保存する、くらいかな。外付けストレージと言っても、STM32F303K8はUSB Host非対応だけど。SPIが1本使えるから、それでゴニョゴニョ……


 今回は、ピーク値の時刻はプログラムが起動してからの経過時間で計測しているけど、別の信号源(e.g. GPS PPS)でも同様に経過時間を計測して、そこからピーク値の絶対時刻を決める、みたいなこともできるはず。PPSで正秒を決めて、それ以上の曖昧さはUARTからの信号で解決するとか。

 ただ、STM32F303K8には32bitタイマが1本しか乗っていないので、精密な時間管理をやろうとするとこれがネックになる。16bitタイマでPPSを計測すると20usくらいの時間分解能になるから、PPSに比べて3-4桁悪くなる。


0 件のコメント:

コメントを投稿