Previous: 5.1.4. バイナリセマフォを取る |
Up: 5. 割込み処理 |
Next: 6. クリティカルセクション |
サンプルプログラム |
freertos_interrupt_counting_semaphore_tim4 |
前項のバイナリセマフォは、セマフォが取り得る状態というのは、セマフォが有るか無いかのどちらかでした。カウンティングセマフォは、一つのセマフォの中に、複数個分のの実行許可を持つものです。
例えばあなたがとある大学の学生寮に住んでいるとします。住んでいる学生の数は10人です。この学生寮には3台の共用の自転車が有ります。自転車を使うときには、寮の玄関に掛けてある鍵を取り、使い終わったらすぐに返すというルールになっています。自転車を使いたい学生は多いのですが、自転車が3台しか有りませんから、鍵を取った学生が自転車を使える、鍵をとれなかった学生は鍵が返されるまで待つということになります。この例での鍵置き場と鍵がカウンティングセマフォに相当します。つまり利用できる資源が一つではないが一定数以下しかない場合に、鍵置き場と鍵(カウンティングセマフォ)を利用して、資源を利用できる者を管理するという仕組みな訳です。
マイコンの場合であれば、例えばSTM32のように、ADCが二つしかないが、多くのセンサが接続されていて、複数のタスクがADCを共用したい場合に、カウンティングセマフォを利用すれば、それぞれのタスクに資源配分を管理できるようになります。
カウンティングセマフォを利用する場合には、前項と同じように、プログラムの冒頭でセマフォのハンドラを定義しておきます。ハンドラの型はxSemaphoreHandleで同じです。
static xSemaphoreHandle xCountingSemaphore; |
次にxSemaphoreCreateCounting関数を使用してカウンティングセマフォを実際に作成します。xSemaphoreCreateCountingも実際にはマクロですがここでは関数として説明します。
関数名 |
xSemaphoreCreateCounting関数 |
|
関数プロトタイプ |
xSemaphoreHandle xSemaphoreCreateCounting ? ( ??? unsigned portBASE_TYPE uxMaxCount, ??? unsigned portBASE_TYPE uxInitialCount ? ) |
|
動作 |
カウンティングセマフォを作成します。 |
|
引数 |
uxMaxCount |
作成するカウンティングセマフォで与えることのできるセマフォの最大数を指定します。 |
uxInitialCount |
セマフォ作成時の初期状態ですでに与えられているセマフォの数を指定します。 |
|
戻り値 |
セマフォのハンドラ。セマフォを作成できなかったときはNULLを返します。 |
サンプルプログラムでは以下のようにして、最大の容量が10のセマフォを作成しています。
xCountingSemaphore = xSemaphoreCreateCounting(10,0); |
これまでと同じように、セマフォが作成できない場合を想定して、対応するルーチンを記載しておきましょう。サンプルプログラムでは、セマフォがNULLで無い場合のみ、スケジューラを開始し、NULLであった場合にはエラーメッセージを表示させています。
if( xCountingSemaphore != NULL ) ? { ??? xTaskCreate(vHandlerTask, (signed portCHAR *)"HandlerTask", 192, NULL, 3, NULL); ??? vTaskStartScheduler(); ? } else ? { ??? cprintf("Failed to create semaphore.\r\n"); ? } |
カウンティングセマフォにセマフォを与えるには、バイナリセマフォの場合と同様xSemaphoreGiveFromISR関数を使用します。サンプルプログラムは長いので引用しませんが、要は、1000ms毎に3つのカウンティングセマフォを与えるルーチンになっています。各1000msの期間の開始時から50ms、100ms、150msが経過する時点で、セマフォを与えています。なおカウンティングセマフォに入っているセマフォの数をカウントするため、セマフォを与える度にsemaphore_counter変数をインクリメントしています。
カウンティングセマフォからセマフォを取るには、バイナリセマフォと同様、xSemaphoreTake関数を使用します。
サンプルプログラムでは以下のようにして、200ms毎にセマフォを取っています。
while(1) ? { ??? // Take semaphore to wait for interrupt ??? xSemaphoreTake (xCountingSemaphore, portMAX_DELAY); ?? ?//Print message through COM ??? delay_ms(200); ??? semaphore_counter--;
??? cprintf("Handler took semaphore.\r\n"); ??? Print_Decimal(semaphore_counter); ??? cprintf(" semaphore pending.\r\n"); ? } |
サンプルプログラムでは、周期中、0msから150msまでの間に、セマフォが連続して与えられます。この時点でセマフォの数は3つです。その後200ms毎にセマフォを取っているので、600ms経過して、3回セマフォを取った時点でセマフォが0になっています。200ms毎に実行されているので1秒間に5回実行されそうですが、4回目の時点では取るセマフォが無いので、Blocked状態になります。
Tick. 0 semaphore pending. ISR gave semaphore. 1 semaphore pending. ISR gave semaphore. 2 semaphore pending. ISR gave semaphore. 3 semaphore pending. Handler took semaphore. 2 semaphore pending. Handler took semaphore. 1 semaphore pending. Handler took semaphore. 0 semaphore pending. Tick. 0 semaphore pending. ISR gave semaphore. 1 semaphore pending. |
なお本来であれば、カウンティングセマフォに入っているセマフォの数のカウントはOS自体の機能として備わっているのが望ましいと考えられます。ただFreeRTOS自体には、カウンティングセマフォ用のセマフォのカウント用関数は用意されていません。またキューのカウント用関数であるuxQueueMessagesWaiting関数を使ってみたところ(freertos_interrupt_counting_semaphore_tim4_get_num_by_func)、実際のセマフォの数とはずれが出ました。今後の改善に期待したいところです。
Previous: 5.1.4. バイナリセマフォを取る |
Up: 5. 割込み処理 |
Next: 6. クリティカルセクション |