今回はFreeRTOSのメッセージキューを使ってみます。
投稿時の開発環境を記しておきます。
PC:Windows10 OS
IDE: STM32CubeIDE Version1.3.0
Configurator: STM32CubeMX Version5.6.0
Board: STM32Nucleo-F401RE
(少し前に PlatformIOの記事を書きました。その時に STM32CubeMXのバージョンを 5.6.0 に上げました)
メッセージキューはマルチスレッドプログラムにおいて、スレッド間のデータ通信(タスク間通信とも呼びます)に使うアイテムのひとつです。
ちょっとしたデータを送る場合にはメッセージキューが軽くて便利です。
IDEのプロジェクトを作成する
File – New -STM32 Project を選択し、Boart SelectorタブからNUCLEO-F401REを選択しNextボタンを押します。
Project Nameは RtosMessageQueue としておきます。
言語はCで良いので、そのままFinishを押します。
Initialize all peripherals with their default Mode? と聞いてくるので、Yesを押します。
This kinod of project is associated with the STM32CubeMx perspectiove.
Do you want to open this perspective now?と聞いてくるので、Yesを押します。
FreeRTOSを追加する
Pinout & Configuration – Categories の Middleware – FREERTOSをチェックします。
右側の FREERTOS Mode and Configuration の Mode , Interface から CMSIS_V1 を選択します。
これはOSのバージョンです。
どちらでも良いと思うのですが、安定しているV1の方を使ってみます。
SysTickを変更する
次に Pinout & Configuration – Categories の System Coreの SYS – Timebase Source から TIM5 を選択します。
FreeRTOSでは Timebase Sourceは SysTick 以外を使うことが推奨されています。
タスクを追加する
Modeの下のConfigurationでTasks & Queuesを選択します。
Tasks用の上にある方の Addボタンを押します。
リストの以下の3項目だけ以下の通りに変更してOKボタンを押します。
Task Name : Receiver
Priority : osPriorityNormal
Entry Function : ReceiveFunc
次に最初からリストに登録されている defaultTask をダブルクリックして2項目を以下の通りに編集してOKボタンを押します。
Task Name : Sender
Entry Function : SendFunc
こんな感じですね。
ここで言うタスクはスレッドと同じ意味と考えて構いません。
この先はスレッドという言葉で統一します。
メッセージキューの作成
メッセージキューについては こちら を参考にしてください。
左側の緑色のスレッドから送信(put)し、右の水色のスレッドが受信(get)します。
説明のところに「渡されるデータは、整数型またはポインタ型にすることができます」と書かれています。
(The data to be passed can be of integer or pointer type:)
これは32ビットのデータを送受信できることを意味しています。
Modeの下のConfigurationでTasks & Queuesを選択します。
下の方にある Queues の Addボタンを押し、そのまま OKボタンを押します。
これでメッセージキューを使う準備ができました。簡単ですね。
コーディングする
main.c にすでに準備されている、送信側の関数 SendFunc(), 受信側の関数 ReceiveFunc() の中身をそれぞれ以下のように書きます。
void SendFunc(void const * argument)
{
/* USER CODE BEGIN 5 */
static uint32_t counter = 0;
/* Infinite loop */
for(;;)
{
osMessagePut(myQueue01Handle, counter++, 0);
osDelay(1000);
}
/* USER CODE END 5 */
}
void ReceiveFunc(void const * argument)
{
/* USER CODE BEGIN ReceiveFunc */
/* Infinite loop */
for(;;)
{
osEvent event = osMessageGet(myQueue01Handle, 0);
if (event.status == osEventMessage)
{
if ((event.value.v & 0x01) == 0)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}
}
}
/* USER CODE END ReceiveFunc */
}
ビルドする
ビルドしてエラーがないことを確認しておきましょう。
もし問い合わせのウィンドウが出てきたら Yes を押します。
それでは Run – Resume してプログラムを動かしてみましょう。
コードの概要
コンフィグレーターがある程度のことをやってくれるので助かります。
今回は送信側と受信側の関数内にコードを書くだけですみました。
osDelay()
HAL の HAL_Delay()を時間待ちで良く使っていますが、FreeRTOSには同じような機能で osDelay()があります。
引数の単位は msec です。
osDelay()は時間待ちをして、処理を他のスレッドに譲ります。
タイムスライス
SendFunc()とReceiveFunc()は優先順位が同じなので、osDelay()のような故意の切り替え処理を実装しなくても、OSが適切なタイムスライスで処理を切り替えてくれます。
FreeRTOS.hで以下のように定義されています。(configUSE_TIME_SLICING が 1 で切り替えを行う)
#ifndef configUSE_TIME_SLICING
#define configUSE_TIME_SLICING 1
#endif
SendFunc()内に書いた osDelay()を HAL_Delay()に置き換えてみてください。その状態でもスレッドの切り替えが行われることが確認できます。
送信側
osMessagePut(myQueue01Handle, counter++, 0);
第1引数:osMessageCreate()で取得した osMessageQId型の値
第2引数:送信する32ビットの値
第3引数:タイムアウト値(タイムアウトがない場合は0を指定する)
受信側
osEvent event = osMessageGet(myQueue01Handle, 0);
戻り値: osEvent型
この変数 event の statusメンバが osEventMessage である時にメッセージを受信したことになる。
そしてその時の event.value.v が送られてきた 32ビットの値
if()文のところは値が偶数なら
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
でLEDを点灯させて、奇数なら消灯させている。
今回は比較的簡単に FreeRTOS のメッセージキューを体感できました。
皆さん、いかがでしたか?うまくLチカできましたか。
コメントを書く