Azure RTOSミューテックスとバイナリセマフォを使ったプロジェクトを作成し、スレッド間の排他制御方法を説明します。Azure RTOSでは、ミューテックスとバイナリセマフォが、同じ機能であることも判ります。
先にまとめ、次に詳細の順で説明します。
まとめ
Azure RTOS排他制御には、ミューテックスやリソースアクセス数1のバリナリセマフォが使われます。
STM32G4評価ボードにミューテックスとバリナリセマフォサンプルコードが無いため、STM32G0C1E-EVのTx_Thread_Syncサンプルコードを流用し、AzureRtosMutexSemaプロジェクトを作成しました。
作成したAzureRtosMutexSema プロジェクトを使い、Azure RTOS排他制御を、STM32G4評価ボードにArduinoプロトタイプシールドを追加し、ミューテックスは、バイナリセマフォと同じ動作結果をもたらすことを示しました。
リソースアクセス数2以上のカウントセマフォは、排他制御だけでなくイベント通知にも使えますが、優先度逆転など注意事項もあります。
本稿説明のAzure RTOS APIは、下記です。
・ミューテックス/セマフォ取得:tx_mutex / semaphore_getと、TX_NO_WAIT
・ミューテックス/セマフォ開放:tx_mutex / semaphore_put
・スレッド処理中断:tx_thread_sleepと、コンテキストスイッチ
・セマフォ優先度の逆転、ミューテックス優先度の継承と、TX_INHERIT / TX_NO_INHERIT
単純なAzure RTOS排他制御は、ミューテックス利用が良さそうです。
STM32G4 Azure RTOSサンプルコード探し
STM32G4(Cortex-M4/170MHz)評価ボード:NUCLEO-G474REへのAzure RTOSサンプルコードは、現在3個、この中にミューテックスやセマフォを使うサンプルコードはありません。
対策は、キューサンプルコードの前稿と同じです。CubeIDEのInformation Centerℹ️でImport STM32CubeMX exampleをクリックし、Example SelectorタブでThreadXにチェックを入れて流用できるサンプルコードを選定します。
選定したSTM32G0C1E-EVのTx_Thread_Suncコードを、STM32G4へ流用します。
Tx_Thread_Syncプロジェクト
ミューテックスやセマフォは、スレッド間の排他制御に用います。しかし、サンプルプロジェクト名は、“Sync”:同期となっていて違和感があります。
サンプルプロジェクトのreadme.htmlを読むとミューテックスとバリナリセマフォを使っていますので、ミューテックスとバイナリセマフォの排他制御利用例であることは、間違いありません。
サンプルの2つのスレッドは、それぞれLED点灯の独立した制御です。プロジェクトが示したいのは、LED点灯の直列制御、この直列化のため、ミューテックス、または、バイナリセマフォを同期手段として用いたからと筆者は、解釈しました。
つまり、手段のミューテックスやバイナリセマフォよりも、制御結果からプロジェクト名を付けたのだと思います。
面白いのは、このTx_Thread_Syncは、1プロジェクト内で、ミューテックスとバイナリセマフォをマクロで切替え、両者が同じ結果をもたらすことを示している点です(最初のVCP出力参照)。
しかしながら弊社は、Azure RTOSの排他制御手段でのプロジェクト名や日本語コメントを付けます。
AzureRtosMutexSemaプロジェクト
下表が、Tx_Thread_Syncの処理内容です。STM32G4評価ボード:NUCLEO-G474REとArduinoプロトタイプシールド用に、赤の工夫を加えました。
スレッド名 | 処理内容:スレッド1/2のLED点灯排他制御
(LD2:評価ボード単体+ |
優先度 | プリエンプション閾値 |
スレッド1 | 1) 排他オブジェクト取得確認 2) 成功時LED1 500ms/5秒トグル後、オブジェクト開放 3) 失敗時排他オブジェクト取得待ち |
10 | 10 |
スレッド2 | 1) 排他オブジェクト取得確認 2) 成功時LED2 500ms /5秒トグル後、オブジェクト開放 3) 失敗時排他オブジェクト取得待ち |
10 | 10 |
排他オブジェクトは、マクロでTX_MUTEX定義済みならミューテックス、未定義ならバイナリセマフォ利用に変更できます。
AzureRtosMutexSemaプロジェクト作成
AzureRtosMutexSemaプロジェクト作成方法も、AzureRtosEventFlagの時と同じです。
汎用テンプレートAzureRtos0をコピー&別名AzureRtosMutexSemaでペーストし、AzureRtosMutexSemaプロジェクトを作成します。ペースト先のAzureRtos0.icoは、AzureRtosMutexSema.icoへRenameします。
これで、AzureRtosMutexSemaプロジェクトのひな型ができました。
STM32G0C1E-EV のTx_Thread_Syncコードapp_threadx.c/hを流用し、AzureRtosQueueプロジェクトの、app_threadx.cとapp_threadx.h へ追記します。追記コードの一部抜粋が下記です。
app_threadx.c追記例
app_threadx.h追記例
Azure RTOSミューテックスとバイナリセマフォ
サンプルコードは、ミューテックスがデフォルト利用ですので、Azure RTOSミューテックスで説明します。
スレッド1/2は、優先度、プリエンプション閾値ともに同じです。
しかし、スレッド1を先に生成し即実行(TX_AUTO_START)しますので、常にスレッド1が排他オブジェクト:ミューテックスを先に取得(tx_mutex_get)し、LED1トグル点灯を5秒間続けます。5秒経過後、ミューテックスを開放(tx_mutex_put)し、1ティックの10msスリープ(tx_thread_sleep)します。この処理を繰返します。
スレッド2は、開始後オブジェクト:ミューテックス取得を狙いますが、スレッド1取得済みのため待ち無し(TX_NO_WAIT)で取得狙いを繰返します。スレッド1のミューテックス解放で、ミューテックスを取得し、LED2トグル点灯を5秒間続けます。5秒経過後、ミューテックスを開放し、10msスリープするのは、スレッド1と同じです。
ミューテックスオブジェクトにより、スレッド1/2が排他動作します。
ここで、ミューテックス解放後の1ティックスリープは重要です。このスリープが、スレッド1/2のコンテキストスイッチを行います。試しにスリープをコメントアウトすると、排他制御が働きません。
app_thread.x.h L60のマクロUSE_TX_MUTEXをコメントアウトすると、バイナリセマフォ(tx_semaphore_get/tx_semaphore_put)を使ってミューテックスと同じ動作結果が確認できます。
Azure RTOSサイトのカウントセマフォを読むと、カウントセマフォは、排他制御だけでなくイベント通知にも使用可能です(前稿キュー イベント チェーンがその利用例)。また、デットロックやスレッド優先度の落とし穴、“優先度の逆転”(Priority Inversion)などの利用時注意事項もあります。
排他制御だけなら、ミューテックス利用がシンプルで良さそうです。但し、“優先度の逆転”対策の“優先度の継承”(Priority Inheritance)オプション:TX_INHERITが必要です。サンプルコードは、TX_NO_INHERITです。
※優先度の逆転、優先度の継承を試すには、スレッド1/2以外の第3のスレッドが必要です。第3スレッドは、スレッド1/2の中間の優先度と、1/2排他制御とは無関係なことも必要です。この逆転、継承もRTOSらしい機能ですが、サンプルコード実装は無く、各自でお試しください、ということでしょう。
ミューテックス/バイナリセマフォ排他制御の結果、ArduinoプロトタイプシールドのLED1とLED2が、交互に点滅を繰返すことが確認できます。
オリジナルプロジェクト名:“Sync”が示すようにLED1/2は同期して交互点滅している、と記述することも可能です。
RTOS文章直列記述→並列動作マッピング
本サンプルコードは、わずか2個スレッドです。それでも、RTOS処理を文章で記述する難しさを感じます。文章は、動作を「直列」で記述することが得意だからです。
RTOS処理は、複数のスレッドが「並列」に動作します。勿論、シングルコアで時分割動作なので、実際に動作しているのは、RTOSを含め1個です。ですが、これを判り易く文章化するのは、結構難しいことです。
例えば、本稿スレッド1/2のtx_thread_sleepです。開発者同士なら、ソースコードを見せれば、それで事足ります。しかし、そもそもRTOS理解レベルが不明の顧客へ、スリープの目的や意味を説明しても、判ってもらえるでしょうか?
RTOS開発は、開発アプリの顧客向け資料作成でも苦労しそうです😭。
RTOS習得には、公式サイト文章記述の各種RTOS機能を、サンプルコードへ変換後、さらに、個々の開発者が、サンプルコードと評価ボードを使って「納得するまで色々変えてみること」が必要だと思います。
この試行で、直列記述の文章で表現されたRTOS動作が、開発者の中でRTOS並列動作へマッピングされます。RTOS習得・開発には、並列動作マッピングの過程が最重要だと思います。