ESP32[5] タイマー割込

ある程度複雑なプログラムを書くとなると、どうしても割込という機能が欲しくなってきます。割込とはハード的なトリガを受けて、今動いているプログラムを一旦やめて、それぞれの割込みに設定されたプログラムを実行することです。OSが載っている場合は、OSも割込みで動いているので、OSの配下の約束で割込を行うことになります。OSがない場合、割り込みベクタに登録したり、少しCPUのハードウエアの知識が必要です。今回は、ESP32の話なので、OSは関係ありません。そのうえ、ESP32の開発環境は割り込みベクタなどのハードを意識する必要がないので普通にCのプログラムを書く感覚で割り込みが実現できます。

割込みは大抵は二種類の割込が欲しくなります。一つは時間を刻むための時間用の割込、もう一つはGPIOが変化したときの割込です。マイコンでプログラムを書く場合は、永久ループの中での処理が殆どなので、そのループの中でGPIOのステータスを確認すること(ポーリング方式)で、割込無しにプログラムを書くことは可能なので、本当に割込が必要かをよく検討することをお勧めします。また、プログラム構造のテクニックになりますが、ループを離れてしまう処理、いわゆるサブルーチン化をしてしまう場合、GPIOのステータスをチェックする間隔が長くなるように作ると、そのチェックまではステータスが反映されません。したがって、ポーリング方式を取る場合はポーリングする間隔をよく考えないと思うような動きをしません。その点、割込はそのようなことを考えなくても済みます。しかし、割込は他のプログラムを止めてしまうので、割込処理には時間がかかるコードを書くのではなく、ステータスの変化状態を確認する程度で抜けるようにしておかないと、ランタイムエラーで悩まされることになります。

割込のうんちくはこのくらいにしておいて実際のESP32の割込の書き方を書いてみたいと思います。まずは、タイマー割込みです。ESP32には4つのタイマーがあります。私はあまり、多くのタイマー割込を使用しません。msec タイマーを作っておきその呼び出し関数内で必要なタイマーを複数カウントするとする方法をよく取ります。

まずは、タイマーをグローバルで初期化します。

hw_timer_t * timer = NULL; //timer 初期化

int msec; //msec カウンター用グローバル変数

タイマー割込み呼ばれる関数を作ります。この関数には、IRAM_ATTRをつけて置くことだそうです。

void IRAM_ATTR msecond() {
  msec++;


  if (msec < 0) msec = 0; // Over flow

}

タイマーの設定とタイマーイネーブルを setup()の中に書きます。

  // msec timer start
  timer = timerBegin(0, 80, true); //timer=1us
  timerAttachInterrupt(timer, &msecond, true);
  timerAlarmWrite(timer, 1000, true); // 1ms
  timerAlarmEnable(timer);
timer = timerBegin(0-3,クロック数, true/false);

true: カウントアップ

false: カウントダウン

普通は true で良いと思います。

timerAttachInterrupt(timerのポインタ, 呼び出す関数のポインタ, true/false);

true: エッジカウント

false: レベルカウント

普通は true で良いと思います。

timerAlarmWrite(timerのポインター, 割り込みを発生されるためのカウント数, true/false);

true: 繰り返し割込み

false: 1ショット

普通は true で良いと思います 。

したがって、上記の例題は、1usecタイマーを作ってカウントアップしていき、1000カウントで割り込みを発生されるので、つまり、1msec 毎に割込みが発生します。

私はloop()の中で色々なカウンタにこれを使います。例えば、1秒経過で何かをしたい場合は、カウントを始める位置に、

xx_timer = 1000;

割込み関数の中に、 xx_timer--;

loop()の中に、

if(xx_timer <=0) {

do_something();

xx_timer = 1000;

}

と書いておけば実質1秒カウンターが作れます。使い方が良いのかどうかわかりませんが、これならタイマーが足りなくなるということはありません。