MSTimer2の話

Arduinoでタイマー割込みを使うプロジェクトをしたときに、はまったのでメモっておく。

Arduinoのライブラリには、タイマー割込みが入っていない。 タイマー割込みを含むハードウェア割込みは、自分でAVRのコードを書くか、他の方が作ったライブラリを使うことになる。

自分でAVRのコードを書いてるほど時間が無かったので、ライブラリを探してみると、MSTimer2っていうライブラリが良さそうなことが分かった。

MsTimer2::set(500, handler);
MsTimer2::start();

こうやって書くだけで500msごとに、割込みハンドラのhandlerを呼ぶことが出来る。 止める時は、普通にstopさせれば良い。

MsTimer2::stop();

簡単だけど、はまるポイント① ここで注意しないといけないのは、割込みハンドラは返り値を持てないということである。 つまり、ハンドラはvoid型で定義しないといけない。

そして、はまるポイント② これはArduinoだけじゃなくて、組み込みシステム全般に言えることだけど、一定時間ごとに割り込むのだから、その割り込む周期よりも割込み処理の処理時間のほうが早くないといけない。 もし、割込み周期よりも割込み処理時間のほうが長く、割込み処理中に割込みが発生した時どうするかをあらかじめ決めていなかったら、挙動が変になったりする。

MSTimer2のライブラリでは、割込みハンドラ内にinterrupt()って書くことで、割込み処理中の割込みを許可するように設定できる。これを設定しておくことで、割込み処理中に割込みが入っても変な挙動になることが無い。

で、本題はここから。 割込み処理が割込み処理時間に比べて短ければ、interrupt()とか書かなくても不安定になることはないはずなのだ。

僕がやっていたプロジェクトでは、タイマー割込みの周期が1秒とか2秒だったりしたので、それまでに処理が確実に終わるだろうと思ってinterrupt()を書いていなかった。 その割込み処理でやっていたのは、int型の数値を3つくらいシリアルで送るっていうコードだけだったから。

実際それで動いてたし、何一つ問題はなかったんだけど、その数値がintの上限を超えることが分かってlong型に変更したときに謎のバグが発生した。

int型からlong型に変えると、Arduinoがフリーズした。

int型からlong型に変えたらシリアル通信にかかる時間が最大2倍くらいにはなるけれど、送信時間が1秒を超えるとは到底思えないので、めっちゃ悩んだけど、割込みハンドラにinterrupt()を入れたら動いた。

良く分かんないけど、interrupt()を割込みハンドラに入れておいたほうが安全