STM32のFreeRTOSでメッセージキューの使い方を、サンプルコードと合わせてわかりやすく解説します。
メッセージキューとメールキュー
メッセージキューの使い方の前に、CMSIS RTOSではメッセージキューとメールキューの2つのキューがあるので、それぞれの違いを簡単に説明しておきます。
- メッセージキュー
- int型かポインタ型の32ビットデータを送受信できるキュー
- メールキュー
- 32ビットを超えるデータを送受信できるキュー
単なるIDでやり取りできる場合はメッセージキューを使い、構造体などを使って複雑なデータのやり取りをしたい場合はメールキューを使う、という使い分けになるでしょう。
作成するプログラム
それでは今回のゴールを確認しましょう。今回はメッセージキューを使い、2つのスレッド間でデータの送受信を行います。PublisherスレッドはON/OFFデータを送信し、SubscriberスレッドはそのON/OFFデータに基づき、LEDを点灯・消灯します。
このプログラム作成を通してメッセージキューの使い方を覚えましょう。
前提条件
前提条件は目次の記事を参照してください。
プログラムの作成
スレッドの作成
まずはPublisherスレッドとSubscriberスレッドを作成します。STM32CubeIDEのDevice Configuration Tool(STM32CubeMX)を開いてスレッドを作成してください。
- Pinout & Configurationsタブ→Middleware→FREERTOS→Configurationタブ→Tasks and Queue
- 下記の2つのスレッドを作成してください
メッセージキューの作成
次はメッセージキューの作成です。
- Pinout & Configurationsタブ→Middleware→FREERTOS→Configurationタブ→Tasks and Queue
- QueuesのAddボタンをクリックして、Queueの設定画面を開いてください
- 下記の図にある通りに入力し、OKボタンをクリックしてください
ここの設定項目が気になる方は下記の表をご覧になってください。気にせず、とにかく動かしたい方は読み飛ばしてくださいね。
項目 | 説明 |
Queue Name | キューの名前。 |
Queue Size | キューのサイズ。 |
Item Size |
キューに入れるメッセージ1つ分のサイズ。 例えばuint16_tといったデータ型で指定します。 |
Allocation |
メモリの確保の仕方。 Dinamicだと動的確保。 Staticだと静的確保。 |
Buffer Name | バッファの名前。Staticを指定した時のみ使用。 |
Buffer Size | バッファサイズ。自動設定されます。 |
Control Block Name | コントロールブロックの名前。Staticを指定した時のみ使用。 |
スレッドとキューの作成ができたらコードを生成してください。
Publisherスレッドを実装する
ここからは実際にコードを書いていきます。
まずはPublisherスレッドの中身を作ります。Publisherスレッドでは、定期的にON/OFFデータをメッセージキューに入れるだけです。
main.c:Publisherスレッド
void PublisherFunc(void const * argument) { /* USER CODE BEGIN 5 */ uint16_t led = 0; /* Infinite loop */ for(;;) { if (led == 0) { led = 1; } else { led = 0; } osMessagePut(LEDQueueHandle, led, 0); osDelay(1000); } /* USER CODE END 5 */ }
osMessagePut()でメッセージを送信しています。第1引数はメッセージキューのIDで、コード自動生成で定義されたものを指定しています。第2引数でメッセージを渡します。第3引数は送信タイムアウトの設定で、今回は一切待たないようにしました。
Subscriberスレッドを実装する
続いてSubscriber側を作ります。SubscriberスレッドはメッセージキューからON/OFFデータを取り出し、その値に基づいてLEDを点灯・消灯するだけです。
main.c:Sunscriberスレッド
void SubscriberFunc(void const * argument) { /* USER CODE BEGIN SubscriberFunc */ /* Infinite loop */ for(;;) { osEvent event = osMessageGet(LEDQueueHandle, 0); if (event.status == osEventMessage) { if (event.value.v == 0) { HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET); } else { HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET); } } } /* USER CODE END SubscriberFunc */ }
osMessageGet()でイベントを取得しています。第1引数でメッセージキューのIDを指定しています。第2引数は受信タイムアウトの設定で、今回は0なので一切待っていません。
osMessageGet()の返り値であるosEventにイベントの各種情報が含まれています。statusは処理の結果が入っており、これを使ってメッセージが受信できたことを確認しています。valueにはイベントの値、つまりメッセージ自身が入っています。valueは共用体になっていて、value.vだとuint32_t、value.signalsだとint32_t、values.pだとポインタ型で取得できます。
プログラムの作成は以上です。実際に動かしてLEDが点滅することを確認してみてください。
まとめ
STM32のFreeRTOSを使ったメッセージキューの使い方は以上です。そんなに難しいところはなかったと思います。APIもCMSIS RTOSに準拠していて安心して使えますね。
メッセージキューはint型とポインタ型でしか送受信できませんが、もっと大きなデータを使いたい場合はメールキューを使ってください。