ESP8266_RTOS_SDK V3を使ってみる

タスクのスケジューリング

$HOME/ESP8266_RTOS_SDK/examplesディレクトリには多数のサンプルソースが含まれていますが、
タスクを複数同時に起動したときの動作を確認するためのサンプルが無かったので、新しく作りました。

まずは同じ優先度のタスクを2つ連続して起動してみました。
以下のコードをビルドして書き込みます。
ConsumptionTick()は指定したTick数、時間稼ぎをする関数です。
/* The example of ESP8266_RTOS_SDK
 *
 * This sample code is in the public domain.
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"

#include "esp_log.h"
#include "esp_system.h"
#include "esp_err.h"

//static const char *TAG = "main";

/**
 * TEST CODE BRIEF
 *
 * This example will show you how to use xTaskCreate:
 *
**/

//時間稼ぎ
void ConsumptionTick(int delay) {
    TickType_t startTick;
    TickType_t endTick;
    TickType_t nowTick;
    startTick = xTaskGetTickCount();
    endTick = startTick + delay;
    //printf("startTick=%d endTick=%d\n",startTick,endTick);
    while(1) {
      nowTick = xTaskGetTickCount();
      if (nowTick > endTick) break;
    }
}

// Task Body
void taskEntry(void *pvParameters)
{
    UBaseType_t prio;
    TickType_t nowTick;

    prio = uxTaskPriorityGet( NULL );
    nowTick = xTaskGetTickCount();
    ESP_LOGI(pcTaskGetName(0),"[%u] start Priority=%d",nowTick,(int)prio);
    ConsumptionTick(200);
    nowTick = xTaskGetTickCount();
    ESP_LOGI(pcTaskGetName(0),"[%u] end",nowTick);
    vTaskDelete( NULL );
}

void app_main(void)
{
    BaseType_t xReturned;
    TaskHandle_t xHandle1 = NULL;
    TaskHandle_t xHandle2 = NULL;
    UBaseType_t prio;
    TickType_t nowTick;

    prio = uxTaskPriorityGet( NULL );
    nowTick = xTaskGetTickCount();
    ESP_LOGI(pcTaskGetName(0),"[%u] start Priority=%d",nowTick,(int)prio);
    ESP_LOGI(pcTaskGetName(0),"SDK version:%s", esp_get_idf_version());
    ESP_LOGI(pcTaskGetName(0),"portTICK_PERIOD_MS=%u[ms]",portTICK_PERIOD_MS);
    ESP_LOGI(pcTaskGetName(0),"configMAX_PRIORITIES=%d",configMAX_PRIORITIES);

#if 1
// myTask1を優先度=2で起動
// myTask2を優先度=2で起動

    xReturned = xTaskCreate(taskEntry, "myTask1", 1024, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask1 xTaskCreate success");
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 1024, NULL, 2, &xHandle2);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask2 xTaskCreate success");
    }
#endif

#if 0
// myTask1を優先度=2で起動
// myTask2を優先度=4で起動

    xReturned = xTaskCreate(taskEntry, "myTask1", 1024, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask1 xTaskCreate success");
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 1024, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask2 xTaskCreate success");
    }
#endif

#if 0
// myTask1を優先度=2で起動
// 自分自身をDelay
// myTask2を優先度=4で起動

    xReturned = xTaskCreate(taskEntry, "myTask1", 1024, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask1 xTaskCreate success");
    }

    // delay 10mSec
    vTaskDelay(10 / portTICK_PERIOD_MS);

    xReturned = xTaskCreate(taskEntry, "myTask2", 1024, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask2 xTaskCreate success");
    }
#endif

}

結果は以下の様になりました。

app_mainは優先度が14で動き始めます。
myTask1とmyTask2を生成しますが、app_mainよりも優先度が低いので、app_mainが終了しないと動きだすことができま せん。
app_mainが終了すると、myTask1とmyTask2は同時に実行可能状態になりますが、2つのタスクに「公平な」割合のプロセッサ時 間を与えているので
同時にタスクが完了します。

また、xTaskGetTickCount()で得られるのは、ある時点(下記の例では472-21=451Tick目)からの通算Tick数 で、
システム起動時からの通算Tick数(カッコ内の数字)ではないことが分かりました。
I (472) uiT: [21] start Priority=14
I (472) uiT: SDK version:v3.2-dev-44-g5e98e3f-dirty
I (482) uiT: portTICK_PERIOD_MS=10[ms]
I (482) uiT: configMAX_PRIORITIES=15
I (492) uiT: myTask1 xTaskCreate success
I (502) uiT: myTask2 xTaskCreate success
I (502) myTask1: [24] start Priority=2
I (522) myTask2: [25] start Priority=2
I (2532) myTask1: [227] end
I (2532) myTask2: [227] end



次に、後から起動するタスクの優先度を上げてみました。
以下の部分を変更し、ビルドして書き込みます。
/* The example of ESP8266_RTOS_SDK
 *
 * This sample code is in the public domain.
 */

(中略)

