MicroPython的午睡(86) STM32版、タイマ・チャネル、割り込み受け TIPS

Joseph Halfmoon

前々回前回とTimer1のチャネル機能、入力キャプチャと出力コンペアを使ってみました。しかし、入力キャプチャのタイミングを割り込み受けしておらずポーリング。そこで今回はチャネルからの割り込みをハンドリングしてみたいと思います。ま、ね、MicroPythonの割り込みハンドラの書き方を調べとけ、ということだったですが。

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

漠然と割り込みハンドラ書いたらエラーになった件

最初、「callbackパラメータに適当な関数を渡せばよいんでしょ」くらいの理解で以下のようなハンドラを書いて起動したら見事にエラーとなりました。

問題のハンドラが以下に。IntrErrorCode

そしてそのときのエラーが以下に。”uncaught exception in Timer” と新年そうそう、禍々しいっす。IntrErrorEC

 

現象としては以下のごとし。

    1. タイマチャネル側のcallback関数に設定しているけれども。割り込み時に引数として渡されるのはタイマチャネル・オブジェクトではなく、タイマ・オブジェクトである(マニュアルよく読めばそう書いてあるけれど。)
    2. 割り込みハンドラの内部でキャプチャ値をprintするだけなら正常に動作する(上記のコードでは#でコメントアウトされている部分だけなら動作OK。つまり割り込みハンドラは呼ばれている。)
    3. 割り込みハンドラの内部でoldV, newVなる「大域変数」にキャプチャ値を代入しようとするコードだとエラーになる

これは何か「割り込みハンドラ」特有の「ケア」をしないとダメだと思い至りました。

MicroPythonにおける割り込みハンドラの記述法

いつものごとく泥縄。調べてみればちゃんと割り込みハンドラの記述方法に関するドキュメントがありました。以下に。

Writing interrupt handlers

まあ、上記ドキュメントを読みつつ改良したのが以下2点です。

    1. 念のために、alloc_emergency_exception_bufを設定しておく(エラーのときに解析しやすいみたい。知らんけど。)
    2. 割り込みハンドラと「表の」MicroPythonコードで共有する変数は単純な大域変数をやめ、uarray.array型を使用する。

上記にて動作OKとなりました。結局、2がポイントかと。単純な変数といいつつ、MicroPythonの場合メモリをアロケートしたりするからか。その点、uarray.array型であれば、「静的」に領域確保されるので割り込みハンドラでもOK。あたりまえか。

前々回のインプット・キャプチャのテスト用のコードを「割り込み受け」に変更したソースが以下に。

#STM32: Timer 1 IC Test w/Callback function
#Timer1 CH1 ... PA8(D7)
import pyb
import machine
import time
import micropython
import uarray

micropython.alloc_emergency_exception_buf(100)

capV = uarray.array("i", [-1] * 2)

def ch1Intr(tim):
    capV[0] = capV[1]
    capV[1] = icCH.capture()

tim = pyb.Timer(1, prescaler=1023, period=0xffff, callback=None)
icCH = tim.channel(1, pyb.Timer.IC, polarity=pyb.Timer.RISING, pin=machine.Pin.board.D7,callback=ch1Intr)

def enCh1Intr(timCH):
    capV[0] = -1
    capV[1] = -1
    timCH.callback(ch1Intr)

def disCh1Intr(timCH):
    timCH.callback(None)

def main():
    print("STM32F401RE Timer 1 IC Test w/Callback.")      
    print("Source  Freq: {0} Hz".format(tim.source_freq()))
    print("Timer 1 Freq: {0} Hz".format(tim.freq()))
    while True:
        enCh1Intr(icCH)
        while capV[0] < 0:
            pass
        disCh1Intr(icCH)
        itvl = capV[1]-capV[0]
        if itvl > 0:
            print("Diff: {} Freq: {} [Hz]".format(itvl, (0x10000/itvl)*tim.freq()))
        time.sleep(1)

if __name__ == "__main__":
    main()
実機上で実行確認

上記コードをThonny IDEに接続した Nucleo-F401REボードで実行したところが以下に。なお、入力端子PA8(Arduino式端子名D7)に与えているのは10Hz、オフセット1.5V、振幅1.5Vの方形波であります。TimerCint_Result

予定通り割り込みハンドラが働いて、チャネル1のキャプチャ値の差分を求め、周波数が算出できているみたい。割り込み使う前にハンドラの書き方ページくらい読んどけってこと。トホホ。

MicroPython的午睡(85) STM32版、入力キャプチャから出力コンペア へ戻る

MicroPython的午睡(87) STM32版、入力キャプチャ割り込み受けで出力コンペア へ進む