概要
M5stack及びESP32を使ってセンシング・通信をする場合に複数のタスク(処理)を同時に実行させたいときがあります。
特にセンサーでセンシングを行いつつ、SDやサーバー・スマホなどとコネクション張りっぱなしで通信したい場合はマルチタスク処理が必須になるのではないでしょうか?
今回マルチタスクについて触れる機会があったので備忘録的にまとめておく。
マルチタスクを使用する
どの関数を使うべきか?
タスク作成関数には
- xTaskCreate()
- xTaskCreatePinnedToCore()
- xTaskCreateUniversal()
の3つが存在します。
ESP32では基本的にはCPUコアの指定ができるxTaskCreatePinnedToCore()
を使用して問題ないと思います。
xTaskCreatePinnedToCore()
タスクを生成します
BaseType_t xTaskCreatePinnedToCore(
TaskFunction_t pvTaskCode, // タスク関数へのポインタ
const char *const pcName, // タスクの名前
const uint32_t usStackDepth, // スタックサイズ
void *const pvParameters, // タスクのパラメータ
UBaseType_t uxPriority, // タスク優先順位
TaskHandle_t *const pvCreatedTask, // タスクHandleへのポインタ
const BaseType_t xCoreID // 利用するコア
)
その他合わせて使う関数
xTaskGetTickCount()
タスク起動時間ticks数を取得する。
TickType_t xTaskGetTickCount(void) PRIVILEGED_FUNCTION
vTaskDelayUntil()
タスクを一時停止して他のタスクを実行する
void vTaskDelayUntil(
TickType_t *const pxPreviousWakeTime, // 最後にブロック解除された時間を保存するポインタ
const TickType_t xTimeIncrement // サイクル時間
) PRIVILEGED_FUNCTION
雛形サンプルコード
#include <M5Stack.h>
// タスク1の関数
void task1(void *parameter) {
portTickType xLastWakeTime;
xLastWakeTime = xTaskGetTickCount();
while (1) {
// タスク1の処理をここに記述
M5.Lcd.println("Task 1 running");
vTaskDelayUntil(&xLastWakeTime, 1000 / portTICK_PERIOD_MS);
}
}
// タスク2の関数
void task2(void *parameter) {
portTickType xLastWakeTime;
xLastWakeTime = xTaskGetTickCount();
while (1) {
// タスク2の処理をここに記述
M5.Lcd.println("Task 2 running");
vTaskDelayUntil(&xLastWakeTime, 1000 / portTICK_PERIOD_MS);
}
}
void setup() {
M5.begin();
// タスク1の作成と開始(コア0に割り当て)
xTaskCreatePinnedToCore(task1, "Task 1", 10000, NULL, 1, NULL, 0);
// タスク2の作成と開始(コア1に割り当て)
xTaskCreatePinnedToCore(task2, "Task 2", 10000, NULL, 1, NULL, 1);
}
void loop() {
// メインループでは何もしない
}
おわりに
今回は備忘録的に使い方を記述した。LCDやSDが既にあるM5stackはとても便利である。ちょっとセンサーを使って遊ぶ程度の使い方をする場合はここまで必要ないかもしれないが、マルチタスクを使いこなすことでPoC的なプロダクトにも十分使えるのではないか。
また、タスクスケジュール調整は実際に動かしてかつ経験が必要だと聞く。また引き続き調査したい。
(おまけ)マルチコアとマルチスレッドのまとめ
毎度混乱しがちだがこちらの図が大変理解しやすいと思います。