今回は FreeRTOS Ver2 のメッセージキューについて説明します。
投稿時の開発環境を記しておきます。
PC:Windows10 OS
IDE: STM32CubeIDE Version1.6.0
Configurator: STM32CubeMX Version6.2.1
Board: STM32Nucleo-F401RE
メッセージキューとは
複数のスレッド間でデータの受け渡しを行いたい場合があります。
そんな時にメッセージキューを使います。
FreeRTOS V1 ではメールキューとメッセージキューがありました。
V2ではV1のメールキューをメッセージキューとして使うようになったようです。
構造体などのように大きなデータを送ることができます。
プロジェクトを作成する
IDEを起動し、File- New – STM32 Project を選択し、Target Selection ウィンドウが出たら Board Selector タブを選択し Boards List から NUCLEO-F401RE を選択し Next ボタンを押します。
Project 名に F401RtosV2MessageQueue と入力し、Finishボタンを押します。
Initialize all peripherals with their default Mode ? と聞いてくるので Yesを押します。
RTOSを使えるように設定する
Pinout & Configuration – Categories – Middleware – FREERTOS を選択し、
Mode の Interface で CMSIS_V2 を選択します。
その下の Configuration – Tasks and Queues タブを選択し、 まず上(Tasks)のAddボタンを押します。
New Task のウィンドウが出てくるので、そのまま OK ボタンを押します。
次にその下の Queues のAddボタンを押します。
New Queue のウィンドウが出てくるので、そのまま OK ボタンを押します。
これでタスク(スレッど)が2つつくられて、メッセージキューもひとつつくられます。
タイムベース ソースを変更する
Categories – System Core – SYS を選択し、SYS Mode and Configuration – Mode で
Timebase Source を Systick から TIM10 に変更しておきます。
(FreeRTOSでは Systick 以外を使うことが推奨されているからです)
ビルドしてエラーがないことを確認しておきます。
生成された関数を確認しておく
main.cのmain()関数内で、while()ループの少し手前に以下のコードがあることを確認しておきます。
コメントは省略しています。
myQueue01Handle = osMessageQueueNew (16, sizeof(uint16_t), &myQueue01_attributes);
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
myTask02Handle = osThreadNew(StartTask02, NULL, &myTask02_attributes);
今回は構造体を送ることにするので1行目を以下のように編集します。
myQueue01Handle = osMessageQueueNew (MSGQUEUE_OBJECTS, sizeof(MSGQUEUE_OBJ_t), &myQueue01_attributes);
第1引数はキューの数を指定します。
第2引数には構造体で使うメモリの大きさを指定します。
次に使用する構造体の宣言を行っておきましょう。
main()の手前あたりに以下の記述をおこないます。
(どんな構造体でも構いません。以下は一例です)
/* USER CODE BEGIN 0 */
#define MSGQUEUE_OBJECTS 16 //メッセージキューオブジェクトの数
typedef struct
{ //オブジェクトのデータ型
uint8_t Buf [32];
uint8_t Idx;
} MSGQUEUE_OBJ_t;
/* USER CODE END 0 */
最初にキューが、その後2つでスレッドがつくられていることがわかります。
設定するだけで、初期化コードを出力してくれるので便利ですね。
コーディングと動作確認
StartDefaultTask()でメッセージを送信し、StartTask02()で受信してみます。
送信側は以下のようにコーディングします。
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN 5 */
/* Infinite loop */
MSGQUEUE_OBJ_t msg = {};
for(;;)
{
osDelay(1000);
msg.Idx = 3;
msg.Buf[0] = '7';
osMessageQueuePut(myQueue01Handle, &msg, 0U, 0U);
}
/* USER CODE END 5 */
}
受信側は以下のようにコーディングします。
void StartTask02(void *argument)
{
/* USER CODE BEGIN StartTask02 */
MSGQUEUE_OBJ_t msg = {};
osStatus_t status;
__IO int flg = 0;
uint8_t c = 0;
/* Infinite loop */
for(;;)
{
status = osMessageQueueGet(myQueue01Handle, &msg, 0U, 0U);
if (osOK == status)
{
if (msg.Idx == 3)
{
flg = 1;
}
else
{
flg = 0;
}
c = msg.Buf[0];
}
}
/* USER CODE END StartTask02 */
}
送信には osMessageQueuePut()を、受信は osMessageQueueGet()を使います。
第1引数には osMessageQueueNew()でつくられた戻り値を指定します。
第2引数には 構造体のアドレスを指定します。
ビルドして実行する
ビルドしてエラーがないことを確認後、実行します。
StartTask02()のスレッドで1秒毎に送信した構造体データを受信できれば成功です。