見出し画像

Arduinoでタスク xTaskCreate()を使う

経緯

  • 前回の記事で、WindowsでFreeRTOSを動かして、Taskの理解を深めたが、今回は、Arduino IDE でTaskを使い、ESP32で動作確認する

  • といっても、今までも、深く考えずに、xTaskCreate()を使ってきた

  • 突如、登場する、他と命名則が異なる xTaskCreate() という関数に怯えていたが

    • FreeRTOSの理解を深めたことで、恐怖心はなくなった

    • xが何を示しているのか、

    • Create Task という順番ではなく、Task Createという順番なのかについては、

    • 以下の記事で書いた

試してみたこと

  • 1秒に1回、loop()関数で、LEDを点灯させる、シリアルに出力する

  • 3秒に1回、Taskでシリアルに出力する

  • シリアルモニターで以下のように、3回loopが表示された後に、1回taskが表示されることを確認

    • Serial.print()がスレッドセーフなのかは確認できていないが、期待通り動いてくれた

21:39:50.112 -> loop
21:39:51.152 -> loop
21:39:52.190 -> loop
21:39:52.494 -> task
21:39:53.266 -> loop
21:39:54.307 -> loop
21:39:55.350 -> loop
21:39:55.518 -> task

実際のコード全文

#define LED_BUILTIN 2
#define INTERVAL_LOOP 1000
#define INTERVAL_TASK 3000

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

  pinMode (LED_BUILTIN, OUTPUT);

  xTaskCreate(my_task, "my_task", 2048, NULL, 1, NULL);
}

void loop() {
  Serial.println("loop");

  analogWrite(LED_BUILTIN, 30);
  delay(50);
  analogWrite(LED_BUILTIN, 0);

  delay(INTERVAL_LOOP);
}

void my_task(void *arg) {
  while (1) {
    Serial.println("task");
    vTaskDelay(INTERVAL_TASK / portTICK_RATE_MS);
  }
}

xTaskCreate()関数の引数と戻り値

  • pvTaskCode: タスクのエントリーの関数のポインタ

  • pcName : デバッグ用に使う名前(カーネル側では利用しない)

  • usStackDepth: タスクがスタック領域で使う容量(スタックが32ビットの場合、400を設定すると、 1600 bytesが割り当てられる)

  • pvParameters: タスクのエントリーの関数に渡す変数へのポインタ、使わないときはNULLにしてよい

  • uxPriority: タスクの優先順位

  • pxCreatedTask: タスクを操作するための変数(タスクを終了するときなどに使う)、使わないときはNULLにしてよい

  • 戻り値: 成功するとpdPASSが返り、失敗するとerrCOULD_NOT_ALLOCATE_REQUIRED_MEMORYが返る

 BaseType_t xTaskCreate(    TaskFunction_t pvTaskCode,
                            const char * const pcName,
                            configSTACK_DEPTH_TYPE usStackDepth,
                            void *pvParameters,
                            UBaseType_t uxPriority,
                            TaskHandle_t *pxCreatedTask
                          );

参考にしたサイト

気づいたこと

  • Arduinoのフレームワークを使って、タスクを使いときは、vTaskStartScheduler()を実行する必要がない

    • フレームワーク側でタスク関連の処理をしてくれていると推測

  • analogWrite()をloop()とmy_task()の両方で操作しようとすると正しく動作しない

    • スレッドセーフなのではないと思われるので、セマフォなどを使う必要がありそう

  • M5 Stick Cの参考コードを見ると、何もしていないloop()関数で、 `vTaskDelay(1000 / portTICK_RATE_MS);` と書いている

    • そうしないと、メインタスクが、CPUサイクルの半分を消費することになると書かれている

    • loop()を使わないときは注意する

  • Arduino IDEでは、includeなどが不要で、簡単に実装できる一方で、隠蔽されたことにより、逆に混乱することもある

    • ESP IDFで実験することも検討する

次回

  • Simulatorの簡単なサンプルに含まれていなかったイベント関連の処理をFreeRTOS Windows Simulatorで確認してみる


この記事が気に入ったらサポートをしてみませんか?