
前回は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チャンネルを開くまで待っているのも同じデス。
推しは別だがバイナリ・セマフォを使ってみた、の回でした。
