データをタスク間で転送する場合に使うバッファがキュー(Queue)で、待ち行列の
構成となっています。
【キューの働き】
キューバッファつまり待ち合わせ行列は下図のような動作をさせるときに使います。
例えば、タスクが3個あり、Task3が液晶表示器に表示出力するタスクとします。
これに対し、Task1とTask2の2つのタスクから表示データを送って表示要求をするもの
とします。Task1とTask2は全く非同期で表示出力するものとします。
Task3は表示要求が来たらすぐ表示をしますが、2つのタスクからの表示要求が
重なったら同時には動作できませんから、どちらかを優先して後の要求を待たせる
必要があります。この「待たせる」という動作をさせるためにキューバッファを使います。
つまり、すべての表示要求は、いったんキューバッファに蓄え、早いものから順に
取り出しては表示するようにします。(FIFO(First-In First-Out)動作)
こうすれば、同時に表示要求が来ても待たせることができますし、表示する動作に
時間がかかる場合でも表示終了までバッファに貯めておくことで、ゆっくり処理すること
ができます。
FreeRTOSのキューバッファ動作は、データそのものをキューバッファにコピーします
ので、取り出す場合にもデータ全部を取り出せる一時バッファが必要となります。
この場合の出し入れするデータサイズは、キューを確保するときのパラメータとして
固定長として与えます。
【キュー制御用関数】
キューを制御する基本のAPI関数には下記が用意されています。
関数名 機能と書式 xQueueCreate キューバッファの生成
《書式》
xQueueHandle xQueueCreate(unsigned portBASE_TYPE uxQueueLength,
unsigned portBASE_TYPE uxItemSize);
uxQueueLength :格納する最大アイテム数
uxItemSize :アイテムのバイト数(固定長)
戻り値 :確保できたらハンドル値 不可なら0
《使用例》
lcdQueue = cQueueCreate(5, 17); //17バイト幅で5個分のキューxQueueSend キューに送信(キューの最後尾に書き込み) xQueueSendToBackと同じ
割り込み処理内では使用禁止
《書式》
portBASE_TYPE xQueueSend(xQueueHandle xQueue,
const void *pvItemToQueue,
portTickType xTicksToWait);
xQueue :キューのハンドル値
pvItemToQueue :キューへ送るデータへのポインタ
xTicksWait :キュー空きを待つ最大時間
portMAX_DELAYとすると永久待ちとなる
戻り値 :成功でpdTRUE 失敗でerrQUEUE_FULL
《使用例》
Msg[] = "Display Data xxxx";
xQueueSend(lcdQueue, Msg, portMAX_DELAY);xQueueSendToBack キューに送信(キューの最後尾に書き込み) xQueueSendと同じで
書式もxQueueSendと同じ。割り込み処理内では使用禁止
xQueueSendToFront キューに送信(キューの最前部に書き込み)
書式はxQueueSendToBackと同じ。割り込み処理内では使用禁止
xQueueReceive キューからデータを取り出す。取り出したデータを格納するアイテムサイズと同じ長さのバッファを用意する必要がある
割り込み処理内では使用禁止
《書式》
portBASE_TYPE xQueueReceive(xQueueHandle xQueue, void *pvBuffer,
portTicksType portTicksToWait);
xQueue :キューのハンドル値
pvBuffer :取り出し先のバッファのポインタ
portTicksToWait:キューが空のときデータ送信を待つ最大時間、
portMAX_DELAYとすると永久待ち
戻り値 :正常ならpdTRUE 失敗ならpdFALSE
《使用例》
unsigned char Buf[20];
xQueueReceive(lcdQueue, Buf, portMAX_DELAY);xQueuePeek キューからデータを取り出す。キューのデータはそのまま残す
取り出したデータを格納するバッファが必要
割り込み処理内では使用禁止
《書式》
portBASE_TYPE xQueuePeek(xQueueHandle xQueue, void *pvBuffer,
portTicType xTicksToWait);
xQueue :キューのハンドル値
pvBuffer :取り出し先のバッファのポインタ
xTicksToWait:キューが空のときデータ送信を待つ最大時間、
portMAX_DELAYとすると永久待ち
戻り値 :正常ならpdTRUE 失敗ならpdFALSExQueueSendFromISR 割り込み処理ルーチン内で使うxQueueSend
データをキューにコピーするので、データサイズは小さくすべき
《書式》
portBASE_TYPE xQueueSendFromISR(xQueueHandle xQueue,
const void *pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskjWoken);
xQueue :キューのハンドル値
pvItemToQueue :キューへ送るデータへのポインタ
pxHigherPriorityTaskWoken:pdTRUEへのポインタとすると
キューを送った先のタスクのブロックを解除し優先レベル
を現在実行タスクより高くする
pdFALSEへのポインタとした場合には割り込み処理の最後で
portSWITCH_CONTEXT()を実行してコンテキストの切り替えを
する必要がある
戻り値 :正常ならpdTRUE 、失敗ならpdFALSExQueueSendToBackFromISR 割り込み処理ルーチン内で使うxQueueSendToBackで
書式はxQueueSendFromISRと同じxQueueSendToFrontFromISR 割り込み処理ルーチン内で使うxQueueSendToFrontで
書式はxQueueSendFromISRと同じxQueueReceiveFromISR 割り込み処理ルーチン内で使うxQueueReceive
《書式》
portBASE_TYPE xQueueReceiveFromISR(xQueueHandle xQueue,
void *pvBuffer, portBASE_TYPE *pxTaskWoken);
xQueue :キューのハンドル値
pvBuffer :取り出し先のバッファのポインタ
pxTaskWoken :pdTRUEへのポインタとすると、キューが空で
待っていたタスクのブロックを解除する。
pdFALSEへのポインタとすると何もしないxQueueAddToRegistry デバッグ用にキューに名前を付け、レジストリにキューを追加する
《書式》
void vQueueAddToRegistry(xQueueHandle xQueue,
signed portCHAR *pcQueueName);
xQueue :キューのハンドル値
pcQueueName :キューに付ける名称へのポインタ
《使用例》
xQueue = xQueueCreate(10, sizeof(portCHAR));
vQueueAddToRegistry(xQueue, "MeaningfulName");vQueueUnregisterQueue デバッグ用に追加したキューのレジストリを削除する
《書式》
void vQueueUnregisterQueue(xQueueHandle xQueue);
xQueue :キューのハンドル値
《使用例》
vQueueUnregisterQueue(xQueue);
vQueueDelete(xQueue);
【キュー制御用補助関数】
マクロ名 機能と書式 uxQueueMessagesWaiting キューに格納されているアイテム数を返す
《書式》
unsigned portBASE_TYPE uxQueueMessagesWaiting(
xQueueHandle xQueue);
xQueue:キューのハンドル値
戻り値:キュー内のアイテム数xQueueDelete キューを削除する。キューの使用しているメモリを開放する
《書式》
void vQueueDelete(xQueueHandle xQueue);
xQueue:キューのハンドル値
【キューバッファの使用例】
例題として最初の3つのタスクで液晶表示器に表示を出す例題を試してみます。
まず、メイン関数部で、キューバッファと、タスクの生成をします。
例題は下記リストのように、宣言部でキューのハンドル用変数の宣言定義とタスク関数
のプロトタイプをしてから、main関数の中でキューバッファの生成(xQueueCreate)と
タスクの生成(xTaskCreate)を実行し、スケジュールを開始しています。
キューの生成を行うとキューの名前がハンドル変数(lcdQueue)に返されますので、
以降はこのキューハンドルを使ってキューを指定します。
キューの大きさは液晶表示器の1行分を1回に送るデータとすることとしますので、17バイト
を単位とし、それを5個まで蓄えられるサイズとしています。
次にタスク関数部は下記リストのようにして、キューを使います。
まずタスク1とタスク2ではキューにデータを送り(xQueueSendToBack)
タスク3ではキューからデータを取り出して(xQueueReceive)います。
送る側では、16文字の液晶表示器への表示文字を用意し、それを一定時間
間隔で送っています。表示データの中味にカウンタデータを含め、毎回データ
が異なるようにしています。
キューが一杯で送れない場合にはバッファが空くのを永久に待つものとしています。
キューを取り出す方では、一時バッファとしてBufを用意し、これに毎回取り出しては
表示出力します。キューが空の場合には、キューにデータが送られるまで永久に待つ
ものとしています。
さらに取り出したデータの内容をチェックして、タスク1からのデータの場合には1行目
に表示し、タスク2からの場合には2行目に表示するようにしています。
このプログラムの実行結果では、液晶表示器に下図のように表示されます。
数値が高速で更新されていき、2行目の数値は1行目の半分になっています。
★★★ この例題のプロジェクトファイル一式 (sample3)
目次に戻る