今回はFreeRTOSのメールキューを使ってみます。
投稿時の開発環境を記しておきます。
PC:Windows10 OS
IDE: STM32CubeIDE Version1.3.0
Configurator: STM32CubeMX Version5.6.0
Board: STM32Nucleo-F401RE
メールキューはマルチスレッドプログラムにおいて、スレッド間のデータ通信(タスク間通信とも呼びます)に使うアイテムのひとつです。
少し前にメッセージキューの記事を書きました。
ちょっとしたデータを送る場合にはメッセージキューが軽くて便利ですが、大きなデータを扱いたい場合にはメールキューを使います。
IDEのプロジェクトを作成する
File – New -STM32 Project を選択し、Boart SelectorタブからNUCLEO-F401REを選択しNextボタンを押します。
Project Nameは RtosMailQueue としておきます。
言語は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項目を以下の通りに編集します。
Task Name : Sender
Entry Function : SendFunc
こんな感じですね。
ここで言うタスクはスレッドと同じ意味と考えて構いません。
この先はスレッドという言葉で統一します。
メールキューの実装
メールキューについては こちら を参考にしてください。
左側の緑色のスレッドがメモリ領域を確保して送信(put)し、右の水色のスレッドが受信後、メモリ領域を解放する。
という流れになります。
メッセージキューの時と違い、大きなデータを扱う分メモリを確保・解放する必要があります。
メモリの解放忘れがないように注意します。
コーディングする
メールキューのコードはコンフィグレーターが生成してくれないので自前で用意する必要があります。
あまり意味のあるコードではないのですが、構造体などの32ビットを超えるデータの送受信ができることを確認してみます。
main()の付近で以下のようにコーディングします。
/* USER CODE BEGIN 0 */
#define COUNTER_MAX 256
typedef struct
{
uint8_t buffer[COUNTER_MAX];
uint32_t counter;
} strData_t;
osMailQId strMailHandle;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
osMailQDef(strMail, 8, strData_t);
strMailHandle = osMailCreate(osMailQ(strMail), NULL);
/* USER CODE END 1 */
下の方にある2つのスレッドを以下のようにコーディングします。
void SendFunc(void const * argument)
{
/* USER CODE BEGIN 5 */
strData_t data;
for (int i = 0; i < COUNTER_MAX; i++)
{
if (i & 1)
{
data.buffer[i] = 1;
}
}
/* Infinite loop */
for(;;)
{
strData_t *p = osMailAlloc(strMailHandle, osWaitForever);
p = &data;
osMailPut(strMailHandle, p);
osDelay(100);
}
/* USER CODE END 5 */
}
void ReceiveFunc(void const * argument)
{
/* USER CODE BEGIN ReceiveFunc */
static int i = 0;
/* Infinite loop */
for(;;)
{
osEvent event = osMailGet(strMailHandle, osWaitForever);
if (event.status == osEventMail)
{
strData_t *p = (strData_t*)event.value.p;
if ((p->buffer[i] & 0x01) == 0)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}
osMailFree(strMailHandle, p);
if (++i >= COUNTER_MAX)
{
i = 0;
}
}
}
/* USER CODE END ReceiveFunc */
}
ビルドする
ビルドしてエラーがないことを確認しておきましょう。
もし問い合わせのウィンドウが出てきたら Yes を押します。
それでは Run – Resume してプログラムを動かしてみましょう。
コードの概要
メールキューの生成は自前で行わなければなりません。
定義とメールキューの生成
typedef struct
{
uint8_t buffer[COUNTER_MAX];
uint32_t counter;
} strData_t;
osMailQId strMailHandle;
構造体のメンバに counter がありますが、これは使っていません。
(構造体のデータを送れることを確認するためにメンバにしておきました)
それから osMailQId型の変数を定義します。
osMailQDef(strMail, 8, strData_t);
strMailHandle = osMailCreate(osMailQ(strMail), NULL);
osMailDef() でメールキューに使う型とそのサイズと名前を関連付けます。
第1引数:名前を指定する
第2引数:キューのサイズを指定する
第3引数:キューで使う型を指定する
osMailCreate() でメールキューを作成します。
第1引数:先ほど関連付けした名前を指定する
第2引数:NULLを指定する。
送信側
strData_t *p = osMailAlloc(strMailHandle, osWaitForever);
p = &data;
osMailPut(strMailHandle, p);
osDelay(100);
osMailAlloc()でメモリ領域を取得します。
第1引数:osMailCreate()で取得したosMailQid型の値を指定する。
第2引数:待ち時間を指定する。単位はmsec、osWaitForeverを指定すると無制限に待つ。
p = &data; で取得したメモリ領域に構造体の変数を代入します。
osMailPut()で送信します。
第1引数:osMailCreate()で取得したosMailQid型の値を指定する。
第2引数:osMailAlloc()で返ったデータ型へのポインタを指定する。
受信側
osEvent event = osMailGet(strMailHandle, osWaitForever);
if (event.status == osEventMail)
{
strData_t *p = (strData_t*)event.value.p;
(略)
}
osEvent event = osMessageGet(myQueue01Handle, 0);
戻り値: osEvent型
この変数 event の statusメンバが osEventMail である時にメッセージを受信したことになる。
そしてその時の event.value.p が送られてきた データ型のポインタになっている。
第1引数:osMailCreate()で取得したosMailQid型の値を指定する。
第2引数:0を指定する。
(略)としているがそれ以降の if() 文のところは値が偶数なら
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
でLEDを点灯させて、奇数なら消灯させている。
処理内容はメッセージキューと同じになっていますが、構造体のデータを送受信できることを確認できました。
皆さん、いかがでしたか?うまくLチカできましたか。
コメントを書く