前回はNotifyを使って割り込み(ISR)とタスク間で「同期」をとってみました。今回は同様なことをSemaphoreを使って行ってみます。でも御本家ページより引用の”TIPS”を上に掲げましたるとおり、Notify使う方が「推し」みたいデス。「でもでも」APIは存在するし、大願成就のためには使ってみるしかないっと。
※「モダンOSのお砂場」投稿順Indexはこちら
※Arduino IDE上で「スケッチ」形式のソースからFreeRTOSを使って実験してみてます。ターゲット機はArduino UNO R4 Minima。泣く子も黙る?Armコアのルネサスマイコン搭載機です。
バイナリ・セマフォ
Googleの生成AI(試験運用中)に問えば、セマフォ(semaphore)は『資源の利用可能な数を表す値で、排他制御や同期を行うために使用されます。』とのお答えであります。
しかしバイナリ(2値)セマフォを生成するためにFreeRTOS御本家の以下のドキュメントを拝見してみます。
上記から1か所引用させていただきます。
This makes binary semaphores the better choice for implementing synchronisation (between tasks or between tasks and an interrupt), and mutexes the better choice for implementing simple mutual exclusion.
ということであります。ぶっちゃけ排他制御するなら別にあるmutexを使え、binary semaphoreは同期向けね、ってな感じか。でも冒頭に掲げましたるとおり、同期のためにはbinary semaphoreでなくnotification推しなのでbinary semaphoreの立つ瀬が無いデス(今回は使ってみますケド。)トホホだなあ。
実験に使用したソース
今回もまた前回ソースのチョイ変です。チョイ変の連鎖だな。。。前回はISRからTaskへ向かってNotifyしてみました。割り込み発生をうけてTask側で赤色LEDを3回点滅させてみたっと。今回は同じことをバイナリ・セマフォを使って行ってみます。微妙に点滅カウンタの設定方法を変更したので、前回と点滅の仕方が微妙に違うのだけれども。。。
「排他制御」の場合、リソースを確保した奴が使い終わったリソースを返上するというのが原則ですが、このバイナリ・セマフォの場合、Taskの方は一方的にセマフォを確保(Take)しますが返しませぬ。一方割り込み側では一方的にGiveしてます。
なお、mutexの方だと優先順位とかいろいろ考慮してくれるみたいですが、バイナリ・セマフォAPIの場合はそれも無だと。結構勝手やれるのね。。。
なおいままで同様、無関係に緑色LEDでLチカをやり続けるループタスクは健在。
#include <Arduino_FreeRTOS.h> #define LED_RED (7) #define LED_GREEN (5) #define BUTTON (3) #define LOOP_W (1000) #define LED_W (500) #define PRIOBASE (1) #define ON (0) #define OFF (1) #define LONG_TIME (0xffff) TaskHandle_t loop_task; TaskHandle_t task1; SemaphoreHandle_t xSemaphore = NULL; void initPins() { pinMode(LED_RED, OUTPUT); digitalWrite(LED_RED, 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) { int32_t counter; xSemaphore = xSemaphoreCreateBinary(); while (1) { if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE ) { counter = 3; while(counter-- > 0) { digitalWrite(LED_RED, ON); vTaskDelay(LED_W); digitalWrite(LED_RED, OFF); vTaskDelay(LED_W); } } } } void buttonHandler() { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken ); } 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; } attachInterrupt(digitalPinToInterrupt(BUTTON), buttonHandler, FALLING); Serial.println("Starting scheduler ..."); vTaskStartScheduler(); for( ;; ); /* Never! */ } /* NEVER CALLED! */ void loop() { }
実験結果
前回の実機写真とほぼほぼ同じ(このところそういうのばかりだな)なので実線結果の写真も無っす。
Serial通信使っているので、Arduino IDE付属のシリアルモニタなどでSerialチャンネルを開くまで待っているのも同じデス。
推しは別だがバイナリ・セマフォを使ってみた、の回でした。