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

Joseph Halfmoon

前回はバイナリ・セマフォを使ってみました。バイナリが出来るなら次はカウンティングだろ~ということで今回はカウンティングです。でも御本家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チャンネルを開くまで待っているのもいつもと同じデス。今回は、シリアルモニタに今誰がリソースを確保して動いているのか出力するようにしたので、それを眺めてみますです。Result_OK

上記で、赤、青、緑の各色で印をつけたのが、Task1、Task2、Task3がリソースを確保して走っているところです。上記をみると、必ず2個以下のTaskだけがアクティブとなって、3個同時ということは無いことが分かります。予定通りね。

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

モダンOSのお砂場(76) UNO R4でFreeRTOS、ミューテックスを使う一手間 へ進む