6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

esp32のtimer割り込みと外部割込みの個人的メモ

Last updated at Posted at 2022-01-25

概要

タイマー割り込みを使おうとしたら、案外一筋縄ではいかなかったのでメモします。
難しいことは一点に限ります

なんかrebootがかかっている!

マルチタスクの処理等の影響かもしれませんが、非常に難しいです。

非常に参考になるサイト

timer関数自体の中身

サンプル1

タイマー割り込みではGiveするのみで、別に回してるタスクで監視する
CPU1で回します。CPU0はdelay(1)を入れないとWDTが起動してrebootします。


hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
volatile uint32_t isrCounter = 0;
volatile boolean flag = false;
volatile boolean flag_change = false;

void IRAM_ATTR onTimer() {
  portENTER_CRITICAL_ISR(&timerMux);
  isrCounter++;
  portEXIT_CRITICAL_ISR(&timerMux);
  xSemaphoreGiveFromISR(timerSemaphore, NULL);
}

void timer_task(void *pvParameters) {
  while (1) {
    if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE) {
      uint32_t isrCount = 0;
      portENTER_CRITICAL(&timerMux);
      isrCount = isrCounter;
      portEXIT_CRITICAL(&timerMux);
      digitalWrite(13, !digitalRead(13));
      flag_change = true;
      //Serial.println(isrCount);
    }
    if (isrCounter % 300 == 0 and flag_change == true) {
      flag_change = false;
      //timerEnd(timer);
      timerWrite(timer, 0);        //タイマー初期化
      timerStop(timer);
      if (flag == true) {
        flag = false;
        timerAlarmWrite(timer, 100, true);            //repeat
        //timerAttachInterrupt(timer,&onTimer,true);
      } else {
        flag = true;
        timerAlarmWrite(timer, 50, true);            //repeat
        //timerAttachInterrupt(timer,&onTimer,true);
      }
      //timerAlarmEnable(timer);
      timerRestart(timer);
    }
  }
}
void setup() {
  Serial.begin(115200);
  delay(50);  // Serial Init Wait
  xTaskCreateUniversal(
    timer_task,
    "task1",
    8192,
    NULL,
    1,
    NULL,
    APP_CPU_NUM
  );

  timerSemaphore = xSemaphoreCreateBinary();

  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 50, true);
  timerAlarmEnable(timer);

  pinMode(13,OUTPUT);
}
void loop() {
  delay(1);
}

#結果

タスク内でpin13を出力を反転させているので、pin13をロジアナで計測した結果を示す。
image.png

タスク内で300回ごとに周期を100usと50usの切り替えをしている。切り替え時の様子

image.png

image.png

50usもまぁまぁ正確です。
切り替え時の間隔は5~10usほど伸びているように見えました。

周期を10usの場合を見ると、かなりきついイメージです。

タスクをもう一つ増やしたところ

image.png

こんな感じで、ちょうど1msタスクが回らない部分が発生しました。

1ms以下に正確に動作してほしい場合esp32は弱いですね。

Timer_ISR内の処理量

どれくらいの処理を入れたらrebootが起こるのか試してみます。

GPIO.out_w1ts
GPIO.out_w1tc

を繰り返してみます。

結果

image.png
100セットぐらいでは特に問題なし。

image.png
300セットまでくると、HIGH,LOWの繰り返しだけで処理時間がかかる
さらに増やすと、次の割り込みまでに処理が終わらないだろう

image.png
800セットまで増やしたところ、間隔が設定の50usから83usに変化した。
これは割り込み中に次の割り込みに飛ばずに、割り込み処理が終わってから、次の処理をしているということだ。


hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
volatile uint32_t isrCounter = 0;
volatile boolean flag = false;
volatile boolean flag_change = false;

void IRAM_ATTR onTimer(){
  portENTER_CRITICAL_ISR(&timerMux);
  isrCounter++;
  portEXIT_CRITICAL_ISR(&timerMux);
  xSemaphoreGiveFromISR(timerSemaphore,NULL);
  for(int i = 0; i<100; i++){
    GPIO.out_w1tc = 1 << 13;     //13pin LOW
    GPIO.out_w1ts = 1 << 13;     //13pin HIGH
  }
}
void setup() {
  pinMode(13,OUTPUT);
  Serial.begin(115200);
  delay(50);

  timerSemaphore = xSemaphoreCreateBinary();

  timer = timerBegin(0,80,true);
  timerAttachInterrupt(timer,&onTimer,true);
  timerAlarmWrite(timer,50,true);
  timerAlarmEnable(timer);

  uint32_t fre= getApbFrequency();

  Serial.println("hello");
  Serial.println(fre);

}

