PlatformIOでESP32を開発する

PreBuildされたFreeRTOSライブラリ


Espressifが提供している ESP-IDFではmenuconfigを使って、FreeRTOSの設定やESP32のパラメータ(動作周波数など)を変更することができますが、
Arduino Frameworkでは、プレビルドされたFreeRTOSのライブラリを使うので、FreeRTOSの設定を変更することができません。
このため、ESP-IDFとは違った動きになる事が有ります。
この点を理解して使わないと、思わぬ不具合が発生します。
以下にいくつかのサンプルを紹介します。



Arduino FrameworkではTick Rateは1000Hzに固定されています。
また、ログレベルはErrorに固定されていて、esp_log_level_set()を使っても変更することができません。
以下のコードを実行すると、Tick Rateとログレベルを確認する事ができます。
#include "freertos/FreeRTOS.h"
#include "Arduino.h"

static const char *TAG = "LogTest";

void setup()
{
    vTaskDelay(2000/portTICK_PERIOD_MS);
    printf("portTICK_PERIOD_MS=%d\n",portTICK_PERIOD_MS);
    printf("Default Log level\n");
    ESP_LOGE(TAG,"This is LOGE");
    ESP_LOGE("","This is LOGE");
    ESP_LOGW(TAG,"This is LOGW");
    ESP_LOGI(TAG,"This is LOGI");
    ESP_LOGD(TAG,"This is LOGD");
    ESP_LOGV(TAG,"This is LOGV");

    printf("Change Log level\n");
    esp_log_level_set("*",ESP_LOG_INFO);
    ESP_LOGE(TAG,"This is LOGE");
    ESP_LOGE("","This is LOGE");
    ESP_LOGW(TAG,"This is LOGW");
    ESP_LOGI(TAG,"This is LOGI");
    ESP_LOGD(TAG,"This is LOGD");
    ESP_LOGV(TAG,"This is LOGV");
    vTaskDelete( NULL );

}

void loop() { // Never run
    printf("[%s:%d] loop\n",pcTaskGetTaskName(NULL),xTaskGetTickCount());
}



Arduino Frameworkでもprintfを使うことができますが、printfのボーレートは115200bpsに固定されています。
また、複数のタスクが同時にprintfを実行したときに、標準出力を排他制御しません。
以下のコードを実行すると、printfの結果が重なって表示されます。
#include "freertos/FreeRTOS.h"
#include "Arduino.h"

SemaphoreHandle_t xSemaphore;

// https://qiita.com/koara-local/items/585755faac70c8b37b5b
// [C++11 ~] variadic templates
template <typename ... Args>
void _printf(const char *format, Args const & ... args) {
//    xSemaphoreTake(xSemaphore, portMAX_DELAY);
    TickType_t _nowTick = xTaskGetTickCount();
    char * _taskName = pcTaskGetTaskName( NULL );
    printf("[%s:%d] ",_taskName, _nowTick);
    printf(format, args ...);
//    xSemaphoreGive(xSemaphore);
}


//時間稼ぎ
void ConsumptionTick(int delay) {
    TickType_t startTick;
    TickType_t endTick;
    TickType_t nowTick;
    startTick = xTaskGetTickCount();
    endTick = startTick + delay;
    while(1) {
      nowTick = xTaskGetTickCount();
      if (nowTick > endTick) break;
    }
    return;
}

void task(void *pvParameter)
{
    UBaseType_t taskPrio = uxTaskPriorityGet( NULL );
    int CoreID = xPortGetCoreID();
    _printf("start CoreID=%d Priority=%d\n",CoreID,taskPrio);
    ConsumptionTick(200);
    CoreID = xPortGetCoreID();
    _printf("end   CoreID=%d Priority=%d\n",CoreID,taskPrio);
    vTaskDelete( NULL );
}

void setup() {
    delay(2000);
    /* Create Mutex */
    xSemaphore = xSemaphoreCreateMutex();
    /* Check everything was created. */
    configASSERT( xSemaphore );
    /* Set prioroty to 6 */
    vTaskPrioritySet(NULL,6);
    UBaseType_t prio = uxTaskPriorityGet( NULL );
    int CoreID = xPortGetCoreID();
    _printf("start CoreID=%d Priority=%d\n",CoreID,prio);
    _printf("portTICK_PERIOD_MS=%d\n",portTICK_PERIOD_MS);

#if 1
    xTaskCreatePinnedToCore(task, "Task1", 4096, NULL, 1, NULL, tskNO_AFFINITY);
    xTaskCreatePinnedToCore(task, "Task2", 4096, NULL, 2, NULL, tskNO_AFFINITY);
    xTaskCreatePinnedToCore(task, "Task3", 4096, NULL, 3, NULL, tskNO_AFFINITY);
    xTaskCreatePinnedToCore(task, "Task4", 4096, NULL, 4, NULL, tskNO_AFFINITY);
#endif

#if 0
    xTaskCreatePinnedToCore(task, "Task1", 4096, NULL, 1, NULL, 0);
    xTaskCreatePinnedToCore(task, "Task2", 4096, NULL, 2, NULL, 0);
    xTaskCreatePinnedToCore(task, "Task3", 4096, NULL, 3, NULL, 1);
    xTaskCreatePinnedToCore(task, "Task4", 4096, NULL, 4, NULL, 1);
#endif

    CoreID = xPortGetCoreID();
    _printf("end   CoreID=%d Priority=%d\n",CoreID,prio);

    /* stop main task */
    vTaskDelete( NULL );
}

