Azure RTOS習得(4):メッセージキュー

Azure RTOSのキューを使ったプロジェクトを作成し、スレッド間のメッセージ送受信方法を説明します。メッセージキューは、比較的判り易い機能です。先にまとめ、次に詳細の順に説明します。

まとめ

Azure RTOSメッセージキュー送受信
Azure RTOSメッセージキュー送受信

Azure RTOSのスレッド間メッセージ送受信には、キューが用いられます。

STM32G4評価ボードのAzure RTOSメッセージキューサンプルコードが無いため、NUCLEO-G0B1REのTx_Thread_MsgQueueサンプルコードを流用し、AzureRtosQueueプロジェクトを作成しました。

作成したAzureRtosQueueプロジェクトを使い、基本的なAzure RTOSメッセージキュー機能を、STM32G4評価ボードにArduinoプロトタイプシールドを追加し説明、動作確認しました。

本稿説明のAzure RTOS APIは、下記です。

・メッセージキュー作成:tx_queue_create
・メッセージキュー送信:tx_queue_send
・メッセージキュー受信:tx_queue_receiveと、TX_NO_WAIT / TX_WAIT_FOREVER
・メッセージキュー送信時通知:tx_queue_send_notifyと、キュー イベント チェーン

複数のQメッセージを受信スレッドで処理し、かつ、メッセージ無しの時、受信処理を中断する場合は、Azure RTOSキュー イベント チェーン(Queue Event chaining)機能が効果的です。

STM32G4 Azure RTOSサンプルコード探し

現在、STM32G4(Cortex-M4/170MHz)評価ボード:NUCLEO-G474REへのAzure RTOSサンプルコードは、3個、この中にキューサンプルコードはありません。

そこで、逆にExample Selectorから流用できるキューサンプルコード:Tx_Thread_MsgQueueを探します。選定条件は、利用中のCubeIDE版数(v1.9.0)、評価ボード(Nucle-64)に近いものが良いでしょう。

CubeIDEのInformation Centerℹ️でImport STM32CubeMX exampleをクリックし、Example SelectorタブでThreadXにチェックを入れて選定します。

選定したNUCLEO-G0B1REのTx_Thread_MsgQueueコードを、STM32G4へ流用します。

STM32G4 Azure RTOSキューサンプルコードの探し方
STM32G4 Azure RTOSキューサンプルコードの探し方

AzureRtosQueueプロジェクト

下表が、Tx_Thread_MsgQueueの処理内容です。STM32G4評価ボード:NUCLEO-G474REとArduinoプロトタイプシールド用に、赤の工夫を加えました。評価ボード+Arduinoプロトタイプシールドの目的は、Azure RTOS習得(2)を参照してください。

スレッド名 処理内容:Q1/Q2によるメッセージ送受信

