Skip to main content
  1. Posts/

ESP-WROOM-02のTickerを使った割り込み実行

ESP-WROOM-02(ESP8266)では、Tickerという簡易なタイマー割り込みのライブラリがあります。 これは、timer0timer1を使う実装よりも設定できることが少ないですが、実装がかんたんです。 ストップウォッチのような時間計測や、定期的なセンシングやフェッチ処理などに使いやすいです。

詳細な実装例は、ライブラリのExampleから見ることができます。

基本的な使い方 #

基本的な実装の流れは以下のとおりです。

#include "Ticker.h"

Ticker ticker;

void printFromTicker() {
  Serial.println(String("ticker: ") + millis());
}

void setup() {
  Serial.begin(115200);
  Serial.println("Program Start.");

  ticker.attach(1.0, printFromTicker); // 割り込み間隔(float 秒), コールバック関数
}

void loop() {
  Serial.println(String("loop: ") + millis());
  delay(1000);
}

このように、Tickerのインスタンスを宣言したあとは、attachメソッドで割り込み間隔(秒)とコールバック関数をセットしてあげるだけで使うことができます。

割り込み間隔の精度 #

上記の実装のmillis()部分をmicros()に変えると、割り込みの精度がよくわかります。 マイクロ秒の下6桁のレベルで(=1.0秒ぴったりのクロックで)、一定間隔に割り込まれていることがよくわかります。 たまに割り込んだときのmicros()がずれることもありますが、すぐもとに戻るようです。

ということは、現実世界の正確な1.0秒との差は、発振器の精度に依存することになるでしょう。 発振器は26MHz±10ppmで動くものだそうです。 Real Time Clockや一般家庭の時計で使われる発振器が32.768kHzと考えると、実用レベルでは十分な精度だろうと思います。

試しに30分連続して割り込みをさせてみると、シリアルモニターは、以下のようなログになりました。 30分経っても、ほとんどずれていないことがわかります。 「ticker: xxx」の部分は、「秒」にしてあります。左側のタイムスタンプは、ホスト側PCの時刻です。

22:47:01.263 -> ticker: 1
22:48:00.290 -> ticker: 60
22:49:00.301 -> ticker: 120
22:50:00.277 -> ticker: 180
22:51:00.283 -> ticker: 240
22:52:00.283 -> ticker: 300
22:53:00.290 -> ticker: 360
22:54:00.260 -> ticker: 420
22:55:00.269 -> ticker: 480
22:56:00.288 -> ticker: 540
22:57:00.294 -> ticker: 600
22:58:00.273 -> ticker: 660
22:59:00.274 -> ticker: 720
23:00:00.299 -> ticker: 780
23:01:00.286 -> ticker: 840
23:02:00.307 -> ticker: 900
23:03:00.286 -> ticker: 960
23:04:00.261 -> ticker: 1020
23:05:00.270 -> ticker: 1080
23:06:00.267 -> ticker: 1140
23:07:00.281 -> ticker: 1200
23:08:00.289 -> ticker: 1260
23:09:00.288 -> ticker: 1320
23:10:00.274 -> ticker: 1380
23:11:00.287 -> ticker: 1440
23:12:00.298 -> ticker: 1500
23:13:00.304 -> ticker: 1560
23:14:00.305 -> ticker: 1620
23:15:00.307 -> ticker: 1680
23:16:00.292 -> ticker: 1740
23:17:00.290 -> ticker: 1800
23:18:00.291 -> ticker: 1860
23:19:00.278 -> ticker: 1920
23:20:00.292 -> ticker: 1980
23:21:00.281 -> ticker: 2040

ストップウォッチとしても十分使えそうですね。

使えるメソッド #

GitHubにあるTicker.hのソースを見ながら、パブリックメソッドを覗いてみます。

  • void attach_scheduled(float seconds, callback_function_t callback)
  • void attach(float seconds, callback_function_t callback)
  • void attach_ms_scheduled(uint32_t milliseconds, callback_function_t callback)
  • void attach_ms_scheduled_accurate(uint32_t milliseconds, callback_function_t callback)
  • void attach_ms(uint32_t milliseconds, callback_function_t callback)
  • void attach(float seconds, void (*callback)(TArg), TArg arg)
  • void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)
  • void once_scheduled(float seconds, callback_function_t callback)
  • void once(float seconds, callback_function_t callback)
  • void once_ms_scheduled(uint32_t milliseconds, callback_function_t callback)
  • void once_ms(uint32_t milliseconds, callback_function_t callback)
  • void once(float seconds, void (*callback)(TArg), TArg arg)
  • void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)

こうしてみると、いくつかのパターンに分かれているようです。

attach once
サフィックスなし 即実行 一度限り、即実行
_ms 即実行(ミリ秒指定) 一度限り、即実行(ミリ秒指定)
_scheduled loop()終了後に実行 一度限り、loop()終了後に実行
_ms_scheduled loop()終了後に実行(ミリ秒指定) 一度限り、loop()終了後に実行(ミリ秒指定)
_ms_scheduled_accurate delay()やyield()の後で実行(ミリ秒指定)かな? (なし)

