-
Notifications
You must be signed in to change notification settings - Fork 0
TimeoutTimer
タイムアウトタイマー支援ツール。
reduceAVR は未対応
このライブラリの最も主要な機能は、タイムアウトブロックである。
#include <TimeoutTimer.h>
TIMEOUT_BLOCK(1000) {
while (true) {
/* 無限ループ等 */
}
}
TIMEOUT_BLOCK
は指定ミリ秒を経過してなおブロック内のコードが実行中であるなら、
それを強制中断してブロックの外に処理を移す。
ブロック内で状態変数を管理し、自身でタイムアウト脱出を判定する必要はない。
従って単純な無限ループでも経過時間が過ぎれば処理を中断させることができ、
記述を単純化できる。
1. TIMEOUT_BLOCK
内での変数書換はブロック外へ伝播されずに破棄される可能性が高い。
これを避けるには変数にvolatile
宣言を付加しておく。
2. タイムアウトはミリ秒で指定するが、計時器精度に伴う誤差がある。 丸めの方向によって最大時間にも解釈されるから最小 2 は指定しなければならない。 一方で指定可能な最大値は 63998 である。
16bit幅ミリ秒指定の最大有効値は
__TIMEOUT_MILLIS_MAX
マクロ定数で参照できる。
3. TIMEOUT_BLOCK
内で全体割込を禁止すると、その間のタイムアウト割込は抑制される。
無限ループ内で割込禁止にすると抜け出せなくなるので注意。
また割込禁止のままブロックを抜けると以後の動作も保証できない。
従ってsei
cli
を直接使うのではなく、
割込禁止対象はATOMIC_BLOCK
で囲むようにすべきだ。
4. TIMEOUT_BLOCK
を再入すると、内側ブロック開始時に外側ブロックの計時は一時停止する。
これらの詳細と解説はTaskChangerサンプルを参照のこと。
各関数は namespaceTimeoutTimer
空間にある。
このライブラリはRTC
周辺機能を使用し
ISR(RTC_CNT_vect)
割込ベクター/ハンドラを専有する。
依存性:<setjmp.h>
このマクロを事前に宣言すると暗黙のTimeout::begin
実行開始が抑止される。
setup
内で明示的にTimeout::begin
が呼ばれるまで
計時動作は開始されず、他の機能も動作しない。
RTC計時器の時間粒度を指定するマクロ定数。 既定値は 1024 (1024Hz)。
<TimeoutTimer.h>
ライブラリをインクルードする前に
これを再定義すると計測時間粒度を変更できる。
指定可能なのは 512、1024、2048 (単位は Hz)の三種。
通常は既定値のままでよいだろう。
- 512Hzにすると約1953us粒度に精度が下がるが、8388608秒(約97日)までの計時が可能になる。
- 2048Hzにすると約488us粒度に精度が上がるが、2097152秒(約24日)までしか計時ができない。
-
delay_timer
や{interval_check_timer`の指定可能値も 31266(ms)まで低下する。
-
16bit幅ミリ秒指定の最大有効値は
__TIMEOUT_MILLIS_MAX
マクロ定数で参照できる。
16bit幅ミリ秒指定の最大有効値を示す。
現在のTIMEOUT_BLOCK
を中断し、ブロック外に抜ける。
TIMEOUT_BLOCK(1000) {
/* STUB */
TimeoutTimer::abort(); /* 途中で中断 */
/* STUB */
}
脱出できるTIMEOUT_BLOCK
は1段だけであり、複数段を一回で抜けることは出来ない。
TIMEOUT_BLOCK
を再入している場合、外側のタイムアウト計時が中断点から再開する。
TIMEOUT_BLOCK
の外でこれを実行すると予測できない結果を招く。多くはリセットが掛かる。
TIMEOUT_BLOCK
内で、タイムアウトまでの残数を取得できる。単位は 計時器精度。(約976us)
取得値をミリ秒に換算するには timeout_ticks_to_millis
マクロを、
マイクロ秒に換算するには timeout_ticks_to_micros
マクロを利用できる。
TIMEOUT_BLOCK(1000) {
while (true) {
uint16_t timeleft = TimeoutTimer::time_left();
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
Serial.println(timeleft, DEC);
}
}
}
取得時間は目安であって運用時の CPU動作クロックや周辺環境(気温等)によっても変動する。
計時器の継続計数を取得する。 単位は 計時器精度(約976us)で 32bit幅の有効範囲を持つ。 従って 4194304秒(約48.5日)でこの計数は一巡する。 元期は MCUリセット直後からである。
取得値をミリ秒に換算するには timeout_ticks_to_millis
マクロを、
マイクロ秒に換算するには timeout_ticks_to_micros
マクロを利用できる。
uint32_t tc, ms, us;
tc = TimeoutTimer::ticks_left();
ms = timeout_ticks_to_millis(tc); // TimeoutTimer::millis_left() 取得結果に同じ
us = timeout_ticks_to_micros(tc); // TimeoutTimer::micros_left() 取得結果に同じ
計時器の継続計数をミリ秒換算で取得する。
計時開始から 4194304000 カウント経過(約48.5日)でゼロに丸められる。
いわゆる millis
関数の代替。
32bit幅いっぱいで溢れる前に丸めが発生することに注意。 ある時間差を求めたい場合は ticks 計数値を直接加減算し、比較判定の換算は最後にすべきである。
計時器の継続計数をマイクロ秒換算で取得する。
これはおおむね 268秒相当で丸められる。
いわゆる maicros
関数の代替。
便宜上用意されているが時間粒度が荒くかつ計数溢れと丸めが早いため目安にしかならないことに注意。
指定計数_ticks
のあいだ処理進行を遅滞させる。いわゆるdelay
関数の代替。
ミリ秒から指定計数値を得るにはtimeout_millis_to_ticks
マクロが使用できる。
直接ミリ秒を指定可能なdelay_timer
マクロも用意されている。
指定可能な最大値は 63998 (ms)である。(16bit幅)
TimeoutTimer::delay_ticks( timeout_millis_to_ticks(1000) );
// または短縮マクロで
delay_timer(1000);
// または(他のAPIを同時に組み込んでいなければ)
delay(1000);
ライブラリをインクルードした時点で従前の Macro API
delay()
は、このマクロに交換される。
delayMaicroseconds()
を代替する機能はない。
なお遅延待機中は(TIMEOUT_BLOCK
の外であれば)yield()
関数が継続して実行されるので、
これを用いると 協調的マルチタスク を実現できる。
詳細は
<TaskChanger.h>
を参照のこと。
指定計数_ticks
のあいだ CPUを休止状態に置く。
指定計数経過か、他の割込によって休止状態は解除される。
ミリ秒で直接指定するにはsleep_cpu_timer
マクロが使用できる。
set_sleep_mode(SLEEP_MODE_STANDBY);
sleep_enable();
TimeoutTimer::sleep_cpu_ticks( timeout_millis_to_ticks(10000) );
// または
sleep_cpu_timer(10000);
この関数はTIMEOUT_BLOCK
の中で使うことが出来ない。その機能を停止する。
休止中は計時が停止する。休止中の実経過時間を取得することはできない。
休止を終了させた条件を知ることは出来ない。これは他の割込ベクタで調べる必要がある。
休止時間は概ねの目安であって精度の保証はない。休止中の正確な経過時間を得るには外部RTCを使用のこと。
時間間隔_interval
が経過するたびに真を返し、指定の保持カウンタ変数_ticks
を更新する。
時間間隔_interval
をミリ秒で直接指定するにはinterval_check_timer
マクロが使用できる。
指定可能な最大値は 63998 (ms)である。
uint32_t _check1 = TimeoutTimer::ticks_left();
uint32_t _check2 = _check1;
while (true) {
// if ( TimeoutTimer::interval_check_ticks( _check1, timeout_millis_to_ticks(30) ) )
if ( interval_check_timer( _check1, 30 ) ) digitalWriteMacro(LED_BUILTIN, TOGGLE);
if ( interval_check_timer( _check2, 31 ) ) digitalWriteMacro(LED_BUILTIN, TOGGLE);
}
殆どの周期的逐次処理はこの機能で記述できるだろう。
実際の確認周期より短い計数を指定すると、常に真を返す。(内部計数が溢れるまで)
保持変数の初期値が現在計時値より小さい場合、それに追いつくまで繰り返し真を返す。
マクロでない方のinterval_check_ticks
の第2引数は
省略したなら 0 指定であると解釈される。
これは計数値が溢れるまで(符号付32bit変数比較なので最大約24日)いちど真になった状態を変化させない。
従って単発の時間経過を知るには、次のように書き下すことができる。
/* 10秒経過後から真となる計数比較 */
uint32_t _check = TimeoutTimer::ticks_left() + timeout_millis_to_ticks(10000);
while (!TimeoutTimer::interval_check_ticks(_check)) {
TIMEOUT_BLOCK(1000) {
while (true) {
/* STUB */
}
}
}
RTC計時器を開始または停止する。
停止時には現在の計数値を返すので、
計時再開時にこれを_ticks
で渡して以前の状態から継続することができる。
これらはsleep_cpu_ticks
内で使用されている。
/* RTC計時器停止 */
uint32_t _save_ticks = TimeoutTimer::end();
/* RTC周辺機能を使用する他の処理... */
/* RTC計時器再開 */
TimeoutTimer::begin( _save_ticks );
停止中は
TIMEOUT_BLOCK
等を使用できない。その中でも使えない。
RTC_CNT_vect
割込ベクタは開放されず再定義も出来ないことには注意。
RTC計時器とその割込は使用するが PIT周期計時器とその割込は使用しないため、 利用者は自由に活用して構わない。 両者は独立して機能する。
#include <avr/io.h>
loop_until_bit_is_clear(RTC_PITSTATUS, RTC_CTRLBUSY_bp);
RTC_PITINTCTRL = RTC_PI_bm;
RTC_PITCTRLA = RTC_PITEN_bm | RTC_PERIOD_CYC32768_gc;
ISR(RTC_PIT_vect) {
RTC_PITINTFLAGS = RTC_PI_bm;
}
RTC_CTRLA 等の PIT 周辺機能制御以外のレジスタには触らないこと。
Twitter(X): @askn37
BlueSky Social: @multix.jp
GitHub: https://github.com/askn37/
Product: https://askn37.github.io/
Copyright (c) 2022,2023 askn (K.Sato) multix.jp
Released under the MIT license
https://opensource.org/licenses/mit-license.php
https://www.oshwa.org/
multix.jp/てくにかるむ(休眠中)
Multix Zinnia Product SDK [*AVR]
AVR.JP(日本語訳)
AVR-LIBC(日本語訳)