目次
前
次
ESP32タイマー割込み処理
ESP32のスケッチで、タイマー割込みを扱うとして
MsTimer2をインクルードしても、まともな動作には
なりませんでした。
Arduinoは、AtmelのATmega168をベースに展開して
きたため、ArduinoIDEであっても、EPS32のように
CPUアーキテクチャが異なる場合、ライブラリには
互換性がないと考えてよいでしょう。
いろいろと調べた結果、次のスケッチでタイマー割込み
によるLED点滅を扱うことができました。
#define LED_PINA 2
#define LED_PINB 4
#define OFF 0
#define ON OFF+1
/* initialize timer */
hw_timer_t* xtimer = NULL ;
hw_timer_t* ytimer = NULL ;
boolean aflag ;
boolean bflag ;
byte xcnt ;
byte ycnt ;
void init_tim0();
void init_tim1();
/* interrupt service routine */
void IRAM_ATTR MY_TIM0_INT()
{
aflag = ON ;
}
void IRAM_ATTR MY_TIM1_INT()
{
bflag = ON ;
}
void setup()
{
/* initialize PIN value and direction */
digitalWrite(LED_PINA,LOW);
digitalWrite(LED_PINB,LOW);
pinMode(LED_PINA,OUTPUT);
pinMode(LED_PINB,OUTPUT);
/* clear flags */
aflag = OFF ;
bflag = OFF ;
/* clear counters */
xcnt = 0 ;
ycnt = 0 ;
/* initialize timers */
init_tim0();
init_tim1();
}
void loop()
{
/* timer0 interrupt handing */
if ( aflag == ON ) {
/* clear event flag */
aflag = OFF ;
/* impress */
digitalWrite(LED_PINA,LOW);
if ( xcnt & ON ) { digitalWrite(LED_PINA,HIGH); }
/* update */
xcnt++ ;
}
/* timer1 interrupt handing */
if ( bflag == ON ) {
/* clear event flag */
bflag = OFF ;
/* impress */
digitalWrite(LED_PINB,LOW);
if ( ycnt & ON ) { digitalWrite(LED_PINB,HIGH); }
/* update */
ycnt++ ;
}
}
void init_tim0()
{
/* 80MHz / 80 = 1MHz (1us) */
xtimer = timerBegin(0, 80, true);
/* set period */
timerAlarmWrite(xtimer,250000,true);
/* attach ISR */
timerAttachInterrupt(xtimer,&MY_TIM0_INT,true);
/* enable timer interrupt */
timerAlarmEnable(xtimer);
}
void init_tim1()
{
/* 80MHz / 80 = 500kHz (2us) */
ytimer = timerBegin(1, 160, true);
/* set period */
timerAlarmWrite(ytimer,500000,true);
/* attach ISR */
timerAttachInterrupt(ytimer,&MY_TIM1_INT,true);
/* enable timer interrupt */
timerAlarmEnable(ytimer);
}
ESP32のスケッチを動かせるように、ArduinoIDEに
必要なファイルを追加すると、タイマーを扱うのに
利用する構造体を使えるようになります。
構造体変数を扱うために、データ型hw_timer_tの
利用を宣言しておきます。
構造体変数は、メモリ中のどこかにブロックごとに
確保されるので、NULLポインタを使い、領域だけを
確保。
hw_timer_t* xtimer = NULL ;
hw_timer_t* ytimer = NULL ;
hw_timer_tは、hardware timer typeの略と解釈
すればよいでしょう。
メモリの中に領域を確保して、後から必要な
設定をやっていく手順でいけるよう。
割込みハンドラは、RAMの中に置く仕様なので
次のように、関数名の前にIRAM_ATTRをつけて
割込み発生時に呼び出されることを宣言。
void IRAM_ATTR MY_TIM0_INT()
{
aflag = ON ;
}
void IRAM_ATTR MY_TIM1_INT()
{
bflag = ON ;
}
割込みハンドラは、極力短くしたいので
イベント通知フラグをセットするだけに
しています。
タイマーを使うには、以下の内容を指定。
- どのチャネルを使うのか
- 分周比(プリスケーラ値指定)
- カウンタサイズ(カウンタ最大値指定)
- 割込みハンドラのエントリアドレス
- 割込みイネーブルかディセーブルか
EPS32は、64ビットのタイマーカウンタを
4チャネル持っています。
また、クロックソースは、いくつか選択でき
プリスケーラに接続されています。
ブロック図で見ると、以下。
リストにあるパラメータを設定するのに
次の関数を利用。
timerBegin
timerAlarmWrite
timerAttachInterrupt
timerAlarmEnable
各関数に関して、パラメータを絡めて理解します。
timerBegin
関数名から、タイマーを使うことを指定すると
わかるはず。3パラメータを指定。
- チャネル番号
- プリスケーラ値
- 論理値
戻り値は、構造体領域のエントリーアドレスなので
ポインタに代入。
次のように書けばOK。
xtimer = timerBegin(0, 80, true);
ytimer = timerBegin(1, 160, true);
プリスケーラ値は、ここで指定するので
カウンタ値は、別の関数で設定します。
timerAlarmWrite
関数名から、カウンタ値を指定するのかと推定できます。
3パラメータを指定。
- 構造体変数のエントリーアドレス
- カウンタ値
- 論理値
カウンタは、インクリメントかデクリメントのいずれか。
インクリメントの場合、論理値にtrueを指定。
次の設定では、インクメントで、500000になると
ゼロクリアしてから、またインクリメント動作を
します。
timerAlarmWrite(ytimer,500000,true);
図でみると、以下。
カウンタのインクリメント動作ですが、最大値と
なれば、リセットされるようになります。
カウンタのデクリメント動作は、最大値から0
まで、減少させていき0になると最大値を再度
と設定。
timerAttachInterrupt
タイマー割込みが発生したときに、動かすコード
のエントリアドレスを指定。
timerAttachInterrupt(ytimer,&MY_TIM1_INT,true);
構造体のプロパティの中に、エントリアドレスを
与えるため、割込みハンドラの関数名に、「&」
をつけて、アドレスであることを明記。
timerAlarmEnable
割込みを許可するために、この関数を使います。
パラメータは、構造体変数のエントリアドレス
とします。
timerAlarmEnable(ytimer);
動作確認は、5x5のLEDマトリクスを使いました。
目次
前
次