#if 0
// myTask1を優先度=2で起動
// myTask2を優先度=2で起動

    xReturned = xTaskCreate(taskEntry, "myTask1", 1024, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask1 xTaskCreate success");
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 1024, NULL, 2, &xHandle2);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask2 xTaskCreate success");
    }
#endif

#if 1
// myTask1を優先度=2で起動
// myTask2を優先度=4で起動

    xReturned = xTaskCreate(taskEntry, "myTask1", 1024, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask1 xTaskCreate success");
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 1024, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask2 xTaskCreate success");
    }
#endif

#if 0
// myTask1を優先度=2で起動
// 自分自身をDelay
// myTask2を優先度=4で起動

    xReturned = xTaskCreate(taskEntry, "myTask1", 1024, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask1 xTaskCreate success");
    }

    // delay 10mSec
    vTaskDelay(10 / portTICK_PERIOD_MS);

    xReturned = xTaskCreate(taskEntry, "myTask2", 1024, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask2 xTaskCreate success");
    }
#endif

}

結果は以下の様になりました。
app_main()が終わると、先に優先度の高いmyTask2が実行され、myTask2が終わるとmyTask1が動き始めます。
優先度の高いタスクにCPU実行権を与えるのは、RTOSの基本です。
I (472) uiT: [21] start Priority=14
I (472) uiT: SDK version:v3.2-dev-44-g5e98e3f-dirty
I (482) uiT: portTICK_PERIOD_MS=10[ms]
I (482) uiT: configMAX_PRIORITIES=15
I (492) uiT: myTask1 xTaskCreate success
I (502) uiT: myTask2 xTaskCreate success
I (502) myTask2: [24] start Priority=4
I (2522) myTask2: [226] end
I (2522) myTask1: [226] start Priority=2
I (4532) myTask1: [427] end



次に、優先度の低いタスクを一瞬動かした後に、優先度の高いタスクを起動してみました。
以下の部分を変更して、ビルドして書き込みます。
/* The example of ESP8266_RTOS_SDK
 *
 * This sample code is in the public domain.
 */

(中略)

#if 0
// myTask1を優先度=2で起動
// myTask2を優先度=2で起動

    xReturned = xTaskCreate(taskEntry, "myTask1", 1024, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask1 xTaskCreate success");
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 1024, NULL, 2, &xHandle2);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask2 xTaskCreate success");
    }
#endif

#if 0
// myTask1を優先度=2で起動
// myTask2を優先度=4で起動

    xReturned = xTaskCreate(taskEntry, "myTask1", 1024, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask1 xTaskCreate success");
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 1024, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask2 xTaskCreate success");
    }
#endif

#if 1
// myTask1を優先度=2で起動
// 自分自身をDelay
// myTask2を優先度=4で起動

    xReturned = xTaskCreate(taskEntry, "myTask1", 1024, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask1 xTaskCreate success");
    }

    // delay 10mSec
    vTaskDelay(10 / portTICK_PERIOD_MS);

    xReturned = xTaskCreate(taskEntry, "myTask2", 1024, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      ESP_LOGI(pcTaskGetName(0),"myTask2 xTaskCreate success");
    }
#endif

}

結果は以下の様になりました。
myTask1を生成した後に、10mSecのDelayを行うので、このタイミングでmyTask1が動き始めます。
その後、優先度の高いmyTask2が実行可能状態になると、実行権はmyTask2に移り、myTask1は待たされます。
myTask2が終わると、再び実行権はmyTask1に移ります。
I (472) uiT: [21] start Priority=14
I (482) uiT: SDK version:v3.2-dev-44-g5e98e3f-dirty
I (482) uiT: portTICK_PERIOD_MS=10[ms]
I (492) uiT: configMAX_PRIORITIES=15
I (502) uiT: myTask1 xTaskCreate success
I (502) myTask1: [24] start Priority=2
I (512) uiT: myTask2 xTaskCreate success
I (522) myTask2: [26] start Priority=4
I (2532) myTask2: [227] end
I (4542) myTask1: [428] end



SDKのバージョンを3.2→3.4にアップデートしたら、app_mainの優先度が1に変わっていました。
myTask1 myTask2の方が優先度が高いので、app_mainよりも先に終了します。
I (236) uiT: [1] start Priority=1
I (238) uiT: SDK version:v3.4-dev-415-ge09f004f
I (240) uiT: portTICK_PERIOD_MS=10[ms]
I (242) uiT: configMAX_PRIORITIES=15
I (248) myTask1: [3] start Priority=2
I (2257) myTask1: [204] end
I (2258) uiT: myTask1 xTaskCreate success
I (2259) myTask2: [204] start Priority=2
I (4267) myTask2: [405] end
I (4268) uiT: myTask2 xTaskCreate success

sdkconfigを探しましたが、メインタスクの優先度に関する項目は見つかりませんでした。
$ grep PRIO sdkconfig
CONFIG_FMB_PORT_TASK_PRIO=10
CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5
CONFIG_MB_SERIAL_TASK_PRIO=10
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5

続く....