vTaskSuspend()
で suspend となったタスクを vTaskResume()
で再開すると、どこから再開するのか?という素朴な疑問を確認しました。
検証環境:
- ボード: Wio LTE JP Version (STM32F405RG)
- FreeRTOS: Arduino_FreeRTOS_Library 11.1.0
結論
suspend したところから再開します。タスクの先頭からの再開ではありません。
※ 挙動や目的を考えると、当たり前と言えば当たり前ですが。
コードと結果
-
pushToQueueTask
popFromQueueTask
startAndStopTask
という3つのタスクを作成 -
pushToQueueTask
からキューを通してpopFromQueueTask
へカウントデータを1秒毎に送信、受信側でデータを表示 -
startAndStopTask
が不定期(1.0秒~2.5秒毎)にpushToQueueTask
を suspend/resume している
where_is_resuming_from.ino
#include <WioLTEforArduino.h>
#define CONSOLE SerialUSB
#include <STM32FreeRTOS.h>
#include <STM32FreeRTOSConfig_extra.h> // ref: https://qiita.com/ma2shita/items/88036fa3a75ffa684ec2
WioLTE Wio;
TaskHandle_t pushToQueueTask_h = NULL;
QueueHandle_t xQueue;
struct QUEUE_PAYLOAD {
uint8_t data;
};
void pushToQueueTask(void *pvParameters) {
CONSOLE.println("(pushToQueueTask) init.");
QueueHandle_t q = (QueueHandle_t)pvParameters;
struct QUEUE_PAYLOAD q_payload;
for (;;) {
for (uint8_t i = 0; i < 100; i++) {
q_payload.data = i;
xQueueSend(q, &q_payload, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
}
void popFromQueueTask(void *pvParameters) {
QueueHandle_t q = (QueueHandle_t)pvParameters;
struct QUEUE_PAYLOAD q_payload;
for (;;) {
xQueueReceive(q, &q_payload, portMAX_DELAY);
CONSOLE.printf("(popFromQueueTask) q_payload.data: %d\r\n", q_payload.data);
}
}
void startAndStopTask(void *pvParameters) {
for (;;) {
if (eTaskGetState(pushToQueueTask_h) == eSuspended) {
CONSOLE.println("(startAndStopTask) vTaskResume()");
vTaskResume(pushToQueueTask_h);
} else {
CONSOLE.println("(startAndStopTask) vTaskSuspend()");
vTaskSuspend(pushToQueueTask_h);
}
vTaskDelay(pdMS_TO_TICKS(random(10, 26)*100));
}
}
void setup() {
CONSOLE.begin(9600);
delay(2000);
Wio.Init();
xQueue = xQueueCreate(1, sizeof(QUEUE_PAYLOAD));
xTaskCreate(popFromQueueTask, nullptr, 1024, xQueue, 1, nullptr);
xTaskCreate(pushToQueueTask, nullptr, 1024, xQueue, 1, &pushToQueueTask_h);
xTaskCreate(startAndStopTask, nullptr, 1024, nullptr, 1, nullptr);
vTaskStartScheduler();
for (;;) __asm__("wfi");
}
void loop() {
// Unnecessary Impl.
}
結果は以下の通りです。
init は xCreateTask()
の直後1回のみ。以降は suspend したところから resume しています。
(pushToQueueTask) init.
(startAndStopTask) vTaskSuspend()
(popFromQueueTask) q_payload.data: 0
(startAndStopTask) vTaskResume()
(popFromQueueTask) q_payload.data: 1
(popFromQueueTask) q_payload.data: 2
(popFromQueueTask) q_payload.data: 3
(startAndStopTask) vTaskSuspend()
(startAndStopTask) vTaskResume()
(popFromQueueTask) q_payload.data: 4
(popFromQueueTask) q_payload.data: 5
(startAndStopTask) vTaskSuspend()
(startAndStopTask) vTaskResume()
(popFromQueueTask) q_payload.data: 6
(popFromQueueTask) q_payload.data: 7
タスクの初期処理をしたければ vTaskDelete/xCreateTask を使う
タスクを初期処理から動かしたければ、vTaskDelete()
でタスクを削除し、xCreateTask()
でタスクを作ることで実現できます。
startAndStopTask()
を、以下のように差し替えます。
void startAndStopTask(void *pvParameters) {
for (;;) {
if (eTaskGetState(whereIsResumingFromTask_h) == eDeleted) {
CONSOLE.println("(startAndStopTask) xTaskCreate");
xTaskCreate(pushToQueueTask, nullptr, 1024, xQueue, 1, &pushToQueueTask_h);
} else {
CONSOLE.println("(startAndStopTask) vTaskDelete");
vTaskDelete(pushToQueueTask_h);
}
vTaskDelay(pdMS_TO_TICKS(random(10, 26)*100));
}
}
結果は以下の通り。
(pushToQueueTask) init.
(startAndStopTask) vTaskDelete
(popFromQueueTask) q_payload.data: 0
(startAndStopTask) xTaskCreate
(pushToQueueTask) init.
(popFromQueueTask) q_payload.data: 0
(popFromQueueTask) q_payload.data: 1
(popFromQueueTask) q_payload.data: 2
(startAndStopTask) vTaskDelete
(startAndStopTask) xTaskCreate
(pushToQueueTask) init.
(popFromQueueTask) q_payload.data: 0
(popFromQueueTask) q_payload.data: 1
(startAndStopTask) vTaskDelete
(startAndStopTask) xTaskCreate
(pushToQueueTask) init.
(popFromQueueTask) q_payload.data: 0
(popFromQueueTask) q_payload.data: 1
(startAndStopTask) vTaskDelete
このままだと、xTaskCreate の引数設定が冗長なので、関数で共通化したほうが良いかもしれません。
EoT