ESP-IDFを使ってみる

FreeRTOSの新機能 Hooks


ESP32は2つのコアを持っているので、Hooks機能も拡張されています。
Hooks機能の拡張についてはこ ちらに説明が有りますが、よくわからなかったので、サンプルを作って確認してみました。
以下のコードでコアごとの空き時間を求めることができます。
/* The example of ESP-IDF
 *
 * This sample code is in the public domain.
 */
#include "freertos/FreeRTOS.h"
#include "esp_freertos_hooks.h"
#include "freertos/task.h"
#include "esp_log.h"

long total_idle_count = 0;
long core0_idle_count = 0;
long core1_idle_count = 0;
long total_tick_count = 0;
long core0_tick_count = 0;
long core1_tick_count = 0;

//時間稼ぎ
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;
    }
}

bool ApplicationIdleHook(void)
{
    total_idle_count++;
    return true;
}

bool Core0IdleHook(void)
{
    core0_idle_count++;
    return true;
}

bool Core1IdleHook(void)
{
    core1_idle_count++;
    return true;
}

void ApplicationTickHook(void)
{
    total_tick_count++;
}

void Core0TickHook(void)
{
    core0_tick_count++;
}

void Core1TickHook(void)
{
    core1_tick_count++;
}


void clearHook() {
    total_idle_count = 0;
    core0_idle_count = 0;
    core1_idle_count = 0;
    total_tick_count = 0;
    core0_tick_count = 0;
    core1_tick_count = 0;
}

void printHook(char * title) {
    ESP_LOGI(pcTaskGetName(NULL),"%s",title);
    ESP_LOGI(pcTaskGetName(NULL),"total_idle_count=%ld total_tick_count=%ld",total_idle_count,total_tick_count);
    ESP_LOGI(pcTaskGetName(NULL),"core0_idle_count=%ld core0_tick_count=%ld",core0_idle_count,core0_tick_count);
    ESP_LOGI(pcTaskGetName(NULL),"core1_idle_count=%ld core1_tick_count=%ld",core1_idle_count,core1_tick_count);
}


void app_main()
{
    ESP_LOGI(pcTaskGetName(NULL),"start");

    esp_register_freertos_idle_hook(&ApplicationIdleHook);
    esp_register_freertos_tick_hook(&ApplicationTickHook);
    esp_register_freertos_idle_hook_for_cpu(&Core0IdleHook,0);
    esp_register_freertos_idle_hook_for_cpu(&Core1IdleHook,1);
    esp_register_freertos_tick_hook_for_cpu(&Core0TickHook,0);
    esp_register_freertos_tick_hook_for_cpu(&Core1TickHook,1);

    clearHook();
    ConsumptionTick(200);
    printHook("Busy 200");

    clearHook();
    vTaskDelay(200);
    printHook("Delay 200");

    clearHook();
    ConsumptionTick(200);
    vTaskDelay(200);
    printHook("Busy 200 + Delay 200");
}



Core#0を使って、200Tick全速力でループすると、Core#0のIdleカウンターは0になります。
その後200TickのDelayを行うと、Core#0のIdleカウンターも200になります。
CPUにどの程度余裕が有るのか、簡単に調べることができます。
I (275) main: start
I (2285) main: Busy 200
I (2285) main: total_idle_count=0 total_tick_count=201
I (2285) main: core0_idle_count=0 core0_tick_count=201
I (2285) main: core1_idle_count=201 core1_tick_count=201
I (4285) main: Delay 200
I (4285) main: total_idle_count=200 total_tick_count=200
I (4285) main: core0_idle_count=200 core0_tick_count=200
I (4285) main: core1_idle_count=200 core1_tick_count=200
I (8305) main: Busy 200 + Delay 200
I (8305) main: total_idle_count=200 total_tick_count=401
I (8305) main: core0_idle_count=200 core0_tick_count=401
I (8305) main: core1_idle_count=402 core1_tick_count=402



Hooks機能を使ってパフォーマンス・モニターを作ってみました。
CPUと各コアの余裕度を定期的に表示します。


コードを以下の様に変更します。
ビルドして実行すると、CPUと各コアの余裕度を10秒ごとに表示します。
ついでに、esp_get_free_heap_size()で取得した値も表示しています。
/* The example of ESP-IDF
 *
 * This sample code is in the public domain.
 */
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "esp_freertos_hooks.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"

