esp-open-rtosを使ってみる

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

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

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

//時間稼ぎ
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();
    printf("[%s:%d] start Priority=%d\n",pcTaskGetName(0),nowTick,(int)prio);
    ConsumptionTick(200);
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] end\n",pcTaskGetName(0),nowTick);
    vTaskDelete( NULL );
}

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

    uart_set_baud(0, 115200);
    prio = uxTaskPriorityGet( NULL );
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] start Priority=%d\n",pcTaskGetName(0),nowTick,(int)prio);
    printf("[%s:%d] SDK version:%s\n",pcTaskGetName(0),nowTick,sdk_system_get_sdk_version());
    printf("[%s:%d] portTICK_PERIOD_MS=%d[ms]\n",pcTaskGetName(0),nowTick,portTICK_PERIOD_MS);
    printf("[%s:%d] configMAX_PRIORITIES=%d\n",pcTaskGetName(0),nowTick,configMAX_PRIORITIES);

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

    xReturned = xTaskCreate(taskEntry, "myTask1", 256, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask1 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 256, NULL, 2, &xHandle2);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask2 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }
#endif

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

    xReturned = xTaskCreate(taskEntry, "myTask1", 256, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask1 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 256, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask2 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }
#endif

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

    xReturned = xTaskCreate(taskEntry, "myTask1", 256, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask1 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }

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

    xReturned = xTaskCreate(taskEntry, "myTask2", 256, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask2 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }
#endif

}

結果は以下の様になりました。
処理が始まると、裏でAPへの接続が動いているのが分かります。
ちょっと意外だったのは、FreeRTOSのスケジュールポリシーがRoundRobinだったことです。
こちらに FreeRTOSのタスク・スケジューリングの詳細が公開されていますが、pre-empteve方式を採用しています。
myTask1とmyTask2は同じ優先度で実行可能状態になりますが、2つのタスクに「公平な」割合のプロセッサ時間を与えているので
同時にタスクが完了します。
[uiT:0] start Priority=14
[uiT:0] SDK version:0.9.9
[uiT:0] portTICK_PERIOD_MS=10[ms]
[uiT:0] configMAX_PRIORITIES=15
[uiT:1] myTask1 xTaskCreate success
[uiT:1] myTask2 xTaskCreate success
mode : sta(18:fe:34:d4:2f:77)
add if0
[myTask1:2] start Priority=2
[myTask2:3] start Priority=2
scandone
add 0
aid 11
cnt

connected with aterm-e625c0-g, channel 10
dhcp client start...
[myTask1:203] end
[myTask2:204] end
ip:192.168.10.113,mask:255.255.255.0,gw:192.168.10.1



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

(中略)


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

    xReturned = xTaskCreate(taskEntry, "myTask1", 256, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask1 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 256, NULL, 2, &xHandle2);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask2 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }
#endif

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

    xReturned = xTaskCreate(taskEntry, "myTask1", 256, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask1 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 256, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask2 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }
#endif

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

    xReturned = xTaskCreate(taskEntry, "myTask1", 256, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask1 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }

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

    xReturned = xTaskCreate(taskEntry, "myTask2", 256, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask2 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }
#endif


}

結果は以下の様になりました。
user_init()が終わると、先に優先度の高いmyTask2が実行され、myTask2が終わるとmyTask1が動き始めます。
優先度の高いタスクにCPU実行権を与えるのは、RTOSの基本です。
[uiT:0] start Priority=14
[uiT:0] SDK version:0.9.9
[uiT:0] portTICK_PERIOD_MS=10[ms]
[uiT:0] configMAX_PRIORITIES=15
[uiT:1] myTask1 xTaskCreate success
[uiT:1] myTask2 xTaskCreate success
mode : sta(18:fe:34:d4:2f:77)
add if0
[myTask2:2] start Priority=4
[myTask2:203] end
[myTask1:203] start Priority=2
scandone
[myTask1:404] end
add 0
aid 11
cnt

connected with aterm-e625c0-g, channel 10
dhcp client start...
ip:192.168.10.113,mask:255.255.255.0,gw:192.168.10.1



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

(中略)


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

    xReturned = xTaskCreate(taskEntry, "myTask1", 256, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask1 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 256, NULL, 2, &xHandle2);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask2 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }
#endif

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

    xReturned = xTaskCreate(taskEntry, "myTask1", 256, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask1 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }

    xReturned = xTaskCreate(taskEntry, "myTask2", 256, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask2 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }
#endif

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

    xReturned = xTaskCreate(taskEntry, "myTask1", 256, NULL, 2, &xHandle1);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask1 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }

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

    xReturned = xTaskCreate(taskEntry, "myTask2", 256, NULL, 4, &xHandle2);
    if( xReturned == pdPASS ) {
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] myTask2 xTaskCreate success\n",pcTaskGetName(0),nowTick);
    }
#endif

}

結果は以下の様になりました。
myTask1を作成した後に、10mSecのDelayを行うので、このタイミングでmyTask1が動き始めます。
その後、優先度の高いmyTask2が実行可能状態になると、実行権はmyTask2に移り、myTask1は待たされます。
myTask2が終わると、再び実行権はmyTask1に移ります。
[uiT:0] start Priority=14
[uiT:0] SDK version:0.9.9
[uiT:0] portTICK_PERIOD_MS=10[ms]
[uiT:0] configMAX_PRIORITIES=15
[uiT:1] myTask1 xTaskCreate success
[myTask1:1] start Priority=2
[uiT:159] myTask2 xTaskCreate success
mode : sta(18:fe:34:d4:2f:77)
add if0
[myTask2:160] start Priority=4
[myTask2:361] end
scandone
[myTask1:480] end
add 0
aid 11
cnt

connected with aterm-e625c0-g, channel 10
dhcp client start...
ip:192.168.10.113,mask:255.255.255.0,gw:192.168.10.1

続く....