esp-open-rtosを使ってみる

Semaphore機能

マルチタスクのシステムを設計する際に、重要な要素としてタスク間、またはタスクと割り込みの間での同期が有ります。
FreeRTOSでも、同期を行うためのSemaphore機能が用意されて います。
Semaphore機能の動作を確認するために簡単なサンプルを作ってみました。
/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <stdlib.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "esp8266.h"

SemaphoreHandle_t xSemaphore;

//時間稼ぎ
void ConsumptionTick(int delay) {
    TickType_t startTick;
    TickType_t endTick;
    TickType_t nowTick;
    startTick = xTaskGetTickCount();
    endTick = startTick + delay;
    //printf("startTick=%d endTick=%d\n",startTick,endTick);
    while(1) {
      nowTick = xTaskGetTickCount();
      if (nowTick > endTick) break;
    }
}

void task1(void *pvParameters)
{
  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
  while(1) {
    ConsumptionTick(200);
    xSemaphoreGive(xSemaphore);
    vTaskDelay(1);
    xSemaphoreTake(xSemaphore, portMAX_DELAY);
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Take Semaphore\n",pcTaskGetName(0),nowTick);
  }
}

void task2(void *pvParameters)
{
  TickType_t nowTick;
  nowTick = xTaskGetTickCount();
  printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
  while(1) {
    xSemaphoreTake(xSemaphore, portMAX_DELAY);
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Take Semaphore\n",pcTaskGetName(0),nowTick);
    ConsumptionTick(200);
    xSemaphoreGive(xSemaphore);
    vTaskDelay(1);
  }
}

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 Semaphore */
  xSemaphore = xSemaphoreCreateBinary();
  /* Check everything was created. */
  configASSERT( xSemaphore );

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

このサンプルを実行すると、以下の表示となります。
task2はTakeでブロックされますが、task1がGiveするとtask2がTakeして動き始めます。
task2がGiveするとtask1がTakeして動き始めます。
portMAX_DELAYは-1の定数で、SemaphoreをTakeすることができるまで永久に待つ定数です。
SDK version:0.9.9
pcTaskGetName=uiT
mode : sta(18:fe:34:d4:2f:77)
add if0
[task1:1] Start
[task2:1] Start
[task2:202] Take Semaphore
[task1:403] Take Semaphore
[task2:604] Take Semaphore
[task1:805] Take Semaphore
[task2:1006] Take Semaphore
[task1:1207] Take Semaphore
[task2:1408] Take Semaphore
[task1:1609] Take Semaphore



Semaphoreを使うためには、vSemaphoreCreateBinary()、または xSemaphoreCreateBinary() で初期化しますが、
初期化後のSemaphore値が違います。
以下のコードで違いを確認することができます。
/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <stdlib.h>
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "esp8266.h"


void user_init(void)
{
  SemaphoreHandle_t xSemaphore1;
  SemaphoreHandle_t xSemaphore2;

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

  vSemaphoreCreateBinary( xSemaphore1 );
  configASSERT( xSemaphore1 );
  UBaseType_t value;
  value = uxSemaphoreGetCount( xSemaphore1 );
  printf("vSemaphoreCreateBinary=%d\n",(int)value);

  xSemaphore2 = xSemaphoreCreateBinary();
  configASSERT( xSemaphore2 );
  value = uxSemaphoreGetCount( xSemaphore2 );
  printf("xSemaphoreCreateBinary=%d\n",(int)value);
}

vSemaphoreCreateBinary()で初期化した時の初期値は1なので、いきなりxSemaphoreTake()してもブロック されませんが、
xSemaphoreCreateBinary()で初期化した時の初期値は0なので、いきなりxSemaphoreTake()するとブロック さ れます。
この違いは大きく、全く互換性が有りません。
vSemaphoreCreateBinary()は後方互換性を確保するために残されているマクロで、使用は推奨されていません。
SDK version:0.9.9
pcTaskGetName=uiT
vSemaphoreCreateBinary=1
xSemaphoreCreateBinary=0



特定のタイミングで一斉に複数のタスクの同期を取る場合、1つのSemaphoreだけで、複数のタスクの同期を取ることができます。
各タスクの先頭でSemaphoreのTakeとGiveを行うことで、Semaphore待ちとなっている他のタスクが動き始めます。
/* The example of esp-free-rtos
 *
 * This sample code is in the public domain.
 */
