FlexiTimer2の挙動の調査(Arduino)
ちょっとやってみたいことがあるので、Arduinoのタイマー割込みライブラリであるFlexiTimer2の使い方を試していました。何をやりたいのかは、もう少し目処が立ってから記事にしたいと思いますが、Arduinoで二種類のタイマー割り込みを使った機能を作りたいと思っています。
ところで、Arduinoのタイマー割り込みで有名なのはMsTimer2です。私もよく使っていて、だいぶ前の記事でこのタイマーの精度を調べたことがあります。MsTimer2は使い易いのですが、割込み間隔が1ms 単位しか設定出来ないので、これより細かいタイミングを作ることが出来ません。
もっと細かい時間が設定出来るようにするために、MsTimer2の上位互換のライブラリとしてFlexiTimer2があり、割込み周期をより細かく指定出来るようになっています。関数の形式は下記で、
FlexiTimer2::set(unsigned long units, double resolution, void (*f)());
最初の引数(units)には時間を、二番目の引数(resolution)には時間の単位を指定することになっていて、
例えば、FlexiTimer2::set(1, 1.0/3000, IRQ);
とやれば1秒間に3000回の割り込みがかかります。これ、Arduino Playground のFlexiTimer2の解説に書いてある通りで、これ以外の制約について何も書かれていません。ならば、第二引数に 1.0/1000000 と入れると1μs単位の値が設定出来そうな気配です。
とは言ってもArduinoの時間は4μs単位でカウントされているので、そのあたりが限界になる?それと、第二引数のタイプがdouble(倍精度浮動小数点)になっているけど、この型をArduinoで使うのはちょっと違和感があるなー。
なんて思いながら動かしてみると、FlexiTimer2はとんでもない挙動を示しました。
▼オシロで波形を確認しながら

同時にユニバーサルカウンタでパルスの周期を測りました。
▼測定に使ったスケッチ
▼測定結果

これはびっくり。設定値に対して実際に出力される値が一致するのは 40~1000μsの範囲(0.00004~0.001)だけでした。50μsで誤差が多くなっているのは、時間の単位である4μsで割り切れないので、こういう結果になったのだと思います。ちなみに4μの倍数で値を設定すると誤差が少なくなり、24μsあたりが少ない誤差で設定出来る下限でした。
時間が短い方はタイミング的に苦しくなるのでまあこういう結果になっても仕方ないと思います。でも意外だったのは時間が長い方です。1ms以上の値を指定しても全く無視されています。ライブラリの説明にはそんなことは書かれていないのに、この結果はちょっとどうなんだかなーと思います。
▼測定結果のグラフ

設定出来るのは、このグラフに白抜き矢印で「使える範囲」と書いてある領域になります。なおこれは設定単位の話で、これに対する倍率を別途第一引数で指定出来るので、実際には1ms以上の割込み間隔を指定することが出来ます。
FlexiTimer2はMsTimer2の構造を流用して作ったようなことがどこかに書いてあったような気がするので、上限の時間単位が1msになっているのかも知れません。
◆まとめ
ということで、FlexiTimer2の使い方には条件があることが判りました。この記事に書いたような制約があることを理解して使わないと、思わぬトラブルに巻き込まれると思います。 というか、実際に私がトラブルに遭ったので詳しく調べた結果がこの記事です。
こういう制約はライブラリの説明のどこかに書いておいて欲しいです。まあ私が見落としている、あるいは英語の文章の行間が読めなくて、こんな記事を書いてしまっているなら申し訳ないです。
あと、この記事の結果は、ここで示しているスケッチを動かして得られた結果です (@Arduino
UNO 16MHz)。特に割込み処理ルーチン(21~26行)の中に何を書くかで結果が大きく変わってしまうはずなのでご注意下さい。
ところで、Arduinoのタイマー割り込みで有名なのはMsTimer2です。私もよく使っていて、だいぶ前の記事でこのタイマーの精度を調べたことがあります。MsTimer2は使い易いのですが、割込み間隔が1ms 単位しか設定出来ないので、これより細かいタイミングを作ることが出来ません。
もっと細かい時間が設定出来るようにするために、MsTimer2の上位互換のライブラリとしてFlexiTimer2があり、割込み周期をより細かく指定出来るようになっています。関数の形式は下記で、
FlexiTimer2::set(unsigned long units, double resolution, void (*f)());
最初の引数(units)には時間を、二番目の引数(resolution)には時間の単位を指定することになっていて、
例えば、FlexiTimer2::set(1, 1.0/3000, IRQ);
とやれば1秒間に3000回の割り込みがかかります。これ、Arduino Playground のFlexiTimer2の解説に書いてある通りで、これ以外の制約について何も書かれていません。ならば、第二引数に 1.0/1000000 と入れると1μs単位の値が設定出来そうな気配です。
とは言ってもArduinoの時間は4μs単位でカウントされているので、そのあたりが限界になる?それと、第二引数のタイプがdouble(倍精度浮動小数点)になっているけど、この型をArduinoで使うのはちょっと違和感があるなー。
なんて思いながら動かしてみると、FlexiTimer2はとんでもない挙動を示しました。
▼オシロで波形を確認しながら

