esp-open-rtosを使ってみる

Queue Set機能

Queueを使うためには、xQueueCreate()、またはxQueueCreateStatic()でQueue領域を確保しますが、
この時「キューの数(QueueLength)」と「1つのキューのサイズ(ItemSize)」を指定しします。
QueueHandle_t xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );

struct AMessage
{
    char ucMessageID;
    char ucData[ 20 ];
};
QueueHandle_t xQueue2 = xQueueCreate( 20, sizeof( struct AMessage * ) );

上の例では、xQueue1はunsigned longの領域が10個確保され、xQueue2はAMessageの構造体の領域が20個確保されます。
xQueue1にはunsigned longの変数、xQueue2にはAMessage構造体の変数しか格納できません。
1つのタスクでxQueue1とxQueue2の両方からデータを待ち受けする場合、以下の手順となります。
QueueHandle_t xQueue1;
QueueHandle_t xQueue2;
while (1) {
  xQueue1 = xQueueReceive(Queue1,*pvBuffer1,0);
  xQueue2 = xQueueReceive(Queue2,*pvBuffer2,0);
  if( xQueue1 != 0 ) {
    xQueue1のデータを処理
  }
  if( xQueue2 != 0 ) {
    xQueue2のデータを処理
  }
  vTaskDelay(1000);
}

2つ以上のQueueからデータを待つ場合、タイムアウト無しでxQueueReceive()を呼び出すことになり、
vTaskDelay()で待ち時間を設けないと永久ループになってしまいます。
マルチタスクのシステムで優先度の高いタスクが永久ループすると、他のタスクは全く動けません。

FreeRTOSにはQueue Setの機能が有り、QueueとSemaphoreの組み合わせでデータを待つことができます。
Queue Set機能の確認のために簡単なサンプルを作ってみました。
Queue Set機能を利用するためには、ソースと同じ場所にある「FreeRTOSConfig.h」に以下を追加する必要が有ります。
#define configUSE_QUEUE_SETS     1

以下のコードでは4つのタスクを起動しています。

task1
xQueue1 xQueue2 xSemaphoreのいずれかを待つタスクです。

task2
xQueue1にデータを積むタスクです。

task3
xQueue2にデータを積むタスクです。

task4
xSemaphoreをリリースするタスクです。
/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 *
 * You have to add this in FreeRTOSConfig.h.
 * #define configUSE_QUEUE_SETS     1
 */
#include <stdlib.h>
#include <string.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "esp8266.h"

QueueSetHandle_t xQueueSet;
QueueHandle_t xQueue1, xQueue2;
SemaphoreHandle_t xSemaphore;
QueueSetMemberHandle_t xActivatedMember;
uint32_t xReceivedFromQueue1;
char     xReceivedFromQueue2[20];

/* Define the lengths of the queues that will be added to the queue set. */
#define QUEUE_LENGTH_1 10
#define QUEUE_LENGTH_2 10

/* Define the size of the item to be held by queue 1 and queue 2 respectively.
The values used here are just for demonstration purposes. */
#define ITEM_SIZE_QUEUE_1 sizeof( xReceivedFromQueue1 )
#define ITEM_SIZE_QUEUE_2 sizeof( xReceivedFromQueue2 )

/* Binary semaphores have an effective length of 1. */
#define BINARY_SEMAPHORE_LENGTH 1

/* The combined length of the two queues and binary semaphore that will be
added to the queue set. */
#define COMBINED_LENGTH ( QUEUE_LENGTH_1 + \
                          QUEUE_LENGTH_2 + \
                          BINARY_SEMAPHORE_LENGTH )


void task1(void *pvParameters)
{
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
    while(1) {
      /* Block to wait for something to be available from the queues or
      semaphore that have been added to the set.  Don't block longer than
      5000ms. */
      nowTick = xTaskGetTickCount();
      printf("[%s:%d] Waitting.....\n",pcTaskGetName(0),nowTick);
      xActivatedMember = xQueueSelectFromSet( xQueueSet,
                                              5000 / portTICK_PERIOD_MS );

      /* Which set member was selected?  Receives/takes can use a block time
      of zero as they are guaranteed to pass because xQueueSelectFromSet()
      would not have returned the handle unless something was available. */
      if( xActivatedMember == xQueue1 ) {
          xQueueReceive( xActivatedMember, &xReceivedFromQueue1, 0 );
          nowTick = xTaskGetTickCount();
          printf("[%s:%d] xReceivedFromQueue1=%ul\n",pcTaskGetName(0),nowTick,xReceivedFromQueue1);
      } else if( xActivatedMember == xQueue2 ) {
          xQueueReceive( xActivatedMember, &xReceivedFromQueue2, 0 );
          nowTick = xTaskGetTickCount();
          printf("[%s:%d] xReceivedFromQueue2=[%s]\n",pcTaskGetName(0),nowTick,xReceivedFromQueue2);
      } else if( xActivatedMember == xSemaphore ) {
          /* Take the semaphore to make sure it can be "given" again. */
          xSemaphoreTake( xActivatedMember, 0 );
          nowTick = xTaskGetTickCount();
          printf("[%s:%d] xSemaphoreTake\n",pcTaskGetName(0),nowTick);
          //break;
      } else {
          /* The 2000ms block time expired without an RTOS queue or semaphore
          being ready to process. */
          nowTick = xTaskGetTickCount();
          printf("[%s] Nothing.....%d\n",pcTaskGetName(0),nowTick);
      }

    }
}

