esp-open-rtosSを使ってみる

MessageBuffer機能

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構造体の変数しか格納できません。

Queueとよく似た機能にMessageBufferの機能が有ります。
MessageBuffer機能を使うためには、xMessageBufferCreate()、または xMessageBufferCreateStatic()で領域を確保しますが、
指定するのは領域全体のバイト数です。
この領域をどのように使うかは自由で、文字列でもバイナリの配列でも、好きなサイズのデータを書き込むことができます。

例えば、書き込む文字列の長さが不定の場合、Queueを使うときは、ItemSizeには想定できる最大文字数を指定してQueue領域を確保 し、
1バイトのデータをxQueueSend()した時もQueue領域(キューの数)を1つ消費します。
一方、MessageBufferでは、格納した分だけのMessage領域を消費します。

MessageBuffer機能の確認のために簡単なサンプルを作ってみました。
最初に100バイトのMessageBuffer領域を確保します。
task1は1秒ごとに「nowTick=xxxx」の文字列をMessageBufferに格納します。
メッセージの長さ(xxxxの部分)は現在のtick数に応じて変わっていきます。
task2はMessageBufferから2秒ごとにデータを読み出します。
/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <stdlib.h>
#include <string.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "message_buffer.h"
#include "task.h"
#include "esp8266.h"

void task1(void *pvParameters)
{
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
    MessageBufferHandle_t *buffer = (MessageBufferHandle_t *)pvParameters;
    size_t xBytesSent;
    char pcStringToSend[30];

    while(1) {
      // delay 1000mSec
      vTaskDelay(1000 / portTICK_PERIOD_MS);
      nowTick = xTaskGetTickCount();
      sprintf(pcStringToSend,"nowTick=%d",nowTick);
      xBytesSent = xMessageBufferSend( *buffer,
                                    ( void * ) pcStringToSend,
                                    strlen( pcStringToSend ),
                                    0 );
      printf("[%s:%d] xBytesSent=%d\n",pcTaskGetName(0),nowTick,xBytesSent);
    }
}

void task2(void *pvParameters)
{
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
    MessageBufferHandle_t *buffer = (MessageBufferHandle_t *)pvParameters;
    size_t xBytesReceived;
    char ucRxData[ 100 ];
    //const TickType_t xBlockTime = pdMS_TO_TICKS( 2000 );

    while(1) {
      vTaskDelay(2000 / portTICK_PERIOD_MS);
      while(1) {
        xBytesReceived = xMessageBufferReceive( *buffer,
                                            ( void * ) ucRxData,
                                            sizeof( ucRxData ),
                                            0 );
        nowTick = xTaskGetTickCount();
        printf("[%s:%d] xBytesReceived=%d\n",pcTaskGetName(0),nowTick,xBytesReceived);
        if (xBytesReceived != 0) {
          ucRxData[xBytesReceived] = 0;
          printf("[%s:%d] ucRxData=[%s]\n",pcTaskGetName(0),nowTick,ucRxData);
        } else {
          break;
        }
      }
    }
}

static MessageBufferHandle_t xMessageBuffer;

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));

    //MessageBufferHandle_t xMessageBuffer;
    const size_t xMessageBufferSizeBytes = 100;

    /* Create a message buffer that can hold 100 bytes.  The memory used to hold
    both the message buffer structure and the data in the message buffer is
    allocated dynamically. */
    xMessageBuffer = xMessageBufferCreate( xMessageBufferSizeBytes );
    if( xMessageBuffer == NULL ) {
       /* There was not enough heap memory space available to create the
       message buffer. */
       printf("%s: Unable to create MessageBuffer ...\n", __FUNCTION__);
    }
    else
    {
       /* The message buffer was created successfully and can now be used. */
       printf("%s: Success to create MessageBuffer ...\n", __FUNCTION__);
       xTaskCreate(task1, "task1", 256, &xMessageBuffer, 2, NULL);
       xTaskCreate(task2, "task2", 256, &xMessageBuffer, 2, NULL);
    }
}

実行すると以下の表示となります。
最初はtick数が小さいので、task1は全体の長さが11文字のデータを書き込みます。
task1は1秒毎、task2は2秒毎の実行なので、task2は必ず2つのデータを読み込みます。
SDK version:0.9.9
pcTaskGetName=uiT
[task1:1] Start
[task2:1] Start
[task1:138] xBytesSent=11
[task2:201] xBytesReceived=11
[task2:201] ucRxData=[nowTick=138]
[task2:201] xBytesReceived=0
[task1:238] xBytesSent=11
[task1:338] xBytesSent=11
[task2:401] xBytesReceived=11
[task2:401] ucRxData=[nowTick=238]
[task2:401] xBytesReceived=11
[task2:401] ucRxData=[nowTick=338]
[task2:401] xBytesReceived=0
[task1:438] xBytesSent=11
[task1:538] xBytesSent=11
[task2:601] xBytesReceived=11
[task2:601] ucRxData=[nowTick=438]
[task2:601] xBytesReceived=11
[task2:601] ucRxData=[nowTick=538]
[task2:601] xBytesReceived=0

ある程度時間が経過すると、tick数が大きくなるので、task1は全体の長さが14文字のデータを書き込み、
task2も14文字のデータを読み込みます。
[task1:101038] xBytesSent=14
[task1:101138] xBytesSent=14
[task2:101201] xBytesReceived=14
[task2:101201] ucRxData=[nowTick=101038]
[task2:101201] xBytesReceived=14
[task2:101201] ucRxData=[nowTick=101138]
[task2:101201] xBytesReceived=0
[task1:101238] xBytesSent=14
[task1:101338] xBytesSent=14
[task2:101401] xBytesReceived=14
[task2:101401] ucRxData=[nowTick=101238]
[task2:101401] xBytesReceived=14
[task2:101401] ucRxData=[nowTick=101338]
[task2:101401] xBytesReceived=0
[task1:101438] xBytesSent=14
[task1:101538] xBytesSent=14
[task2:101601] xBytesReceived=14
[task2:101601] ucRxData=[nowTick=101438]
[task2:101601] xBytesReceived=14
[task2:101601] ucRxData=[nowTick=101538]
[task2:101601] xBytesReceived=0

このように文字列の長さが不定のメッセージをやり取るするときには、QueueよりもMessageBufferの方が
効率よく領域を使うことができる場合が有ります。
但し10文字のデータを書き込むと、4バイトの長さ情報も書き込まれるので、実際には14バイトの領域を消費します。



MessageBufferとよく似た機能に、StreamBufferの機能が有ります。
StreamBuffer機能を使うためには、xStreamBufferCreate()、または xStreamBufferCreateStatic()で領域を確保しますが、
指定するのは領域全体のバイト数とトリガーバイト数です。
Send側はMessageBufferと同じですが、Receive側はトリガーバイト数以上のデータがないと、Receiveがブロックされ ます。
名前の通りStreamData(区切りのないデータ)のための機能です。

続く....