ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【ESP32】マルチタスク中におけるdelayについて

rikoubou.hatenablog.com

 前回取り上げたマルチタスクの続きです。ESP32のタスクの中でミリ秒単位やマイクロ秒単位でのdelayを行う方法について色々わかったので記録しておきます。

 結論から言うと、実機で検証した結果マルチタスク中ではdelay、delayMicroseconds、microsといったArduinoでの時間を扱う関数が無効になるので代替関数を使用しなくてはならない」というものです。


1:ESP32で動いているOS
 前回のマルチタスクの記事の参考記事にあるように、ESP32で動いているOSは「freeRTOS」というOSらしいです。マルチタスクは、ArduinoではなくこのOSに対応する記述をすることで実現しています。
 つまり、マルチタスク中ではfreeRTOSに即した記述をする必要があるのです。
(※上記の理由から、Arduinoのdelayなどの関数がマルチタスク内で無効になっていると考えられます)


2:マルチタスク中におけるdelayの書き方
 では実際にdelayを行う方法です。以下のように「vTaskDelay」関数を使います。

void hoge() {
  Serial.println("hoge");
}

void hello() {
  const portTickType yDelay = 1000 / portTICK_RATE_MS; // 1000ms
  while(1) {
    Serial.println("hello");
    vTaskDelay(yDelay); // delay
  }
}

void hogeTask(void *pvParameters) {
  const portTickType xDelay = 3000 / portTICK_RATE_MS; // 3000ms
  while(1) {
    hoge();
    vTaskDelay(xDelay); // delay
  }
}

void helloTask(void *pvParameters) {
  hello();
}

void setup() {
  Serial.begin(115200);
  xTaskCreate(hogeTask,"hogeTask", 1024, NULL, 1, NULL);   // hogeTaskタスク登録して実行
  xTaskCreate(helloTask,"helloTask", 1024, NULL, 2, NULL); // helloTaskタスク登録して実行
}

void loop() {
}

 上記のプログラムを書き込んでシリアルモニタを立ち上げると、1秒ごとにhello、3秒ごとにhogeが出力されます。

const portTickType yDelay = 3000 / portTICK_RATE_MS; // 3000ms
const portTickType yDelay = 1000 / portTICK_RATE_MS; // 1000ms

 vTaskDelay引数の計算で直接の数値ではなくportTICK_RATE_MSを用いているのは、freeRTOSはどうやらTICKという単位で処理をしているらしく、portTICK_RATE_MSという「1ミリ秒辺りのTICK」の基準を使って正確な値を出すためです。
(※ちゃんと調べてないので合ってるかの確証はありません)


 またミリ秒よりもさらに小さいマイクロ秒単位でのdelayも可能です。vTaskDelayの引数に「小数」を設定しても問題ありません。

 たとえば8マイクロ秒delayさせたい場合は以下のようにします。

vTaskDelay((8 / portTICK_RATE_MS) / 1000); // 8μs delay


 以上がマルチタスク中でdelayを使う方法になります。マルチタスク中にdelayMicrosecondsなどがあってもコンパイルが通るため、実機でこの現象に気づくまでにえらく時間がかかりました。またそれをどうにか解決する方法にたどり着くまでも苦労しました。

 ネットで探してもイマイチ情報がないようなので、この記事が役に立てば幸いです。


■参考記事
なんか作ろうよ ESP-WROOM-32でマルチタスク
FreeRTOS のメモ - Qiita