前々回、TaskのSuspendとResumeをやっています。これは停める相手の都合などお構いなしに停止するという乱暴な?ものでした。今回は「待っているのでよろしく」「起きてください」とお互いに尊重しあいながら停止、再開できるNotifyを練習してみます。お互いにGive and Takeちゅうことですかい。
※「モダンOSのお砂場」投稿順Indexはこちら
※Arduino IDE上で「スケッチ」形式のソースからFreeRTOSを使って実験してみてます。ターゲット機はArduino UNO R4 Minima。泣く子も黙る?Armコアのルネサスマイコン搭載機です。
RTOS Task Notifications
タスクのNotificationについてのマニュアルページのURLが以下に。
また、例によって FreeRTOSConfig.h 内の設定によってAPIが使えたり不要として使えなかったりするのですが、手元のArduino環境ではデフォルトで以下のマクロが1と定義されていたので使用可能であります。
#define configUSE_TASK_NOTIFICATIONS (1)
上記のマニュアルページをみてみると、このNotificationに関するAPIも数が多いです。タスク間で通知をできるだけでなく、割り込みサービスルーチン(ISR)からの通知も可能です。また、複数の種類の通知を待ったり、細かく条件つけたりといろいろできるようです。その中で今回は一番簡単な以下の「ギブ・アンド・テイク」を練習してみます。
-
- xTaskNotifyGive()
- ulTaskNotifyTake()
「テイク」をしたタスクは、他の誰かが通知してくれるか、設定したタイムアウト上限値に達するまで自主的にブロック状態に入って待ちます。これにより「テイク」時点で整っていない条件などを待つことなどができますな。そして他の誰か(タスクでも良いしISRでも良い)が、該当のTaskにNotificationを「ギブ」してくれると目出度く復活して実行を再開できると。いいのかそんなホンワカした説明で。
実験に使用したソース
前回使用したソースのチョイ変ですが、かなりバッサリやってます。今回は前回のように割り込みも使ってないので。
-
- 2個のTaskが互いにギブ・アンド・テイクで待合ながら交互に動く
- 片方のTaskは待ち合わせから出てくると赤のLEDを一発光らせる
- もう一方のTaskは待ち合わせから出てくると青のLEDを一発光らせる
- よって外から見ていると赤と青が「排他的」に交互に光る
- なお緑のLEDは上記2つのタスクとは無関係なLチカループで勝手に光る
という塩梅です。
#include <Arduino_FreeRTOS.h> #define NTASKS (3) #define LED_RED (7) #define LED_BLUE (6) #define LED_GREEN (5) #define LOOP_W (1000) #define LED_W (500) #define PRIOBASE (1) #define ON (0) #define OFF (1) TaskHandle_t loop_task; TaskHandle_t task1, task2; void initPins() { pinMode(LED_RED, OUTPUT); digitalWrite(LED_RED, OFF); pinMode(LED_BLUE, OUTPUT); digitalWrite(LED_BLUE, OFF); pinMode(LED_GREEN, OUTPUT); digitalWrite(LED_GREEN, OFF); } void loop_thread_func(void *pvParameters) { while (1) { vTaskDelay(LOOP_W); digitalWrite(LED_GREEN, ON); vTaskDelay(LOOP_W); digitalWrite(LED_GREEN, OFF); } } void task_func1(void *pvParameters) { while (1) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); digitalWrite(LED_RED, ON); vTaskDelay(LED_W); digitalWrite(LED_RED, OFF); xTaskNotifyGive(task2); } } void task_func2(void *pvParameters) { while (1) { xTaskNotifyGive(task1); vTaskDelay(LED_W); ulTaskNotifyTake(pdTRUE, portMAX_DELAY); digitalWrite(LED_BLUE, ON); vTaskDelay(LED_W); digitalWrite(LED_BLUE, OFF); } } void setup() { Serial.begin(115200); while (!Serial) { } initPins(); auto const rc_loop = xTaskCreate ( loop_thread_func, static_cast<const char*>("Loop Thread"), 512 / 4, nullptr, PRIOBASE, &loop_task ); if (rc_loop != pdPASS) { Serial.println("Failed to create 'loop' thread"); return; } BaseType_t xReturned = xTaskCreate ( task_func1, "TASK1", 512 / 4, nullptr, PRIOBASE, &task1 ); if (xReturned != pdPASS) { Serial.println("Failed to create task1."); return; } BaseType_t xReturned2 = xTaskCreate ( task_func2, "TASK2", 512 / 4, nullptr, PRIOBASE, &task2 ); if (xReturned2 != pdPASS) { Serial.println("Failed to create task2"); return; } Serial.println("Starting scheduler ..."); vTaskStartScheduler(); for( ;; ); /* Never! */ } /* NEVER CALLED! */ void loop() { }
実験結果
上記のコードをビルドして走らせたところの実機写真が以下に。なお、Serial通信使っているので、Arduino IDE付属のシリアルモニタなどでSerialチャンネルを開くまで待っています。タスクを生成しLEDが点灯を始めるのはSerialチャネルの接続後なのでご注意を。
上の写真は「赤タスク」task1が走っているところで、「青タスク」task2は自主的に「寝ている」状態です。赤が消灯するとtask1がギブして、task2がテイクして今度は青が点灯してます。まあ、交互に点滅しているだけだけれど、裏じゃそれなりのことをRTOSがやっておるのだと。知らんけど。