今回はArm Cortex-M4コアのルネサスマイコンRA4M1を搭載したArduino UNO R4上で、FreeRTOSのQueueを使ってみたいと思います。複数のタスク間で安全にデータを受け渡しできる便利な構造です。データの送受ができることは当たり前なので、今回は送受にどのくらいの時間がかかるのだが目安を観察。
※「モダンOSのお砂場」投稿順Indexはこちら
FreeRTOSのQueue
Queue構造は、基本、先入れ先出し、入れたものが順番に取り出せるような構造であります。FreeRTOSのQueue構造は、あるタスクがQueueにいれたデータを別なタスクで取り出せるもの。メンドクセー事柄はみなFreeRTOSがやってくれるのでとても便利な仕組みです。詳しいことは御本家の以下ドキュメントなどご覧くだされ。
なお、FreeRTOSのQueueは、以下の記事でも練習してます。
モダンOSのお砂場(20) FreeRTOS、キュー構造。でも別件が気になってしまうっ!
このときも開発環境には Arduino IDE使ってますが、ターゲットがESP32搭載のDevKitC機でした。
まったく同じことをやっても面白くないので、今回は最初のタスクがキューに送り出したタイムスタンプを中継タスクで受信してまた次のキューに入れ、それを末尾のタスクで受信し、受信時のタイムスタンプと比べることでキューを2つ通り抜ける時間の「目安」を知ろうという試みです。当然条件が変われば経過時間など変わるに決まっているカリソメなものね。
なお、キューといいつつ、実験の設定では1段というミニマムさ?です。
実験に使用したソース
ちょっと長くなってしまったのだけれども、そのまま以下に貼り付けます。Arduino IDEの .ino形式(スケッチ)です。
#include <Arduino_FreeRTOS.h> #define AWAITMAX (100) TaskHandle_t loop_task, task1, task2; const uint8_t queueSize = 1; QueueHandle_t msgQueue1, msgQueue2; unsigned long microsMsg1, microsMsg2; void setup() { Serial.begin(115200); while (!Serial) { } msgQueue1 = xQueueCreate(queueSize, sizeof(unsigned long)); msgQueue2 = xQueueCreate(queueSize, sizeof(unsigned long)); auto const rc_loop = xTaskCreate ( loop_thread_func, static_cast<const char*>("Loop Thread"), 512 / 4, nullptr, 1, &loop_task ); if (rc_loop != pdPASS) { Serial.println("Failed to create 'loop' thread"); return; } auto const rc_task1 = xTaskCreate ( task1_func, static_cast<const char*>("Task1"), 512 / 4, nullptr, 1, &task1 ); if (rc_task1 != pdPASS) { Serial.println("Failed to create 'task1' thread"); return; } auto const rc_task2 = xTaskCreate ( task2_func, static_cast<const char*>("Task2"), 512 / 4, nullptr, 1, &task2 ); if (rc_task2 != pdPASS) { Serial.println("Failed to create 'task2' thread"); return; } Serial.println("Starting scheduler ..."); vTaskStartScheduler(); for( ;; ); /* Never! */ } /* NEVER CALLED! */ void loop() { Serial.println(millis()); vTaskDelay(configTICK_RATE_HZ); } void loop_thread_func(void *pvParameters) { unsigned long ts2, ts3; while (1) { if (xQueueReceive(msgQueue2, (void *)&ts2, 0) == pdTRUE) { ts3 = micros(); Serial.print("microsD: "); Serial.print(ts2); Serial.print(" -> "); Serial.print(ts3); Serial.print(" = "); Serial.println(ts3-ts2); } taskYIELD(); } } void task1_func(void *pvParameters) { int count = 0; int waitCount = 0; while (1) { if (count < 10000) { microsMsg1 = micros(); if (xQueueSend(msgQueue1, (void*)µsMsg1, 2) != pdTRUE) { count++; } } vTaskDelay(AWAITMAX); } } void task2_func(void *pvParameters) { int waitCount=0; while (1) { if (xQueueReceive(msgQueue1, (void *)µsMsg2, 0) == pdTRUE) { while (xQueueSend(msgQueue2, (void*)µsMsg2, 2) != pdTRUE) { waitCount++; } } else { taskYIELD(); } } }
実験結果
上記のコードをビルドして走らせ、Arduino IDE付属のシリアルモニタで受信した実行結果が以下に。
Starting scheduler ... microsD: 2399909 -> 2399957 = 48 microsD: 2499899 -> 2499954 = 55 microsD: 2599899 -> 2599954 = 55 microsD: 2699899 -> 2699954 = 55 microsD: 2799899 -> 2799954 = 55 microsD: 2899899 -> 2899954 = 55 microsD: 2999899 -> 2999954 = 55 microsD: 3099899 -> 3099954 = 55 microsD: 3199896 -> 3199944 = 48 microsD: 3299899 -> 3299947 = 48 microsD: 3399896 -> 3399951 = 55 microsD: 3499899 -> 3499954 = 55 microsD: 3599896 -> 3599944 = 48 microsD: 3699899 -> 3699947 = 48
48マイクロ秒か55マイクロ秒で2段の受け渡しができとるということね。意外と速い?いや~もっと?チンタラやっていたらまずいだろうしね。