モダンOSのお砂場(74) UNO R4、RA4M1でFreeRTOS、バイナリ・セマフォ

Joseph Halfmoon

前回はNotifyを使って割り込み(ISR)とタスク間で「同期」をとってみました。今回は同様なことをSemaphoreを使って行ってみます。でも御本家ページより引用の”TIPS”を上に掲げましたるとおり、Notify使う方が「推し」みたいデス。「でもでも」APIは存在するし、大願成就のためには使ってみるしかないっと。

※「モダンOSのお砂場」投稿順Indexはこちら

※Arduino IDE上で「スケッチ」形式のソースからFreeRTOSを使って実験してみてます。ターゲット機はArduino UNO R4 Minima。泣く子も黙る?Armコアのルネサスマイコン搭載機です。

バイナリ・セマフォ

Googleの生成AI(試験運用中)に問えば、セマフォ(semaphore)は『資源の利用可能な数を表す値で、排他制御や同期を行うために使用されます。』とのお答えであります。

しかしバイナリ(2値)セマフォを生成するためにFreeRTOS御本家の以下のドキュメントを拝見してみます。

xSemaphoreCreateBinary

上記から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チャンネルを開くまで待っているのも同じデス。

推しは別だがバイナリ・セマフォを使ってみた、の回でした。

モダンOSのお砂場(73) UNO R4でFreeRTOS、割り込みISRからNotify へ戻る

モダンOSのお砂場(75) UNO R4でFreeRTOS、カウンティング・セマフォ へ進む