
前回はバイナリ・セマフォを使ってみました。バイナリが出来るなら次はカウンティングだろ~ということで今回はカウンティングです。でも御本家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個同時ということは無いことが分かります。予定通りね。