FRDM-K64FでAmazonFreeRTOSを動かす

FRDM-K64FのSDKに付属のamazon FreeRTOS のサンプルをいじって、わかったことを書き留めます。
開発環境はMCUXpressoを使用します。

# frdm_k64f_freertos_hello

* タスクからHelloWorldするサンプル
* ROM/RAM は `-O0` でそれぞれ 22872 Byte / 19120 Byte
* `main()` では最初に`BOARD_xxx` というAPI群を呼ぶ
  * クロック、ペリフェラル初期化など
  * `BOARD_xxx` APIは `board/board.c` にあるBSPのAPI
* `xTaskCreate()` で `Hello_task` のタスク登録
  * 引数はタスクメイン関数ポインタ、タスク名、スタックサイズ、起動引数、プライオリティ、ハンドラ格納用変数
  * このAPIを呼ぶには `source/FreeRTOSConfig.h` の `configSUPPORT_DYNAMIC_ALLOCATION` が 1 でないといけない
  * タスク登録に成功すると `pdPASS` が返る
* `hello_task()` は無限ループ
  * "Hello world." を出力してサスペンドする
  * `vTaskSupend()` は `vTaskResume()` 待ちとなりディスパッチされなくなる
  * 引数はタスクのハンドラだが、NULLで呼ぶと自身をサスペンドする
* タスク登録後 `vTaskStartScheduler()` でタスクスケジューラを起動して、メイン関数は無限ループに入る
* ここで起動しているタスクは3つ
  - Hello_task
  - IDLE : アイドルタスク
  - Tmr svc : タイマーデーモンタスク
* アイドルタスクとタイマーデーモンタスクは `vTaskStartScheduler()` 呼び出し時に自動的に作成される

```c:source/freertos_hello.c抜粋
int main(void)
{
    /* Init board hardware. */
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    if (xTaskCreate(hello_task, "Hello_task", configMINIMAL_STACK_SIZE + 100, NULL, hello_task_PRIORITY, NULL) !=
        pdPASS)
    {
        PRINTF("Task creation failed!.\r\n");
        while (1)
            ;
    }
    vTaskStartScheduler();
    for (;;)
        ;
}

static void hello_task(void *pvParameters)
{
    for (;;)
    {
        PRINTF("Hello world.\r\n");
        vTaskSuspend(NULL);
    }
}
```

# frdm_k64f_freertos_event

* ROM/RAM は `-O0` でそれぞれ 21736 Byte / 19096 Byte
* イベントフラグを使うサンプル
* FreeRTOSではイベントフラグをイベントグループという
* グローバルでイベントグループハンドラを宣言
* `main()` ではhelloworldと同様の初期化
* `xEventGroupCreate()` でイベントグループを作成、ヒープに確保される
* イベントのビット数は `source/FreeRTOSConfig.h` の `configUSE_16_BIT_TICKS` が 0 であるため 24 ビット
* イベント書き込みタスク `WRITE_TASK_1`, `WRITE_TASK_2`, イベント読み出しタスク `READ_TASK` を作成
  * プライオリティは大きい方が高く `READ_TASK` が `WRITE_TASK_1`, `WRITE_TASK_2` より高くしている
* 2つのイベント書き込みタスクはひたすら `xEventGroupSetBits()` で、それぞれビット0, ビット1を立てる
* 読み出しタスクは `xEventGroupWaitBits()` でイベントを待っている
  * この関数はブロックし、戻り値でイベントビットをとれる
  * 引数はイベントグループハンドル、待受けビットマスク、ビットクリア指定、ビットマスクでしたビットのAND待ち、タイムアウトチック
  * 待受けビットマスクは `B0 | B1 = 0x03` で待受け
  * ビットクリア指定は true なので、イベントが起きた後ビットはクリアされる
  * 第4引数は false なので、ビット0, 1, タイムアウトのいずれかが起きると関数は返る
  * タイムアウトは `portMaxDelay = 0xffffffffUL` となっているが詳細不明
* 読み出しタスクは起きたイベントに応じてログ出力する
* 起動するタスクは同じく、作成した3つとアイドルタスク、タイマーデーモンタスク

```c:source/freertos_event.c抜粋
static EventGroupHandle_t event_group = NULL;

int main(void)
{
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();
    event_group = xEventGroupCreate();
    if (xTaskCreate(write_task_1, "WRITE_TASK_1", configMINIMAL_STACK_SIZE + 100, NULL, tskIDLE_PRIORITY + 1, NULL) !=
        pdPASS)
    {
        PRINTF("Task creation failed!.\r\n");
        while (1)
            ;
    }
    if (xTaskCreate(write_task_2, "WRITE_TASK_2", configMINIMAL_STACK_SIZE + 100, NULL, tskIDLE_PRIORITY + 1, NULL) !=
        pdPASS)
    {
        PRINTF("Task creation failed!.\r\n");
        while (1)
            ;
    }
    if (xTaskCreate(read_task, "READ_TASK", configMINIMAL_STACK_SIZE + 100, NULL, tskIDLE_PRIORITY + 2, NULL) != pdPASS)
    {
        PRINTF("Task creation failed!.\r\n");
        while (1)
            ;
    }
    /* Start scheduling. */
    vTaskStartScheduler();
    for (;;)
        ;
}

static void write_task_1(void *pvParameters)
{
    while (1)
    {
        xEventGroupSetBits(event_group, B0);
    }
}

static void write_task_2(void *pvParameters)
{
    while (1)
    {
        xEventGroupSetBits(event_group, B1);
    }
}

static void read_task(void *pvParameters)
{
    EventBits_t event_bits;
    while (1)
    {
        event_bits = xEventGroupWaitBits(event_group,    /* The event group handle. */
                                         B0 | B1,        /* The bit pattern the event group is waiting for. */
                                         pdTRUE,         /* BIT_0 and BIT_4 will be cleared automatically. */
                                         pdFALSE,        /* Don't wait for both bits, either bit unblock task. */
                                         portMAX_DELAY); /* Block indefinitely to wait for the condition to be met. */

        if ((event_bits & (B0 | B1)) == (B0 | B1))
        {
            PRINTF("Both bits are set.");
        }
        else if ((event_bits & B0) == B0)
        {
            PRINTF("Bit B0 is set.\r\n");
        }
        else if ((event_bits & B1) == B1)
        {
            PRINTF("Bit B1 is set.\r\n");
        }
    }
}
```

実行ログはこのようになる

```
Bit B0 is set.
Bit B1 is set.
Bit B0 is set.
Bit B1 is set.
Bit B0 is set.
Bit B1 is set.
Bit B0 is set.
Bit B1 is set.
Bit B0 is set.
.
.
```

試しにタスクプライオリティを `READ` > `WRITE2` > `WRITE1` にしてみる。
`WRITE1` はディスパッチされなくなり `READ` と `WRITE2` だけで回る。

```
Bit B1 is set.
Bit B1 is set.
Bit B1 is set.
Bit B1 is set.
Bit B1 is set.
Bit B1 is set.
Bit B1 is set.
Bit B1 is set.
.
.
```

今回はここまで。