[M5Stack] マルチスレッドで並列処理を行う


1.はじめに

M5StackのMPUのESP32には、並列処理を行うために、マルチスレッドという仕組みが用意されています。
自分の勉強用のため、M5StackのExampleのコードを、その内容を噛み砕いて、整理していきます。

2.プログラムの構造

2.1. Arduinoのプログラムの基本構造

Arduinoのプログラムは、void setup()とvoid main()の二つの関数によるプログラム構造となっています。
まずvoid setup()で、初期化処理を行います。例えば、変数、定数の宣言、通信部分の設定などがこのvoid setup()関数によって行われます。
そして、実行したいメインのタスク内容は、void loop()の中に記述します。例えば、圧力センサーの値をモニターに表示するタスクを考えましょう。(1) 圧力センサーより測定データを取得する。(2)単位の変換などのデータ処理を行う。(3)LCDやSerialモニターへの出力する、(4)一定な時間間隔でこれらの処理を繰り返し実行します。などの一連の処理部分をこのvoid loop()に書いておきます。

2.2. マルチスレッド処理のプログラムの構造

マルチスレッドのプログラムは、上記のArduinoの基本プログラムと構造が少し異なります。
まず、void setup()に、xTaskCreatePinnedCore()を用いて、並列処理を行いたいタスクを宣言します。タスク数は単数、複数のどちらでもOKですが、並列処理が目的なので、基本的に複数のタスクを宣言することになります。

そして、void loop()にはマルチスレッド用の内容を記述しません。(マルチタスクではないタスクは記述しても構いません。)その代わりに、void task0(), void task1()のような並列処理するタスクを関数として記述し、各々の関数の中にはwhile文を利用したループ構造とします。そのループの中に並列処理されるタスクの処理内容を記述します。

マルチスレッドを利用する関数

マルチスレッドは、xTaskCreatePinnedToCore()を利用し、引数のルールは下記のようになります。
ESP32の場合、Core数が2なので、引数Core IDは0か1になります。

xTaskCreatePinnedToCore(タスクの関数名, "タスク名", スタックメモリサイズ, NULL, タスク優先順位, タスクハンドリングポインタ, Core ID)

例えば、二つのタスクを2番目のコア(Core ID=1)にマルチスレッドで並列処理を行いたい場合は、下記のようなコードになります。

xTaskCreatePinnedToCore(task0, "Task_0", 4096, NULL, 1, NULL, 1)
xTaskCreatePinnedToCore(task1, "Task_1", 4096, NULL, 2, NULL, 1)

プログラムコード

M5StackのExampleのMultithread例題のコードです。

// Copyright (c) 2020 aNoken

#include <M5Stack.h>

/* task0のループ関数 */
void task0(void* arg) {
  static int cnt = 0;
  //カウントアップしてシリアルとLCDに表示する
  while (1) {
    Serial.printf("task0 thread_cnt=%ld\n", cnt);
    M5.Lcd.printf("task0 thread_cnt=%ld\n", cnt);
    cnt++;
    vTaskDelay(1000);
  }
}

/* task1のループ関数 */
void task1(void* arg) {
  static int cnt = 0;
  //カウントアップしてシリアルとLCDに表示する
  while (1) {
    Serial.printf("task1 thread_cnt=%ld\n", cnt);
    M5.Lcd.printf("task1 thread_cnt=%ld\n", cnt);
    cnt++;
    vTaskDelay(1500);
  }
}

void setup() {
  M5.begin();                       //M5Stackを初期化
  M5.Power.begin();                 //M5Stackのバッテリ初期化
  M5.Lcd.clear(BLACK);
  M5.Lcd.setTextColor(YELLOW);
  M5.Lcd.setTextSize(3);
  M5.Lcd.setCursor(0, 0);

  //task0のループ関数を起動
  xTaskCreatePinnedToCore(task0, "Task0", 4096, NULL, 1, NULL, 1);

  //task1のループ関数を起動
  xTaskCreatePinnedToCore(task1, "Task1", 4096, NULL, 2, NULL, 1);
}

void loop() {  //Arduinoのメインのループ関数はここで実行
  static int cnt = 0;

  //カウントアップしてシリアルとLCDに表示する
  M5.Lcd.clear(BLACK);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.printf("Maintask thread_cnt=%ld\n", cnt);
  Serial.printf("Maintask thread_cnt=%ld\n", cnt);
  cnt++;
  vTaskDelay(1200);
}

まとめ

M5Stack(ESP32)のマルチスレッドで並列処理を行う方法を整理しました。

参考資料

  1. https://anoken.jimdofree.com/
  2. https://github.com/anoken/
  3. https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html