attach と once #

  • attachを使うと繰り返しの割り込み
  • onceを使うと、1回限りのタイマー

「msなし」と「ms」あり #

  • msなしのメソッドは、引数で渡す割り込み間隔を秒で指定する
  • msありのメソッドは、割り込み間隔をミリ秒で指定する

「scheduledなし」と「scheduled」と「scheduled_accurate」 #

  • scheduledなしの方は、割り込みイベントが発生したらすぐに実行
  • scheduledありの方は、割り込みイベントが発生後、loop()の後で実行
  • scheduled_accurateは、割り込みイベント発生後、yield()の後で実行とのこと

scheduledの挙動 #

loop()内にあるdelay(1000)を、3回にしてみます。

#include "Ticker.h"

void printFromTicker() {
  Serial.println(String("ticker fired.") + millis());
}

Ticker ticker;

void setup() {
  Serial.begin(115200);
  Serial.println("Program Start.");
  ticker.attach_scheduled(1.0, printFromTicker);
}

void loop() {
  Serial.println("--- loop start ---");
  delay(1000);
  delay(1000);
  delay(1000);
  Serial.println("--- loop end ---");
}

すると、割り込みの実行が、3秒に1度、3回まとめて実行されるようになりました。
「loop end」から「loop start」の間に実行されていることがわかります。

23:30:55.608 -> --- loop end ---
23:30:55.608 -> ticker fired.3070
23:30:55.608 -> ticker fired.3070
23:30:55.608 -> ticker fired.3070
23:30:55.608 -> --- loop start ---
23:30:58.596 -> --- loop end ---
23:30:58.596 -> ticker fired.6070
23:30:58.596 -> ticker fired.6070
23:30:58.596 -> ticker fired.6070
23:30:58.596 -> --- loop start ---
23:31:01.608 -> --- loop end ---
23:31:01.608 -> ticker fired.9071
23:31:01.608 -> ticker fired.9071
23:31:01.608 -> ticker fired.9071
23:31:01.608 -> --- loop start ---
23:31:04.584 -> --- loop end ---
23:31:04.584 -> ticker fired.12071
23:31:04.584 -> ticker fired.12071
23:31:04.584 -> ticker fired.12071
23:31:04.621 -> --- loop start ---
23:31:07.592 -> --- loop end ---
23:31:07.592 -> ticker fired.15071
23:31:07.592 -> ticker fired.15071
23:31:07.592 -> ticker fired.15072
23:31:07.592 -> --- loop start ---

ms_scheduled_accurateの挙動 #

scheduledのときと少し変えて、delay(1500)を2回実行してみます。

#include "Ticker.h"

void printFromTicker() {
  Serial.println(String("ticker fired.") + millis());
}

Ticker ticker;

void setup() {
  Serial.begin(115200);
  Serial.println("Program Start.");
  ticker.attach_ms_scheduled_accurate(1000, printFromTicker);
}

void loop() {
  Serial.println("--- loop start ---");
  delay(1500);
  delay(1500);
  Serial.println("--- loop end ---");
}

すると、1回目のdelay(1500)のあとに1回と、 2回目のdelay(1500)のあとに2回まとめて実行されていることがわかります。

実行タイミングは、loop()の途中でも実行されている点が、scheduledと異なります。 また、delayが終わるのを待っている点は、サフィックスなしの「attach」や「once」とは異なります。 _ms_scheduled_accurateは、即時実行ではない点scheduledですが、よりaccurate(正確)ということなのでしょう。

00:07:19.510 -> ticker fired.12071
00:07:19.510 -> ticker fired.12071
00:07:19.510 -> --- loop end ---
00:07:19.510 -> --- loop start ---
00:07:21.012 -> ticker fired.13571
00:07:22.524 -> ticker fired.15072
00:07:22.524 -> ticker fired.15072
00:07:22.524 -> --- loop end ---
00:07:22.524 -> --- loop start ---
00:07:24.008 -> ticker fired.16572
00:07:25.540 -> ticker fired.18072
00:07:25.540 -> ticker fired.18072
00:07:25.540 -> --- loop end ---
00:07:25.540 -> --- loop start ---
00:07:27.038 -> ticker fired.19572
00:07:28.509 -> ticker fired.21072
00:07:28.509 -> ticker fired.21072
00:07:28.509 -> --- loop end ---
00:07:28.509 -> --- loop start ---
00:07:30.035 -> ticker fired.22573
00:07:31.510 -> ticker fired.24073
00:07:31.510 -> ticker fired.24073
00:07:31.510 -> --- loop end ---
00:07:31.510 -> --- loop start ---

1.5秒に1度になりました。

Tickerを止めるには #

detach()を呼びます。

ticker.detach();

ウォッチドッグタイマーを作りたい場合は、detachしたあとにattachonceを再セットしてあげると良さそうです。 timer0timer1のようなwrite()メソッドがないので、こうするしかなさそうです。


まとめ #

ESP-WROOM-02(ESP8266)のタイマー割り込みで使えるTickerライブラリの動きを見てみました。 ほんの数行の実装で、定期的な処理を、シンプル・かんたんに実装することができます。 分周などを計算しなくても良いので、直感的に読み書きできるところも良かったです。