2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

FreeRTOS理解その5(Notifyとタスク状態表示)

Posted at

FreeRTOSにて通知(Notify)とタスク状態表示を実施

FreeRTOSの本家ページのxTaskNotifyGive()ulTaskNotifyTake()のサンプルを試すと同時にタスク状態遷移を確認してみた。

環境

ESP32搭載のM5Stack。ESP32でのFreeRTOSの紹介はこちら。Arduino IDE利用。

FreeRTOSタスク状態遷移

ここに説明がある。
image.png
ESP32でのArduino IDE環境のtask.hでの状態定義は次のようになっている。

/** Task states returned by eTaskGetState. */
typedef enum
{
	eRunning = 0,	/*!< A task is querying the state of itself, so must be running. */
	eReady,			/*!< The task being queried is in a read or pending ready list. */
	eBlocked,		/*!< The task being queried is in the Blocked state. */
	eSuspended,		/*!< The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */
	eDeleted		/*!< The task being queried has been deleted, but its TCB has not yet been freed. */
} eTaskState;

確認内容

3つのタスク構成。1つのタスクはボタン押下を検知し、すべてのタスク状態を表示する。2つのタスクでは、タスク間で通知を行い、別タスクから通知が来るまでは動作しないようなもの。同時にタスク状態を表示。

ソースコード

最初

# define BTN 38 // Middle button in M5Stack

xTaskHandle xTaskBtn;
xTaskHandle xTask1;
xTaskHandle xTask2;

char *task_state[] = {"Running", "Ready", "Blocked", "Suspended", "Deleted"};

ここはほぼ自明。

ボタンハンドリングタスク

void TaskBtn(void *arg) {
  unsigned long t = 0, p;

  for(;;) {
    while (!digitalRead(BTN)) {
      if (!t) { t = millis(); }
    }
    if (t) {
      p = millis()-t;
      if (p > 100) {  // for prevention of chattering
        Serial.printf("\nTaskBtn: Task1 state = %s\n", task_state[eTaskGetState(xTask1)]);
        Serial.printf("TaskBtn: Task2 state = %s\n", task_state[eTaskGetState(xTask2)]);
        Serial.printf("TaskBtn: TaskBtn state = %s\n", task_state[eTaskGetState(xTaskBtn)]);
      }
      t = 0;
    }
    vTaskDelay(1);
  }
}

ボタン押下を検知し、各タスクの状態を表示する。

2つのタスク

void Task1(void *arg) {
  for(;;) {
    Serial.printf("\nTask1(1): Task1 state = %s\n", task_state[eTaskGetState(xTask1)]);
    Serial.printf("Task1(1): Task2 state = %s\n", task_state[eTaskGetState(xTask2)]);
    Serial.printf("Task1(1): TaskBtn state = %s\n", task_state[eTaskGetState(xTaskBtn)]);
    vTaskDelay(5000);
    xTaskNotifyGive(xTask2); // Send notification to xTask2
    Serial.printf("\nTask1(2): Task1 state = %s\n", task_state[eTaskGetState(xTask1)]);
    Serial.printf("Task1(2): Task2 state = %s\n", task_state[eTaskGetState(xTask2)]);
    Serial.printf("Task1(2): TaskBtn state = %s\n", task_state[eTaskGetState(xTaskBtn)]);
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // Block to wait for xTask2
  }
}

void Task2(void *arg) {
  for(;;) {
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // Block to wait for xTask1
    Serial.printf("\nTask2(1): Task1 state = %s\n", task_state[eTaskGetState(xTask1)]);
    Serial.printf("Task2(1): Task2 state = %s\n", task_state[eTaskGetState(xTask2)]);
    Serial.printf("Task2(1): TaskBtn state = %s\n", task_state[eTaskGetState(xTaskBtn)]);
    vTaskDelay(5000);
    xTaskNotifyGive(xTask1); // Send notification to xTask1
    Serial.printf("\nTask2(2): Task1 state = %s\n", task_state[eTaskGetState(xTask1)]);
    Serial.printf("Task2(2): Task2 state = %s\n", task_state[eTaskGetState(xTask2)]);
    Serial.printf("Task2(2): TaskBtn state = %s\n", task_state[eTaskGetState(xTaskBtn)]);
  }
}

まずは、Task1が動作し、Task2に制御を渡し(xTaskNotifyGive())、その後Task2からの通知(ulTaskNotifyTake())を無限に待つ。Task2はTask1からの通知(ulTaskNotifyTake())を無限に待ち、通知があったら動き出し、その後Task1に制御を渡す(xTaskNotifyGive())。同時にその時のタスク状態を表示する。

Arduino IDEお決まり部分

void setup() {
  Serial.begin(9600);
  pinMode(BTN, INPUT);

  xTaskCreate(&TaskBtn, "TaskBtn", 2048, NULL, 1, &xTaskBtn);
  xTaskCreate(&Task1, "Task1", 2048, NULL, 0, &xTask1);
  xTaskCreate(&Task2, "Task2", 2048, NULL, 0, &xTask2);
  Serial.printf("\nsetup(): Task1 state = %s\n", task_state[eTaskGetState(xTask1)]);
  Serial.printf("setup(): Task2 state = %s\n", task_state[eTaskGetState(xTask2)]);
  Serial.printf("setup(): TaskBtn state = %s\n", task_state[eTaskGetState(xTaskBtn)]);
}

void loop() {
  vTaskDelay(1);
}

ボタン初期設定、タスク作成、起動時のタスク状態を表示。タスクPriorityは、TaskBtnが1、Task1および2が0となっている。

実験

起動直後

起動直後.png
タスク作成直後で、Task1とTask2とが”Ready”となっているが、TaskBtnが”Blocked”となっている。これはタスク作成時のPriority設定に関わっているようで、仮に、すべてのタスクPriorityを”0”とすると

  xTaskCreate(&TaskBtn, "TaskBtn", 2048, NULL, 0, &xTaskBtn);
  xTaskCreate(&Task1, "Task1", 2048, NULL, 0, &xTask1);
  xTaskCreate(&Task2, "Task2", 2048, NULL, 0, &xTask2);

起動直後2.png
と、すべてのタスク状態が”Ready”となる。

タスクPriority値が大きいほうが優先度が高いらしいが、このあたりの詳細は調べていない(勘弁)。いずれにせよ、Task1が最初に動作状態”Running”となっている。

ボタン押下なし状態

以降、TaskBtnのタスクPriorityは”1”となっている。
ボタンなし.png
xTaskNotifyGive()を実行すると、もう一つのタスク状態が、”Suspended” -> ”Ready” -> "Running"と変化していることがわかる。また、ulTaskNotifyTake()の前後で、もう一つのタスク状態が、”Running” -> ”Suspended”と変化していることがわかる。(”Ready”などを経由している可能性もあるが、深追いしていない。こちらも勘弁。)

ボタン押下状態

TaskBtnが動作する。Task1動作中のボタン押下時の状態は次のようになった。
タスク1中のボタン押下.png
TaskBtnは当然ながら”Running”状態、Task1の状態は”Running” -> ”Blocked”へと変化している。これは、Task1内でコールされたvTaskDelay()で、タスク状態が”Blocked”に移行しており、この間にボタンが押下されたからである。

Task2動作中のボタン押下時の状態も同様。
タスク2中のボタン押下.png

感想

タスク状態および遷移は奥が深い。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?