同時にユニバーサルカウンタでパルスの周期を測りました。
▼測定に使ったスケッチ
/* FlexiTimer2のテストこのスケッチの9行目にいろいろな値を入れてパルスの周期を測定してみました。
* 引数を変えた時のFlexiTimer2の挙動調査スケッチ
*/
#include <FlexiTimer2.h>
void setup() {
pinMode(13, OUTPUT);
IntervalSet(0.000036); // FlexiTimer2の条件を変えて
}
void loop() {
}
void IntervalSet(float data) { // 引数をFlexiTimer2に設定
FlexiTimer2::stop();
FlexiTimer2::set(1, data, IRQ_timer2); // 第二引数の値を変えて測定
FlexiTimer2::start();
}
void IRQ_timer2() { // FlexiTimer2の割込み処理
// PORTB |= B00100000; // sbi
// PORTB &= B11011111; // cbi
digitalWrite(13, HIGH);
digitalWrite(13, LOW);
}
▼測定結果

これはびっくり。設定値に対して実際に出力される値が一致するのは 40~1000μsの範囲(0.00004~0.001)だけでした。50μsで誤差が多くなっているのは、時間の単位である4μsで割り切れないので、こういう結果になったのだと思います。ちなみに4μの倍数で値を設定すると誤差が少なくなり、24μsあたりが少ない誤差で設定出来る下限でした。
時間が短い方はタイミング的に苦しくなるのでまあこういう結果になっても仕方ないと思います。でも意外だったのは時間が長い方です。1ms以上の値を指定しても全く無視されています。ライブラリの説明にはそんなことは書かれていないのに、この結果はちょっとどうなんだかなーと思います。
▼測定結果のグラフ

設定出来るのは、このグラフに白抜き矢印で「使える範囲」と書いてある領域になります。なおこれは設定単位の話で、これに対する倍率を別途第一引数で指定出来るので、実際には1ms以上の割込み間隔を指定することが出来ます。
FlexiTimer2はMsTimer2の構造を流用して作ったようなことがどこかに書いてあったような気がするので、上限の時間単位が1msになっているのかも知れません。
◆まとめ
ということで、FlexiTimer2の使い方には条件があることが判りました。この記事に書いたような制約があることを理解して使わないと、思わぬトラブルに巻き込まれると思います。 というか、実際に私がトラブルに遭ったので詳しく調べた結果がこの記事です。
こういう制約はライブラリの説明のどこかに書いておいて欲しいです。まあ私が見落としている、あるいは英語の文章の行間が読めなくて、こんな記事を書いてしまっているなら申し訳ないです。
あと、この記事の結果は、ここで示しているスケッチを動かして得られた結果です (@Arduino
UNO 16MHz)。特に割込み処理ルーチン(21~26行)の中に何を書くかで結果が大きく変わってしまうはずなのでご注意下さい。
- 関連記事