(LD2:評価ボード単体+
Arduinoプロトタイプシールド

優先度 プリエンプション閾値
送信スレッド1 500ms毎にSET_GRN_LEDメッセージをQ1へ送信
Q送信失敗時、LD2点灯+停止
5 5
送信スレッド2 1s毎にRESET_GRN_LEDメッセージをQ2へ送信
Q送信失敗時、LD2点灯+停止
5 5
受信スレッド Q1とQ2、両方からメッセージ受信
Q1受信成功時、LED1トグル点灯+VCP出力
Q2受信成功時、LED2トグル点灯+VCP出力
受信失敗時、LD2点灯+停止
10 10

AzureRtosQueueプロジェクト作成

AzureRtosQueueプロジェクト作成方法は、前稿のAzureRtosEventFlagと同じです。汎用テンプレートAzureRtos0をコピー&別名AzureRtosQueueでペーストし、AzureRtosQueueプロジェクトを作成します。ペースト先のAzureRtos0.icoも、AzureRtosQueue.icoへRenameします。

これで、AzureRtosQueueプロジェクトのひな型ができました。

NUCLEO-G0B1REのTx_Thread_MsgQueueコードapp_threadx.c/hを流用し、AzureRtosQueueプロジェクトの、app_threadx.cとapp_threadx.h へ追記します。追記コードの一部抜粋が下記です。

app_threadx.c追記例

app_threadx.c追記ソースコード抜粋(橙色は注意箇所)
app_threadx.c追記ソースコード抜粋(橙色は注意箇所)

app_threadx.h追記例

app_threadx.h追記ソースコード抜粋
app_threadx.h追記ソースコード抜粋

Azure RTOSメッセージキュー

Azure RTOSのスレッド間メッセージ送受信には、メッセージキューが用いられます。

送信スレッド1/2のtx_queue_sendで、Q1/2へメッセージ送信、受信スレッドのtx_queue_receiveで、Q1/2からメッセージ受信、メッセージ内容を確認し、受信成功ならLED1/2をトグル点灯させます。

送信スレッド1/2は、同じ優先度とプリエンプション閾値です。送信間隔が同じ500msだと煩雑ですので、スレッド2は、1秒送信間隔へ変更しました。

Azure RTOSメッセージキューの送受信は、比較的判り易い機能です。メッセージキューを作り(tx_queue_create)、そのQへの送受、STのKnowledge Baseに判り易いアニメもあります。

しかしながら、受信スレッドが、複数Qからのメッセージ処理を行い、かつ、メッセージ無しの時に無限待ち(TX_WAIT_FOREVER)の場合には、キュー イベント チェーン(Event chaining)が効果的です。

本サンプルは、複数Qからの受信処理を行いますが、待ち無し(TX_NO_WAIT)の例です。

AzureRtosQueueプロジェクトVCP出力
AzureRtosQueueプロジェクトVCP出力

キュー イベント チェーン

Azure RTOS ThreadX 機能第 3 章の中程に、“キュー イベント チェーン”の説明があります。簡単に抜粋すると、

本受信スレッドのようにQ1とQ2の両方からメッセージを受信し、メッセージ無しの時、受信中断もある場合は、Q1/Q2に通知関数を登録(tx_queue_send_notify)し、カウントセマフォを使うキュー イベント チェーンが有効。キュー イベント チェーンなしでの実現は“非常に困難”。

流用サンプルコードのreadme.htmlにもキーワード:Event chainingがあります。しかし、このキュー イベント チェーンは未実装です。

カウントセマフォなしでは非常に困難でRTOSらしい機能ですが、各自でお試しください、ということでしょう😢。本ブログもこの方針に従いました。

Azure RTOS習得(2):Azure RTOS ThreadXサンプルコード

Azure RTOS習得2回目は、STM32G4 Azure RTOS ThreadXサンプルコードを解説します(コチラの投稿3章でAzure RTOS開発ツール動作確認に使ったコード)。

STM32G4 Azure RTOS ThreadXサンプルコードは、スレッド優先度とプリエンプション閾値を、スレッド実行時に変更する例と、スレッド間同期にイベントフラグを用いる例を示しています。

STM32G4 Azure RTOS ThreadXサンプルコードの3スレッド

注意)サンプルコードのプリエンプション閾値とREADME記述が異なります。正しくは下記です。

スレッド名 処理内容

(LD2:評価ボード単体)

優先度 プリエンプション閾値
メインスレッド スレッド1/2優先度と閾値変更→LD2点灯シナリオ制御 5 5
スレッド1 LD2を5秒間500msで点滅 10 10
スレッド2 LD2を5秒間200msで点滅 10 9

評価ボード:NUCLEO-G474REは、ユーザLED:LD2を1個実装しています。1個のLD2を2個のスレッド1/2で制御するため、点滅間隔を変えることでどちらのスレッド制御かを示します。また、メインスレッドとスレッド1/2間の同期に、Azure RTOSイベントフラグを用います。