// Send Queue
// QueueHandle_t xQueue1
void task2(void *pvParameters)
{
    uint32_t count = 0;
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
    while(1) {
        vTaskDelay(1000/portTICK_PERIOD_MS);
        xQueueSend(xQueue1, &count, 0);
        count++;
    }
}

// Send Queue
// QueueHandle_t xQueue2
void task3(void *pvParameters)
{
    char msg[ITEM_SIZE_QUEUE_2];
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
    while(1) {
        vTaskDelay(2000/portTICK_PERIOD_MS);
        nowTick = xTaskGetTickCount();
        snprintf(msg, ITEM_SIZE_QUEUE_2, "nowTick=%d", nowTick);
        xQueueSend(xQueue2, (void *)msg, 0);
    }
}

// Give Semaphore
// SemaphoreHandle_t xSemaphore
void task4(void *pvParameters)
{
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
    while(1) {
        vTaskDelay(5000/portTICK_PERIOD_MS);
        xSemaphoreGive( xSemaphore );
    }
}



void user_init(void)
{
    uart_set_baud(0, 115200);
    printf("SDK version:%s\n", sdk_system_get_sdk_version());
    printf("pcTaskGetName=%s\n",pcTaskGetName(0));

    /* Create the queue set large enough to hold an event for every space in
    every queue and semaphore that is to be added to the set. */
    xQueueSet = xQueueCreateSet( COMBINED_LENGTH );

    /* Create the queues and semaphores that will be contained in the set. */
    printf("ITEM_SIZE_QUEUE_1=%d ITEM_SIZE_QUEUE_2=%d\n",ITEM_SIZE_QUEUE_1,ITEM_SIZE_QUEUE_2);
    xQueue1 = xQueueCreate( QUEUE_LENGTH_1, ITEM_SIZE_QUEUE_1 );
    xQueue2 = xQueueCreate( QUEUE_LENGTH_2, ITEM_SIZE_QUEUE_2 );

    /* Create the semaphore that is being added to the set. */
    xSemaphore = xSemaphoreCreateBinary();

    /* Check everything was created. */
    configASSERT( xQueueSet );
    configASSERT( xQueue1 );
    configASSERT( xQueue2 );
    configASSERT( xSemaphore );

    /* Add the queues and semaphores to the set.  Reading from these queues and
    semaphore can only be performed after a call to xQueueSelectFromSet() has
    returned the queue or semaphore handle from this point on. */
    xQueueAddToSet( xQueue1, xQueueSet );
    xQueueAddToSet( xQueue2, xQueueSet );
    xQueueAddToSet( xSemaphore, xQueueSet );

    xTaskCreate(task1, "task1", 256, NULL, 2, NULL);
    xTaskCreate(task2, "task2", 256, NULL, 2, NULL);
    xTaskCreate(task3, "task3", 256, NULL, 2, NULL);
    xTaskCreate(task4, "task4", 256, NULL, 2, NULL);
}

実行すると以下の表示となります。
100Tick1毎にxQueue1からデータ(uint32_t)を取り出します。
200Tick毎にxQueue2からデータ(char[20])を取り出します。
500Tick毎にSemaphoreを得ます。
[task1:3838] xReceivedFromQueue1=37l
[task1:3838] Waitting.....
[task1:3938] xReceivedFromQueue1=38l
[task1:3938] Waitting.....
[task1:4002] xSemaphoreTake
[task1:4002] Waitting.....
[task1:4002] xReceivedFromQueue2=[nowTick=4002]
[task1:4002] Waitting.....
[task1:4038] xReceivedFromQueue1=39l
[task1:4038] Waitting.....
[task1:4138] xReceivedFromQueue1=40l
[task1:4138] Waitting.....
[task1:4202] xReceivedFromQueue2=[nowTick=4202]
[task1:4202] Waitting.....
[task1:4238] xReceivedFromQueue1=41l
[task1:4238] Waitting.....
[task1:4338] xReceivedFromQueue1=42l
[task1:4338] Waitting.....
[task1:4402] xReceivedFromQueue2=[nowTick=4402]
[task1:4402] Waitting.....
[task1:4438] xReceivedFromQueue1=43l
[task1:4438] Waitting.....
[task1:4502] xSemaphoreTake

続く....