MicroPython的午睡(80) STM32F401RE版、タイマ駆動でADサンプリング

Joseph Halfmoon

第77回でビルドしたNucleo-F401RE用MicroPython、Pyboard用のpybモジュールを搭載(第78回)。pybモジュールはいろいろ強力な機能を搭載。その一つがタイマ駆動のADサンプリング機能です。Pyboard上では2チャンネルを210kHzでサンプリングできると。結構速いんでないかい。

※「MicroPython的午睡」投稿順 Indexはこちら

MicroPythonのソフトウエアループの中でADコンバータを読み取っていては、サンプリング速度も大したことはなく、またソフトウエアのループなのでサンプリング時刻も頼りありません。そこでタイマ割り込み利用を考えますが、MicroPython上でタイマ割り込みを定義し、そのコールバック関数でAD読み取りをするのは、速度的にはやっぱり大したことできないんでないかい。たぶん10kHzくらいか?上限測ったわけでないケド。

しかし、pybモジュールには、Timerオブジェクトを直接渡して「よきに計らってくれる」 ADC読み出し関数 read_timed ありです。以下そのドキュメントへのリンクです。

クラス ADC — アナログ-デジタル変換

上記ドキュメントによれば、ADC2チャンネル測定で、210kHzでオーバランなしのサンプリングができた(pyboardで)、215kHzでは欠落があったとのこと。MicroPythonでの処理としては出色の速さでないの。F401REは、Pyboardの半分しかメモリを積んでいませんが、クロック周波数は上回ります。ちょっとしたアナログ波形のキャプチャもMicroPythonでできそう。ホントか?

ただ、この関数「ブロッキング」です。バッファが一杯になるまでサンプリングに専念。連続処理ができるわけでないです。ごもっとも。

今回実験に使ったMicroPythonコード

いつものとおりエラーなど何も考えてない実験用コードが以下に。

バッファサイズは後の処理を考えて4096点としてあります。バッファはuarray.arrayのH型(符合無ハーフワード)。ADCは12ビットのハズなので読み取り値をそのまま保持可能。

ADコンバータはA1ピンを入力とし、タイマ4(16ビットのアップダウン可能)をサンプリング用のタイマとしました。設定したサンプリング周波数は控えめに10kHzです。

なお、STM32F401REは多数のタイマを搭載してますが、MicroPythonがどのタイマをどう使っているのか調べてないので(いいかげんな)、適当(ロシアンルーレット方式ともいう)で4番としてます。後で調べないと。

バッファが一杯になったら、オンチップFlashのストレージにテキストで書き出してます。ただし、実際にやってみるとテキスト書き出し、ひどく時間がかかってよくない予感がします。他の方法にした方が良いかもしれません。

#STM32 ADC with Timer
import pyb
import machine
import time
import uarray

def saveBuffer(buf, bufsiz):
    with open('/flash/adcData.txt', 'w') as fw:
        for i in range(bufsiz):
            fw.write(str(buf[i]))
            fw.write("\n")

def main():
    print("Start ADC sampling test.")
    bufsiz = 4096
    buf = uarray.array("H", [0] * bufsiz) # H means unsigned Half word.
    adc = pyb.ADC(machine.Pin.board.A1)
    tim = pyb.Timer(4, freq=10000)
    adc.read_timed(buf, tim)
    saveBuffer(buf, bufsiz)
    print("End of test.")

if __name__ == "__main__":
    main()
実験の準備と実行

今回、以下の別シリーズでNucleo-F446RE用に作成した「アナログフロントエンド」もどきをF401REに転用してます。

手習ひデジタル信号処理

信号電圧を1.65V中心まで持ち上げた後ボルテージフォロワしている回路です。今回、実験用の入力信号の生成には、Digilent Analog Discovery2を使っているので、不要だったかもしれないです。

入力信号の波形が以下に。440Hz、オフセット0V、振幅100mV設定の正弦波であります。これがAFEもどきによりオフセット1.65Vに持ち上げられてA1端子に印加されまする。waveInput

一方、MicroPythonコードの方は、いつものようにThonny IDEから書き込み、そして実行します。このバージョンのMicroPythonの場合、内蔵Flash上のストレージは /flash として見えました。Flash書き込みはかなり遅いのでいやな感じですが、処理が終われば測定結果を格納した adcData.txt ができてます(Filesウインドウは自動更新されないので、手動で右クリックからリフレッシュする必要がありました。)

マイコン側のストレージ上のファイルは右クリックからPC側にダウンロードできるのでPC側に取得します。STM32ADC_Thonny

取り出したファイルの内容は以下のような12ビット範囲の生のAD読み出し整数値が1行1データでならんだものです。

1928
1933
1941
1957
1979
2009
2045
2080
2109

これにFFTかけて、本当に440Hzの信号なのか調べてみようと思います。これにはScilab使わせてもらいます。以下の別シリーズ投稿でFFTのグラフを描けるようにしてあります。なお、上記のファイルをそのままScilabのfscanfMat()関数で読み取ると4096行x1列の配列になるのですが、以下の自作関数は1行x4096列対応なので、読み取った後転置してます。

手習ひデジタル信号処理(60) Scilab、FFT表示用関数「とりあえず版」追加

まずは、素の時間波形(最初の200点)を観察してみます。Y軸はADCの生の値、横軸はサンプル数の成り行き(タイマ指定のサンプリング周波数10kHz)

timeWave

まあ、SIN波には見えるわな。

FFT(サンプリング周波数10kHz、ハミング窓指定)の結果は以下に。FFT

だいたい440Hz付近なんでないの。ツッコミどころがありそうだが。

MicroPython的午睡(79) STM32F401RE版、dhtモジュールを試す へ戻る

MicroPython的午睡(81) STM32F401RE版、使われているタイマを調べる へ進む