この評価ボードに、2個LED1/2、1個SWを実装したArduinoプロトタイプシールドを追加し、各スレッド処理を、下記のように工夫しました。

スレッド名 処理内容

(LD2:評価ボード+

LED1/LED2/S1:Arduinoプロトタイプシールド

優先度 プリエンプション閾値
メインスレッド スレッド1/2優先度と閾値変更→LD2点灯シナリオ制御 5 5
スレッド1 LED1を5秒間500msで点滅 10 10
スレッド2 LED2を5秒間200msで点滅 10 9

評価ボード単体で複数スレッド動作を確認するよりも、断然判り易くなります。

もちろん、評価ボード単体でも確認可能です。しかし、イベントフラグだけでなくセマフォなど今後様々なAzure RTOS機能習得にも、Arduinoプロトタイプシールド追加は、役立ちます。

また、VCP:Virtual Com Portへメッセージを出力する工夫も加え、タイトルやエラー表示を行います(評価ボード+Arduinoプロトタイプシールド動作例は、5章図)。

イベントフラグ

Microsoft公式Azure RTOS ThreadXサイト第 3 章:Azure RTOS ThreadX 機能を開き、右コラム“この記事の内容”のイベント フラグをクリックすると、イベント フラグの説明が示されます。要旨を抜粋すると、

・イベントフラグは、スレッド間同期手段。32フラグ単位グループ化可能、待ちタイムアウトなどあり
・tx_event_flags_set でフラグ設定、tx_event_flags_get でフラグ取得(AND/OR演算可能)
第 4 章:APIに、tx_event_flags_setやtx_event_flags_getの詳細記述

例えば、4章でtx_event_flags_set は、ソースコードへひな型をコピー&ペーストできる形で表現されています。

tx_event_flags_setのAPI
tx_event_flags_setのAPI

スレッド1/2

以下、説明が簡単になるので、オリジナル評価ボードソースにコメント追記したコードで説明します。

スレッド1/2は、初期設定+無限ループの簡単で単純な構成です。

スレッド1(評価ボード単体動作ソースコード)
スレッド1(評価ボード単体動作ソースコード)

スレッド1は、500msトグルを5秒間繰返し、5秒経過後、イベントフラグ:THREAD_ONE_EVTをセットします。スレッド2は、200msトグルとイベントフラグ:THREAD_TWO_EVTがスレッド1と異なるのみです。

フラグセットAPI失敗時は、Error_Handle内で停止します。

メインスレッド

メインスレッド(評価ボード単体動作ソースコード)
メインスレッド(評価ボード単体動作ソースコード)

メインスレッドは、LD2点滅シナリオを作成します。優先度が5で高優先なので、低優先スレッド1/2からのイベントフラグセットを常時ゲットできます。

スレッド1/2優先度は同一の10ですが、L170でスレッド1のイベントフラグを永遠に待つので、スレッド2はスレッド1と多重動作ができません。

スレッド2のプリエンプション閾値が9なのに、スレッド1が先に動作するのは、スレッド1がtx_thread_createで先に生成、開始(TX_AUTO_START)するからです。試しに、スレッド2を先に生成すると、LD2は200ms点滅から始まりシナリオは進みません。スレッド1→2の生成順序なら、スレッド2のプリエンプション閾値は、10でもLD2は、同じシナリオで点滅します。

スレッド1のイベントフラグを得た後、スレッド2優先度と閾値を(8、8)へ変更するのは、スレッド2単独動作のためです。その後、L179でスレッド2のイベントフラグを永遠に待ちます。

スレッド2のイベントフラグを得ると、スレッド2優先度と閾値を元の(10、9)へ変更します。このシナリオを3回繰返します。

シナリオ終わりにスレッド1/2をterminateさせるのは、動作不要だからです。terminateしなくても、メインスレッドプリエンプション閾値が5なので、スレッド1/2は動作しません。従ってLD2動作シナリオは変わりません。

シナリオは変わりませんが、terminateをコメントアウトした時のThreadX Thread Listとオリジナルの時のListが下記です。不要スレッドは、Terminate Stateへ入れると他へ影響を与えないメリットがあります。

スレッド1と2 terminate有無の差
スレッド1と2 terminate有無の差

RTOS習得とArduinoプロトタイプシールド追加

ベアメタル開発のLチカ理解に相当するのが、本稿説明のスレッド間同期イベントフラグをはじめとする多様なRTOS機能理解です。

本稿サンプルコード動作程度であれば、ベアメタルで開発する方が簡単です。但し、ベアメタルでは、動作に必要な機能を全てユーザが開発します。

一方、RTOS開発では、RTOSが提供する機能を活用し、残りの差分をユーザ開発しさえすればアプリが完成します。下図のようにベンダー提供資産(RTOS、セキュリティなどのミドルウェアやドライバ)有効活用が、現代的MCUユーザアプリケーション開発の肝です。RTOS機能が多すぎるのが、玉に瑕ですが…😂。

現代的ユーザMCU開発の例(出展:The ST blog)
現代的ユーザMCU開発の例(出展:The ST blog)

RTOS活用で、ユーザアプリケーションが資産化できます(RTOSの目的が、アプリケーション資産化なので当然です)。

メインスレッド章で説明したように、スレッド1/2はそのままで、RTOSのスレッド生成順序やプリエンプション、イベントフラグのみでLD2点滅シナリオ変更が簡単にできます。ソフトウェア規模が大きくなれば、このメインテナンス性の良さが活きてきます。

多様なAzure RTOS機能を手間なく効率的に学ぶには、Arduinoプロトタイプシールドを評価ボードに追加し、思いついたRTOS機能を直に試すことをお勧めします。

Arduinoプロトタイプシールド追加により、スレッド毎の動作を別々のLEDで目視でき、メインスレッドがスレッド2の優先度、閾値を変更しない場合、スレッド1と並列動作するか等々、様々な試行を簡単に確認できます。この試行で、ベアメタル開発経験も活かせます。

つまり、過去に開発したベアメタル機能が、RTOSに有るか無いかを、多くのRTOS機能をふるい(経験)にかけながらRTOS習得ができる訳です。

今後のAzure RTOS習得は、Arduinoプロトタイプシールドを評価ボードへ追加した構成で、新規Azure RTOSプロジェクトをRTOS機能毎に作成し行います。

新規Azure RTOS機能プロジェクト作成方法は、次回投稿予定です。

評価ボードへArduinoプロトタイプシールドを追加しスレッド毎にLED点滅中
評価ボードへArduinoプロトタイプシールドを追加しスレッド毎にLED点滅中

まとめ

STM32G4 Azure RTOS ThreadXサンプルコードを解説しました。本稿で説明したAzure RTOS APIは、下記です。

・スレッド間同期イベントフラグ:tx_event_flags_set/getと、待ち処理
・スレッド優先度、プリエンプション閾値変更:tx_thread_priority/preemption_change
・スレッド終了:tx_thread_terminateと、Terminate State目的
・スレッド生成:tx_thread_createと、TX_AUTO_START、生成順序

優先度とプリエンプション閾値をスレッド実行時に変更できる機能は、スレッド開発が容易で流用性を高め、ユーザ開発アプリケーションの資産化に効果があります。

ユーザLEDが1個のみ搭載の評価ボードを使い複数スレッド動作を確認するよりも、2個LEDやユーザS1搭載のArduinoプロトタイプシールド追加により、RTOS APIパラメタ変更時の各スレッド動作確認が容易です。

意図しないスレッド並列処理も直ぐに判るので、効率的にAzure RTOS習得ができます。Arduinoプロトタイプシールド付属の小さなブレッドボード利用も試行実験に便利です。

多くのパラメタを持つAzure RTOS効率的習得に、評価ボード+Arduinoプロトタイプシールドをお勧めします。