long total_idle_count = 0;
long core0_idle_count = 0;
long core1_idle_count = 0;
long total_tick_count = 0;
long core0_tick_count = 0;
long core1_tick_count = 0;

typedef struct {
    long total_idle_count;
    long core0_idle_count;
    long core1_idle_count;
    long total_tick_count;
    long core0_tick_count;
    long core1_tick_count;
} COUNTER_t;

QueueHandle_t xQueue;

bool ApplicationIdleHook(void)
{
    total_idle_count++;
    return true;
}

bool Core0IdleHook(void)
{
    core0_idle_count++;
    return true;
}

bool Core1IdleHook(void)
{
    core1_idle_count++;
    return true;
}

void ApplicationTickHook(void)
{
    total_tick_count++;
}

void Core0TickHook(void)
{
    core0_tick_count++;
}

void Core1TickHook(void)
{
    core1_tick_count++;
}


void clearHook() {
    total_idle_count = 0;
    core0_idle_count = 0;
    core1_idle_count = 0;
    total_tick_count = 0;
    core0_tick_count = 0;
    core1_tick_count = 0;
}

void printHook(char * title) {
    ESP_LOGI(pcTaskGetName(NULL),"%s",title);
    ESP_LOGI(pcTaskGetName(NULL),"total_idle_count=%ld total_tick_count=%ld",total_idle_count,total_tick_count);
    ESP_LOGI(pcTaskGetName(NULL),"core0_idle_count=%ld core0_tick_count=%ld",core0_idle_count,core0_tick_count);
    ESP_LOGI(pcTaskGetName(NULL),"core1_idle_count=%ld core1_tick_count=%ld",core1_idle_count,core1_tick_count);
}


static void monitor_task(void *pvParameters)
{
    COUNTER_t mess;
    ESP_LOGI(pcTaskGetName(NULL),"start");

    float idleRate;
    while (1) {
        xQueueReceive(xQueue, &mess, portMAX_DELAY);
        printf("Free memory: %"PRIu32" bytes\n", esp_get_free_heap_size());

        idleRate = (float)mess.total_idle_count/(float)mess.total_tick_count*100.0;
        if (idleRate > 100.0) idleRate = 100.0;
        printf("CPU Idle: %.2f %%\n", idleRate);

        idleRate = (float)mess.core0_idle_count/(float)mess.core0_tick_count*100.0;
        if (idleRate > 100.0) idleRate = 100.0;
        printf("CORE0 Idle: %.2f %%\n", idleRate);

        idleRate = (float)mess.core1_idle_count/(float)mess.core1_tick_count*100.0;
        if (idleRate > 100.0) idleRate = 100.0;
        printf("CORE1 Idle: %.2f %%\n", idleRate);
        printf("\n");
    } // end while

    vTaskDelete(NULL);
}

// Performance Task
void perf_task(void *pvParameters)
{
    COUNTER_t mess;
    ESP_LOGI(pcTaskGetName(NULL),"start");

    while(1) {
      clearHook();
      vTaskDelay(10000 / portTICK_PERIOD_MS);
      //printHook("perf");
      mess.total_idle_count = total_idle_count;
      mess.core0_idle_count = core0_idle_count;
      mess.core1_idle_count = core1_idle_count;
      mess.total_tick_count = total_tick_count;
      mess.core0_tick_count = core0_tick_count;
      mess.core1_tick_count = core1_tick_count;
      xQueueSend(xQueue, &mess, 0);
    }
}

void app_main()
{
    ESP_LOGI(pcTaskGetName(NULL),"start");

    /* Create queue */
    xQueue = xQueueCreate( 20, sizeof(COUNTER_t) );

    /* Check everything was created. */
    configASSERT( xQueue );

    esp_register_freertos_idle_hook(&ApplicationIdleHook);
    esp_register_freertos_tick_hook(&ApplicationTickHook);
    esp_register_freertos_idle_hook_for_cpu(&Core0IdleHook,0);
    esp_register_freertos_idle_hook_for_cpu(&Core1IdleHook,1);
    esp_register_freertos_tick_hook_for_cpu(&Core0TickHook,0);
    esp_register_freertos_tick_hook_for_cpu(&Core1TickHook,1);

    xTaskCreate(monitor_task, "minitor", 4096, NULL, 5, NULL);
    xTaskCreate(perf_task, "perf", 4096, NULL, 5, NULL);
}

続く....