前回はバイナリ・セマフォを使ってみました。バイナリが出来るなら次はカウンティングだろ~ということで今回はカウンティングです。でも御本家FreeRTOS様的には「Notify推し」ということで「セマフォ」はディスられて?ますけど。まあ有るものは使ってみるっと。今回は2つしかないリソースを3人が取り合う形。
※「モダンOSのお砂場」投稿順Indexはこちら
※Arduino IDE上で「スケッチ」形式のソースからFreeRTOSを使って実験してみてます。ターゲット機はArduino UNO R4 Minima。泣く子も黙る?ArmコアのルネサスRA4M1マイコン搭載機です。
カウンティング・セマフォ
前回は、0か1の値しかとらないバイナリ・セマフォでした。排他制御にも使えますが、FreeRTOSの中の人的には排他制御ならmutex用のAPI推しみたい。バイナリ・セマフォはTask間の同期にでも使えよ、ってな感じでした。さらに追い打ちがかかっていて、タスク間の同期ならNotify用のAPIの方がいいんじゃね、と。
今回のカウンティング・セマフォのAPIドキュメントの冒頭にも以下のTIPが掲げられております。1文引用させていただきます。
TIP: ‘Task Notifications’ can provide a light weight alternative to counting semaphores in many situations
やっぱり第72回、第73回でやった「Notify推し」なのね。。。
でもま実験はやります。カウンティング・セマフォの例題としては、同時に利用可能な複数のリソースがあり(今回は2個としました)、それをリソース数を上回るTaskが必要としていて(今回はTask数は3個としました)、お互いに譲り合って(?)シェアするという形が良ろしいかと。
実験に使用したソース
Arduino IDEのスケッチ形式のソースです。今回はリソースを奪い合う?3個のTaskが必要だったので、第71回で使用したソースをちょい変してみました。例によって上記3タスクを後目に吉例Lチカをやり続ける「ループ」タスクもあるので、走っているのは合計4タスクです。
#include <Arduino_FreeRTOS.h> #define NTASKS (3) #define LED_RED (7) #define LOOP_W (100) #define PRIOBASE (1) TaskHandle_t loop_task; TaskHandle_t tasks[NTASKS] = {NULL, NULL, NULL}; const char taskname[NTASKS][6] = {"Task1", "Task2", "Task3"}; TickType_t taskParam[NTASKS] = {1000, 2111, 3333}; SemaphoreHandle_t xSemaphore; const uint8_t queueSize = 5; QueueHandle_t msgQueue; volatile int errCount; void initPins() { pinMode(LED_RED, OUTPUT); digitalWrite(LED_RED, 0); } void loop_thread_func(void *pvParameters) { while (1) { vTaskDelay(LOOP_W); digitalWrite(LED_RED, 0); vTaskDelay(LOOP_W); digitalWrite(LED_RED, 1); } } void task_func(void *pvParameters) { const TickType_t xTimeWait = *(TickType_t *)pvParameters; while (1) { if( xSemaphoreTake( xSemaphore, portMAX_DELAY ) == pdTRUE ) { Serial.print("Get semaphore: "); Serial.println(xTimeWait); vTaskDelay(xTimeWait); xSemaphoreGive( xSemaphore ); Serial.print("Give: "); Serial.println(xTimeWait); vTaskDelay(xTimeWait); } } } void setup() { Serial.begin(115200); while (!Serial) { } initPins(); xSemaphore = xSemaphoreCreateCounting( 2, 2 ); 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; } TaskHandle_t xHandle = NULL; BaseType_t xReturned; for (int i=0; i<NTASKS; i++) { xReturned = xTaskCreate ( task_func, taskname[i], 512 / 4, (void *)&taskParam[i], PRIOBASE, &xHandle ); if (xReturned != pdPASS) { Serial.println("Failed to create task."); return; } else { tasks[i] = xHandle; } } Serial.println("Starting scheduler ..."); vTaskStartScheduler(); for( ;; ); /* Never! */ } /* NEVER CALLED! */ void loop() { }
実験結果
Serial通信使っているので、Arduino IDE付属のシリアルモニタなどでSerialチャンネルを開くまで待っているのもいつもと同じデス。今回は、シリアルモニタに今誰がリソースを確保して動いているのか出力するようにしたので、それを眺めてみますです。
上記で、赤、青、緑の各色で印をつけたのが、Task1、Task2、Task3がリソースを確保して走っているところです。上記をみると、必ず2個以下のTaskだけがアクティブとなって、3個同時ということは無いことが分かります。予定通りね。