ESP-IDFを使ってみる

タスク起動時のパラメータ

esp-idfではタスク分割することで、見通しの良いコードにすることができます。
また、タスクにしておけば、使い回しが簡単にできます。
今回はタスク起動時のパラメータを紹介します。

タスク起動時のパラメータはxTaskCreate()の4番目の引数で指定します。
FreeRTOSのvTaskCreateの説明に以下の記載が有ります。

If pvParameters is set to the address of a variable then the variable must still exist when the created task executes - so it is not valid to pass the address of a stack variable.

pvParametersが変数のアドレスに設定されている場合、作成されたタスクの実行時に変数がまだ存在している必要があるため、スタック変 数のアドレスを渡すことは無効です。

mainタスクを終了してしまうと、スタック変数(ローカル変数)が解放されてしまいます。
スタック変数が解放されない様に、mainタスクは永久ループしています。
以下のコードを実行してみます。
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

void task_int(void *pvParameters)
{
        int32_t task_parameter = (int32_t)pvParameters;
        ESP_LOGI(pcTaskGetName(0), "Start task_parameter=[%d] %p", task_parameter, &task_parameter);
        while(1) {
                vTaskDelay(1);
        }
        vTaskDelete( NULL );
}


void task_char(void *pvParameters)
{
        char *task_parameter = (char *)pvParameters;
        ESP_LOGI(pcTaskGetName(0), "Start task_parameter=[%s] %p", task_parameter, task_parameter);
        while(1) {
                vTaskDelay(1);
        }
        vTaskDelete( NULL );
}

void app_main()
{
        xTaskCreate(&task_int, "task0", 2048, NULL, 1, NULL);
        vTaskDelay(1);
        // 値が渡る
        xTaskCreate(&task_int, "task1", 2048, (void *)100, 1, NULL);
        vTaskDelay(1);

        // iparamの値が渡る
        int32_t iparam;
        iparam=200;
        ESP_LOGI(pcTaskGetame(0), "iparam=%d %p", iparam, &iparam);
        xTaskCreate(&task_int, "task2", 2048, (void *)iparam, 1, NULL);
        iparam=-200;
        xTaskCreate(&task_int, "task3", 2048, (void *)iparam, 1, NULL);
        vTaskDelay(10);

        // cparamのアドレスが渡る
        char cparam[32];
        strcpy(cparam,"test1");
        ESP_LOGI(pcTaskGetName(0), "cparam=%s %p", cparam, cparam);
        xTaskCreate(&task_char, "task11", 2048, (void *)cparam, 1, NULL);
        // タスクを起動しないとアドレスの内容が変わってしまう
        strcpy(cparam,"test2");
        xTaskCreate(&task_char, "task12", 2048, (void *)cparam, 1, NULL);
        vTaskDelay(10);
        strcpy(cparam,"test3");
        xTaskCreate(&task_char, "task13", 2048, (void *)cparam, 1, NULL);
        vTaskDelay(10);

        // アドレスが渡る
        char *cptr1 = "TEST";
        ESP_LOGI(pcTaskGetName(0), "cptr1=%s %p", cptr1, cptr1);
        xTaskCreate(&task_char, "task21", 2048, (void *)cptr1, 1, NULL);
        char *cptr2 = "TESTTEST";
        ESP_LOGI(pcTaskGetName(0), "cptr2=%s %p", cptr2, cptr2);
        xTaskCreate(&task_char, "task22", 2048, (void *)cptr2, 1, NULL);

        // スタック変数が解放されない様に、mainタスクは永久ループ
        while(1) {
                vTaskDelay(1);
        }
}

注意しなければいけないのは、443Tick目のtask11とtask12の表示です。
メインタスクでは違う文字列を設定してタスクを起動していますが、タスク側に渡った文字列は同じです。
タスク起動時のパラメータに文字列を使う場合、アドレス(=0x3ffb2c84)を渡すことになります。
task11にもtask12にもtask13にも同じアドレスが渡ります。
タスク起動が完了する前にアドレスの中身を変えてしまうと、正しい文字列が渡りません。
vTaskCreate()の後でvTaskDelay()してやると、CPUの実行権が生成したタスク側に切り替わるので、正しい文字を取り出 すことができます。
タスク側もパラメータをローカル変数にコピーしてやらないと、突然、アドレスの中身が変わってしまう可能性があります。
I (0) cpu_start: Starting scheduler on APP CPU.
I (0) task0: Start task_parameter=[0] 0x3ffb7530
I (333) task1: Start task_parameter=[100] 0x3ffb7d40
I (343) main: iparam=200 0x3ffb5cf0
I (343) task2: Start task_parameter=[200] 0x3ffb8540
I (343) task3: Start task_parameter=[-200] 0x3ffb8d40
I (443) main: cparam=test1 0x3ffb2c84
I (443) task11: Start task_parameter=[test2] 0x3ffb2c84
I (443) task12: Start task_parameter=[test2] 0x3ffb2c84
I (543) task13: Start task_parameter=[test3] 0x3ffb2c84
I (643) main: cptr1=TEST 0x3f402f20
I (643) main: cptr2=TESTTEST 0x3f402f1c
I (643) task21: Start task_parameter=[TEST] 0x3f402f20
I (643) task22: Start task_parameter=[TESTTEST] 0x3f402f1c



複数のパラメータをタスク起動時に渡したいときは、構造体を宣言し、構造体の中にパラメータを詰め込んで、構造体のアドレスを渡します。
アドレスが渡るので、こちらもスタック変数が解放されない様に、mainタスクは永久ループしています。
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

typedef struct {
        int16_t i;
        float   f;
        double  d;
        char    c[64];
} PARAMETER_t;

void task_struct(void *pvParameters)
{
        PARAMETER_t *task_parameter = pvParameters;
        PARAMETER_t param;
        ESP_LOGI(pcTaskGetName(0), "Start task_parameter=%p", task_parameter);
        memcpy((char *)&param, task_parameter, sizeof(PARAMETER_t));
        ESP_LOGI(pcTaskGetName(0), "param.i=%d", param.i);
        ESP_LOGI(pcTaskGetName(0), "param.f=%f", param.f);
        ESP_LOGI(pcTaskGetName(0), "param.d=%f", param.d);
        ESP_LOGI(pcTaskGetName(0), "param.c=%s", param.c);
        while(1) {
                vTaskDelay(1);
        }
        vTaskDelete( NULL );
}

void app_main()
{
        PARAMETER_t param;
        param.i = 100;
        param.f = 1.1;
        param.d = 2.2;
        strcpy(param.c,"test");
        ESP_LOGI(pcTaskGetName(0), "param=%p", &param);
        //ESP_LOGI(pcTaskGetName(0), "param.i=%p", &param.i);
        //ESP_LOGI(pcTaskGetName(0), "param.f=%p", &param.f);
        //ESP_LOGI(pcTaskGetName(0), "param.d=%p", &param.d);
        xTaskCreate(&task_struct, "task", 2048, (void *) &param, 2, NULL);

        // スタック変数が解放されない様に、mainタスクは永久ループ
        while(1) {
                vTaskDelay(1);
        }
}


I (0) cpu_start: Starting scheduler on APP CPU.
I (318) main: param=0x3ffb2c80
I (318) task: Start task_parameter=0x3ffb2c80
I (328) task: param.i=100
I (328) task: param.f=1.100000
I (338) task: param.d=2.200000
I (338) task: param.c=test

続く....