void loop() {
  if(xSemaphoreTake(timerSemaphore,0) == pdTRUE){
    uint32_t isrCount = 0;
    portENTER_CRITICAL(&timerMux);
    isrCount = isrCounter;
    portEXIT_CRITICAL(&timerMux);
    //digitalWrite(13,!digitalRead(13));
    flag_change = true;
    //Serial.println(isrCount);
  }
}

外部割込み+タイマー割り込みでタイマー割り込みの処理時間が長い場合
rebootするかと思ったが、問題なかった

引っ掛かったこと

setup内で
xTaskCreateUniversalでタスクを作った後に、delayを入れるとrebootした

原因

setup内でセマフォの定義をしていた
かつ、定義をdelayの後においていた、
つまり

setup{
 task定義
 delay
 セマフォ定義
}
task{
 セマフォ使用
}

としていた。task内でセマフォの状態を参照していたため
delayを入れると、セマフォの定義が終わる前にtaskが実行され、未定義のセマフォが参照されrebootしていた

対策

delayをtask定義の前に入れるか、削除で解決
タスク定義はできるだけ最後にした方がいいのかな。
マルチタスクの弊害が出てしまった。
今回でだいぶesp32のことを知れた。

おもしれぇやつだな、おめぇ

追記:プログラム解説

setup内

setup 内
void setup(){
  xTaskCreateUniversal(
    timer_task,
    "task1",
    8192,
    NULL,
    1,
    NULL,
    APP_CPU_NUM
  );

  timerSemaphore = xSemaphoreCreateBinary();

  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 50, true);
  timerAlarmEnable(timer);
}
  • セマフォ定義(並列実行プログラム定義)
  • タイマー定義
timerBegin(0, 80, true);  // timer0使用(0~3まで)、分周比80
timerAlarmWrite(timer, 50, true);  //割り込みが発生するカウント数、true:繰り返し実行

この場合は、

割り込み処理内

void IRAM_ATTR onTimer() {
  portENTER_CRITICAL_ISR(&timerMux);  //割込み禁止
  isrCounter++;
  // 自由な処理記述:割り込み処理なので内容は軽く
  portEXIT_CRITICAL_ISR(&timerMux);   //割り込み処理許可
  xSemaphoreGiveFromISR(timerSemaphore, NULL);   //セマフォの何かをpdTRUEに変更
}

並列処理内部、セマフォ内部

void timer_task(void *pvParameters) {
  while (1) {
    if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE) {
      portENTER_CRITICAL(&timerMux);
      isrCount = isrCounter;
      // 自由な処理記述
      portEXIT_CRITICAL(&timerMux);
    }
  }
}

Lチカテスト

#include <Arduino.h>
#include <Ticker.h>

//38kHz 搬送波用タイマー
hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
volatile uint32_t isrCounter = 0;
volatile boolean flag = false;
volatile boolean flag_change = false;

//IRIS OHYAMAリモコン用タイマー
Ticker IR_IRIS;

void timer_task(void *pvParameters) {
  while (1) {
    if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE) {
      uint32_t isrCount = 0;
      portENTER_CRITICAL(&timerMux);
      isrCount = isrCounter;
      digitalWrite(13, !digitalRead(13));
      portEXIT_CRITICAL(&timerMux);
    
    }
  }
}


void IRAM_ATTR onTimer() {
  portENTER_CRITICAL_ISR(&timerMux);
  isrCounter++;
  portEXIT_CRITICAL_ISR(&timerMux);
  xSemaphoreGiveFromISR(timerSemaphore, NULL);
}


void setup() {
  Serial.begin(115200);

   xTaskCreateUniversal(
    timer_task,
    "task1",
    8192,
    NULL,
    1,
    NULL,
    APP_CPU_NUM
  );

  timerSemaphore = xSemaphoreCreateBinary();

  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 100, true);
  timerAlarmEnable(timer);

  
  //IR_IRIS.attach();
  pinMode(13,OUTPUT);

}

void loop() {
  // put your main code here, to run repeatedly:

}

image.png

小さいパルスの幅は100us(10kHz)
image.png

なぜか、割り込み処理が起きていないところがある。
割り込みが早すぎて、セマフォの処理が追いついていないということが原因である。

改良

セマフォ内でdigitalWriteしていたが、割り込み処理内に入れてみる

image.png

正しく動いた

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?