古くなっている可能性があるので、最新情報はM5StickC非公式日本語リファレンスで確認してください。
キューとは
処理が必要なデータの集まりで、主に別タスクにデータ処理を依頼する場合に利用します。通常はキューの最後に追加するFIFOですが、先頭に割り込んで追加することもできます。
キューの種類
- 通常のキュー
- メールボックス(最後のアイテムのみ保持)
1つのメッセージしか保存しないメールボックスと呼ばれるキューがあるようです。最後の情報を表示するなどの場合はこれを使うのかな?
通常は複数のメッセージが保存できるキューを使います。FreeRTOSのキューはメッセージを送信すると、送信したデータをコピーしてキューに保存しますので送信元でそのデータを書き換えたりしても安全に利用することができます。
サンプルスケッチ
// キューの大きさ
#define QUEUE_LENGTH 4
// 通常のキュー
QueueHandle_t xQueue;
// Mailbox用のキュー
QueueHandle_t xQueueMailbox;
void setup() {
// キューアイテム
uint8_t data = 1;
// デバッグ出力準備
Serial.begin(115200);
Serial.println("Queue Test");
// キュー作成
xQueue = xQueueCreate( QUEUE_LENGTH, sizeof( uint8_t ) );
// メールボックス用途は長さ1のみ
xQueueMailbox = xQueueCreate( 1, sizeof( uint8_t ) );
// 最後に追加(1)
xQueueSend(xQueue, &data, 0);
data++;
// 先頭に追加(2)
xQueueSendToFront(xQueue, &data, 0);
data++;
// 最後に追加(3)
xQueueSendToBack(xQueue, &data, 0);
data++;
// 上書き送信(4)
xQueueOverwrite(xQueueMailbox, &data);
data++;
// 上書き送信(5)
xQueueOverwrite(xQueueMailbox, &data);
data++;
// キューの数確認
Serial.printf( "uxQueueMessagesWaiting = %d\n", uxQueueMessagesWaiting(xQueue) );
// キューの追加可能数確認
Serial.printf( "uxQueueSpacesAvailable = %d\n", uxQueueSpacesAvailable(xQueue) );
// キューの状態取得
Serial.printf( "xQueueIsQueueEmptyFromISR = %d\n", xQueueIsQueueEmptyFromISR(xQueue) );
Serial.printf( "xQueueIsQueueFullFromISR = %d\n", xQueueIsQueueFullFromISR(xQueue) );
Serial.printf( "uxQueueMessagesWaitingFromISR = %d\n", uxQueueMessagesWaitingFromISR(xQueue) );
// Peek受信(何度受信しても同じ値)
Serial.println( "Peek Test" );
xQueuePeek( xQueue, &data, 0 );
Serial.println( data );
xQueuePeek( xQueue, &data, 0 );
Serial.println( data );
// 通常受信(213の順で受信し、受信成功した場合のみアイテムが更新される)
int ret;
Serial.println( "Receive Test" );
ret = xQueueReceive( xQueue, &data, 0 );
Serial.printf( "ret = %d, data = %d\n", ret, data );
ret = xQueueReceive( xQueue, &data, 0 );
Serial.printf( "ret = %d, data = %d\n", ret, data );
ret = xQueueReceive( xQueue, &data, 0 );
Serial.printf( "ret = %d, data = %d\n", ret, data );
ret = xQueueReceive( xQueue, &data, 0 );
Serial.printf( "ret = %d, data = %d\n", ret, data );
ret = xQueueReceive( xQueue, &data, 0 );
Serial.printf( "ret = %d, data = %d\n", ret, data );
// Mailbox受信(最後に送信したデータのみ受信)
Serial.println( "Mailbox Test" );
xQueuePeek( xQueueMailbox, &data, 0 );
Serial.println( data );
// キュークリア
Serial.println( "xQueueReset Test" );
xQueueReset(xQueue);
xQueueReset(xQueueMailbox);
// キューセット
Serial.println( "QueueSet Test" );
QueueSetHandle_t xQueueSet;
xQueueSet = xQueueCreateSet( QUEUE_LENGTH + 1 ); // Setするキューの合計
xQueueAddToSet( xQueue, xQueueSet );
xQueueAddToSet( xQueueMailbox, xQueueSet );
// アイテム追加
data = 100;
xQueueSend(xQueue, &data, 0);
data++;
xQueueSend(xQueue, &data, 0);
data++;
xQueueSend(xQueueMailbox, &data, 0);
// 取得してみる
while(1){
// 取得可能なキューを取得する
QueueHandle_t queue = xQueueSelectFromSet( xQueueSet, 0 );
if( queue == NULL ){
// キューがなくなったので終了
break;
}
// キューの種類を調べる
if( queue == xQueue ){
Serial.print( "xQueue " );
} else if( queue == xQueueMailbox ) {
Serial.print( "xQueueMailbox " );
} else {
Serial.print( "? " );
}
// 受信
ret = xQueueReceive( queue, &data, 0 );
Serial.printf( "ret = %d, data = %d\n", ret, data );
}
// キュー削除
vQueueDelete(xQueue);
vQueueDelete(xQueueMailbox);
}
void loop() {
}
実行結果
Queue Test uxQueueMessagesWaiting = 3 uxQueueSpacesAvailable = 1 xQueueIsQueueEmptyFromISR = 0 xQueueIsQueueFullFromISR = 0 uxQueueMessagesWaitingFromISR = 3 Peek Test 2 2 Receive Test ret = 1, data = 2 ret = 1, data = 1 ret = 1, data = 3 ret = 0, data = 3 ret = 0, data = 3 Mailbox Test 5 xQueueReset Test QueueSet Test xQueue ret = 1, data = 100 xQueue ret = 1, data = 101 xQueueMailbox ret = 1, data = 102
解説
いろいろごちゃごちゃ処理を書いてありますが、xQueueCreate()でキューを作成して、xQueueSend()で送信して、xQueueReceive()で受信できます。キューの先頭に割り込ませたい場合にはxQueueSendToFront()で送信します。
割り込みの内部から呼び出した場合にはFromISRが最後についている関数群があるのでそちらを利用します。
QueueSetは複数のタスクからキューにアクセスするときの機能なので、Arduinoでのユースケースは思いつかないです。
まとめ
おそらくは受信は通知で、送信はキューを使ってメッセージ処理をするのが王道のはずです。
あと標準のFreeRTOSと、ESP32で使えるようにしたESP-IDF用FreeRTOSと、それをArduinoで使えるようにした Arduino用FreeRTOSがあり、3つとも使える機能がちょっと違うのでわかりにくいです。
ドキュメントはESP-IDF用FreeRTOSが一番無難なんですが、Arduino版で使えない関数などがあるので、Arduino IDEで実際にコンパイルしてリファレンスは書いています。概要は日本語で公開されているFreeRTOSのオフィシャルが一番充実していますがArduinoでは使えない関数とか、ESP-IDFで拡張した機能とかがわからないです。



コメント