お次は純然たるタイマーです。
ESP32 は1つのグループ内にタイマーを2つ持ち、更にそのグループが2つあります。よって4つのタイマーを利用可能です。
プリスケーラは 16bit, そしてカウンタはなんと 64bit です。すごいですねぇ〜〜
ソースコード
取り敢えず先に載せておきます。若干独特。
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/timer.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
void timer_isr(void *parameters) {
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
timer_group_enable_alarm_in_isr(TIMER_GROUP_0, TIMER_0);
ets_printf("Hey\n");
}
void hwtimer_init(void) {
// Timer Clock Source: APB(80MHz)
timer_config_t config = {
.alarm_en = TIMER_ALARM_EN,
.counter_en = TIMER_PAUSE,
.intr_type = TIMER_INTR_LEVEL,
.counter_dir = TIMER_COUNT_UP,
.auto_reload = TIMER_AUTORELOAD_EN,
.divider = 80
};
timer_init(TIMER_GROUP_0, TIMER_0, &config);
timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 1000 * 1000);
timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_isr, NULL, 0, NULL);
timer_enable_intr(TIMER_GROUP_0, TIMER_0);
timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0);
timer_start(TIMER_GROUP_0, TIMER_0);
}
void app_main(void) {
hwtimer_init();
while(1) {
vTaskDelay(1);
}
}
設定
では簡単に述べます。そこまで難しくはないです。
構造体による初期化
例によって初期化には構造体を用います。それに続くコードを見ればお分かりでしょうが、案外それ以降の設定は関数を呼んでますね。独特なポイントです。
割り込みはコンペアマッチのみで、これを Alarm と呼んでいます。
// Timer Clock Source: APB(80MHz)
timer_config_t config = {
.alarm_en = TIMER_ALARM_EN,
.counter_en = TIMER_PAUSE,
.intr_type = TIMER_INTR_LEVEL,
.counter_dir = TIMER_COUNT_UP,
.auto_reload = TIMER_AUTORELOAD_EN,
.divider = 80
};
timer_init(TIMER_GROUP_0, TIMER_0, &config);
特にどうってこと無いコードですが、強いて言うならクロック源が 80MHz であることですかね。.divider
を 80 とすることによって、カウンタに入力されるクロック周波数は 1MHz となります。
また、.counter_en
を TIMER_PAUSE
にすることで、「まだ動くな」と指示しています。
timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, 1000 * 1000);
timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_isr, NULL, 0, NULL);
timer_enable_intr(TIMER_GROUP_0, TIMER_0);
timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0);
timer_start(TIMER_GROUP_0, TIMER_0);
具体的な設定です。timer_set_alarm_value
は割り込みを入れるコンペアマッチ値を指定します。
timer_isr
は割り込み関数ですね。後はカウンタをリセットしてスタートするだけです。なんと楽ちん。
割り込み関数内で必要な処理
地味に独特で、ちょっとハマっていました。ってかこの処理 IDF 側がやってくれよ、と思わなくもない。
void timer_isr(void *parameters) {
timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0);
timer_group_enable_alarm_in_isr(TIMER_GROUP_0, TIMER_0);
ets_printf("Hey\n");
}
まず、ステータスのフラグをクリアする必要があります。そこ手動なのかい。そして割り込み関数内だとコードのように timer_group_clr_intr_status_in_isr
を呼ぶ必要があります。(ってかむしろ、割り込み関数外だとレジスタを手打ちする必要があるらしい。なんじゃそりゃ〜〜)
そしてお次は、タイマーの仕様で、アラーム割り込みが入ったら次のアラーム割り込みが自動的に無効化されるということに注意です。ですので、何処かで再度有効化する必要があります。こちらも割り込み関数内で有効化しました。
〆
ということで、若干ハマりかけましたが割と素直なタイマーでした。
結構簡単に動いてくれましたね。