3.3. 仮想タイマー

タイミング のページでは、タイムスタンプと time.ticks_diff() によって駆動されるノンブロッキングループを扱いました。仮想タイマー はその裏返しです。ソフトウェアがランタイムに対して、ある関数をスケジュールに従って呼び出すよう要求し、ランタイムはスクリプト自身がクロックをチェックすることなくコールバックを呼び出します。

3.3.1. machine.Timer クラス

machine.Timer は仮想タイマーを構築します。ポート間で共通の動作には特別な id -1 が必要です。それ以外のすべて、すなわち period、mode、callback はキーワード引数を通じて設定されます。

from machine import Timer

def tick(t):
    print("tick")

tim = Timer(-1)
tim.init(period=100, callback=tick)   # 10 Hz

コールバックは通常の関数呼び出しと同じように呼び出されます。オブジェクトの確保、print()、その他のライブラリコードの呼び出しを、特別な制約なしに行えます。

3.3.2. 周期動作とワンショット

2 つのモードが利用できます。

  • Timer.PERIODIC(デフォルト)。コールバックは period ミリ秒ごとに、deinit() が呼ばれるかタイマーが再初期化されるまで、永久に発火します。

  • Timer.ONE_SHOT。コールバックは init()period ミリ秒後に一度だけ発火し、その後タイマーは停止します。

Timer(-1).init(mode=Timer.ONE_SHOT, period=2000, callback=fire)

deinit() は周期タイマーを停止し、保留中のコールバックをキャンセルします。

tim.deinit()

3.3.3. タイマーを使うべきとき

仮想タイマーと、タイミング で説明した ticks_diff() のポーリングパターンは、同じ問題を逆方向から解決します。ポーリングするループは毎回クロックをチェックし、十分な時間が経過したときに動作します。タイマーはランタイムに対して、十分な時間が経過したときにスクリプトを起こすよう要求します。

  • ポーリング ticks_diff。すべてが 1 か所にまとまります。ループがタイミングを所有し、把握すべき余分なコールバックがありません。複数の時間駆動タスクを結びつける、短く明確に定義された処理に最適です。

  • 仮想タイマー。 スケジュールをループ本体の外へ移します。周期的なタスクがメインの流れから独立している場合(ハートビート LED、周期的なセンサーサンプリングなど)に最適で、メインループがその時間を他の処理に使えるようにします。

どちらのアプローチも同じ基盤となるクロックを使い、1 ミリ秒以上の周期では同じ精度を与えます。どちらも正確なピンのトグルやサブミリ秒の波形生成には適していません。コールバックのレイテンシはスケジューラのティックのオーダーであって、ナノ秒ではないからです。