void loop() { // Never run
    printf("[%s:%d] loop\n",pcTaskGetTaskName(NULL),xTaskGetTickCount());
}

結果は以下の様になります。
2037Tick目に一斉に色々なタスクがprintfを使うので、その表示が重なっています。
ESP-IDFではこのようなことは起こりません。
[loopTask:2036] start CoreID=1 Priority=6
[loopTask:2037] portTICK_PERIOD_MS=1
[Task3:2037] [Task1:2037] start CoreID=0 Priority=1
[loopTask:2037] end   CoreID=1 Priority=6
[Task4:2037] start CoreID=0 Priority=4
start CoreID=0 Priority=3
[Task4:2245] end   CoreID=0 Priority=4
[Task2:2037] start CoreID=0 Priority=2
[Task3:2248] end   CoreID=0 Priority=3
[Task1:2248] end   CoreID=0 Priority=1
[Task2:2446] end   CoreID=0 Priority=2

Mutexを使って、_printf関数を排他制御するとこの様に表示されます。
なぜか優先度=1のTask1の方が、優先度=2のTask2よりも早く終わります。
実行しているコア番号の取得に不具合が有り、正しい値を取得できません。
以下の表示ではTask1もTask2もCore#0を使っているように見えますが、おそらくどちらかが途中で別のコアにスイッチしているので、
この2つのタスクは並行して動いています。
[loopTask:2036] start CoreID=1 Priority=6
[loopTask:2037] portTICK_PERIOD_MS=1
[Task1:2037] start CoreID=0 Priority=1
[loopTask:2037] end   CoreID=1 Priority=6
[Task4:2040] start CoreID=1 Priority=4
[Task3:2043] start CoreID=0 Priority=3
[Task4:2244] end   CoreID=1 Priority=4
[Task2:2244] start CoreID=0 Priority=2
[Task3:2248] end   CoreID=0 Priority=3
[Task1:2248] end   CoreID=0 Priority=1
[Task2:2445] end   CoreID=0 Priority=2



Arduino FrameworkではTrace Facility機能を有効にする事ができません。
以下のコードは動いているタスクの一覧を表示するサンプルですが、Trace Facility機能が無効になっているので、ビルドが通りません。
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "Arduino.h"


void task(void *pvParameter)
{
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] start Priority=%d\n",pcTaskGetTaskName( NULL ),nowTick, uxTaskPriorityGet( NULL ));
    for(int i=0;i<10;i++) {
      vTaskDelay(10 / portTICK_PERIOD_MS);
    }
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] end   Priority=%d\n",pcTaskGetTaskName( NULL ),nowTick, uxTaskPriorityGet( NULL ));
    vTaskDelete( NULL );
}

void setup() {
    delay(2000);
    printf("[%s] start\n",pcTaskGetTaskName(NULL));


   TaskStatus_t *pxTaskStatusArray;
   volatile UBaseType_t uxArraySize, x;
   uint32_t ulTotalRunTime, ulStatsAsPercentage;

   /* Take a snapshot of the number of tasks in case it changes while this
   function is executing. */
   uxArraySize = uxTaskGetNumberOfTasks();
   //uxArraySize = uxCurrentNumberOfTasks();
   printf("uxArraySize=%d\n",(int)uxArraySize);

   /* Allocate a TaskStatus_t structure for each task.  An array could be
   allocated statically at compile time. */
   pxTaskStatusArray = (TaskStatus_t *)pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
   //printf("pxTaskStatusArray=%x\n",(int)pxTaskStatusArray);

   if( pxTaskStatusArray != NULL )
   {
      /* Generate raw status information about each task. */
      uxArraySize = uxTaskGetSystemState( pxTaskStatusArray,
                                 uxArraySize,
                                 &ulTotalRunTime );

      /* For percentage calculations. */
      printf("ulTotalRunTime=%d\n",(int)ulTotalRunTime);
      ulTotalRunTime /= 100UL;

      /* For each populated position in the pxTaskStatusArray array,
      format the raw data as human readable ASCII data. */
      for( x = 0; x < uxArraySize; x++ ) {
            /* What percentage of the total run time has the task used?
            This will always be rounded down to the nearest integer.
            ulTotalRunTimeDiv100 has already been divided by 100. */

            ulStatsAsPercentage = 0;
            if ( ulTotalRunTime > 0)
              ulStatsAsPercentage =
                  pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime;

            if( ulStatsAsPercentage > 0UL )
            {
               //printf( "%20s\t\t%lu\t\t%lu%%\r\n",
               printf( "%-20s\t\t%lu\t\t%lu%%\r\n",
                        pxTaskStatusArray[ x ].pcTaskName,
                        (unsigned long)pxTaskStatusArray[ x ].ulRunTimeCounter,
                        (unsigned long)ulStatsAsPercentage );
            }
            else
            {
               /* If the percentage is zero here then the task has
               consumed less than 1% of the total run time. */
               printf( "%-20s\t\t%lu\t\t<1%%\r\n",
                        pxTaskStatusArray[ x ].pcTaskName,
                        (unsigned long)pxTaskStatusArray[ x ].ulRunTimeCounter );
            }

      }

      /* The array is no longer needed, free the memory it consumes. */
      vPortFree( pxTaskStatusArray );
   }

   vTaskDelete( NULL );
}

void loop() { // Never run
}

続く...