古くなっている可能性があるので、最新情報は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で拡張した機能とかがわからないです。
コメント