#include <string.h>
#include <espressif/esp_common.h>
#include <esp/uart.h>
#include <FreeRTOS.h>
#include <task.h>
#include "ssid_config.h"

#include <lwip/api.h>

SemaphoreHandle_t xSemaphore;

void Task(void *pvParameters)
{
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Start\n",pcTaskGetName(0),nowTick);
    xSemaphoreTake(xSemaphore, portMAX_DELAY);
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Take\n",pcTaskGetName(0),nowTick);
    xSemaphoreGive(xSemaphore);
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] Give\n",pcTaskGetName(0),nowTick);

    while(1) {
      vTaskDelay(1);
    }
}

void WifiTask(void *pvParameters)
{
    TickType_t nowTick;
    nowTick = xTaskGetTickCount();
    printf("[%s:%d] start\n",pcTaskGetName(0),nowTick);

    struct ip_info info;
    while(1) {
      if (sdk_wifi_get_ip_info(STATION_IF, &info)) {
        /*
        #define IP2STR(ipaddr) ip4_addr1_16(ipaddr), \
        ip4_addr2_16(ipaddr), \
        ip4_addr3_16(ipaddr), \
        ip4_addr4_16(ipaddr)
        ip4_addr2_16(ipaddr), \
        ip4_addr3_16(ipaddr), \
        ip4_addr4_16(ipaddr)
        #define IPSTR "%d.%d.%d.%d"
        */
        nowTick = xTaskGetTickCount();
        printf("[%s:%d] IP address="IPSTR"\n",pcTaskGetName(0),nowTick,IP2STR(&info.ip));
        if (ip4_addr1_16(&info.ip) != 0) break;
      }
      vTaskDelay(100);
    }
    printf("[%s:%d] Netmask="IPSTR"\n",pcTaskGetName(0),nowTick,IP2STR(&info.netmask));
    printf("[%s:%d] Gateway="IPSTR"\n",pcTaskGetName(0),nowTick,IP2STR(&info.gw));

    // Start other task
    xSemaphoreGive(xSemaphore);

    vTaskDelete(NULL);
}

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 Semaphore */
    xSemaphore = xSemaphoreCreateBinary();
    /* Check everything was created. */
    configASSERT( xSemaphore );

    struct sdk_station_config config = {
        .ssid = WIFI_SSID,
        .password = WIFI_PASS,
    };

    // Required to call wifi_set_opmode before station_set_config.
    sdk_wifi_set_opmode(STATION_MODE);
    sdk_wifi_station_set_config(&config);

    xTaskCreate(WifiTask, "WifiTask", 512, NULL, 2, NULL);

    xTaskCreate(Task, "Task1", 256, NULL, 2, NULL);
    xTaskCreate(Task, "Task2", 256, NULL, 2, NULL);
    xTaskCreate(Task, "Task3", 256, NULL, 2, NULL);
    xTaskCreate(Task, "Task4", 256, NULL, 2, NULL);
}

1Tick目で各タスクが起動しますが、Semaphore待ちとなります。
438Tick目でWifi接続が終わると、4つのタスクが同期して動き始めます。
SDK version:0.9.9
pcTaskGetName=uiT
mode : sta(84:0d:8e:8c:f4:00)
add if0
[WifiTask:1] start
[WifiTask:1] IP address=0.0.0.0
[Task1:1] Start
[Task2:1] Start
[Task3:1] Start
[Task4:1] Start
scandone
[WifiTask:138] IP address=0.0.0.0
add 0
aid 8
cnt

connected with aterm-e625c0-g, channel 10
dhcp client start...
[WifiTask:238] IP address=0.0.0.0
[WifiTask:338] IP address=0.0.0.0
ip:192.168.10.119,mask:255.255.255.0,gw:192.168.10.1
[WifiTask:438] IP address=192.168.10.119
[WifiTask:438] Netmask=255.255.255.0
[WifiTask:438] Gateway=192.168.10.1
[Task1:438] Take
[Task1:438] Give
[Task2:438] Take
[Task2:438] Give
[Task3:438] Take
[Task3:438] Give
[Task4:438] Take
[Task4:439] Give

続く....