23
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

ESP32を使ったFreeRTOSのサンプルプログラム

はじめに

CQ出版 Interface誌 2018年10月号へ執筆したESP実験コーナ 「FreeRTOS」をはじめるで紙面の都合上削除されたFreeRTOSのサンプルプログラムを紹介します。
ESP32FreeRTOSを使ったプログラム作成の参考になればと思います。

紹介するサンプルプログラムは以下の5本です。
 (1) タスク制御
 (2) タイマ
 (3) メッセージ・キュー
 (4) セマフォ
 (5) イベント・グループ

サンプルプログラムの動作環境はESP32-DevKitArduino IDEを使っています。

◆Interface誌 2018年10月号紹介ページ
 https://interface.cqpub.co.jp/magazine/201810/
 MIF201810l-280x396.jpg
◆記事紹介ページ
 https://interface.cqpub.co.jp/wp-content/uploads/if10_156.pdf

◆ESP32-DevKit
 https://www.espressif.com/en/products/hardware/esp32-devkitc/overview
 http://akizukidenshi.com/catalog/g/gM-11819/

(1)タスク制御

2つのタスクを生成、LEDを使ってそれぞれのタスクが異なる周期で動作するのを確認するようにしてあります。
生成される2つのタスクはsetup()loop()とは別のタスクです。
LEDはIO22とIO23に接続しています。
 ESP32_LED1.jpg Fig-2.jpg

os1_task.ino
/*
 *  Copyright(C) 2018 by Yukiya Ishioka
 */

#include <freertos/FreeRTOS.h>    /* FreeRTOSを用いるためのヘッダファイル */

#define  LED1PIN   22    /* LED1のポート番号 */
#define  LED2PIN   23    /* LED2のポート番号 */

void  task1( void *param )
{
  while( 1 ) {
    vTaskDelay(500);
    digitalWrite(LED1PIN, HIGH);  /* LED on */
    vTaskDelay(500);
    digitalWrite(LED1PIN, LOW);   /* LED off */
  }
}

void  task2( void *param )
{
  while( 1 ) {
    vTaskDelay(200);
    digitalWrite(LED2PIN, HIGH);  /* LED on */
    vTaskDelay(200);
    digitalWrite(LED2PIN, LOW);   /* LED off */
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.print("setup()\n");

  pinMode(LED1PIN, OUTPUT);
  pinMode(LED2PIN, OUTPUT);
  digitalWrite(LED1PIN, LOW);  /* LED1 off */
  digitalWrite(LED2PIN, LOW);  /* LED2 off */

  /* create task */
  xTaskCreatePinnedToCore( task1,   /* タスクの入口となる関数名 */
                           "TASK1", /* タスクの名称 */
                           0x800,   /* スタックサイズ */
                           NULL,    /* パラメータのポインタ */
                           1,       /* プライオリティ */
                           NULL,    /* ハンドル構造体のポインタ */
                           0 );     /* 割り当てるコア (0/1) */

  xTaskCreatePinnedToCore( task2,
                           "TASK2",
                           0x800,
                           NULL,
                           2,
                           NULL,
                           0 );
}

void loop()
{
  Serial.print("loop()\n");
  vTaskDelay(1000);
}

(2)タイマ

タイマは、指定した周期でタイマ生成時に登録したタイマハンドラを呼び出すことができます。
setup()内で2000ミリ秒(2秒)周期でタイマハンドラtimer_handler()を繰り返し呼び出すタイマを生成します。
タイマハンドラ内でシリアルへの出力処理を入れ、2秒ごとに「timer_handler()」がシリアルモニタ表示されタイマが動作していることが分かります。また、loop()が1秒ごとに呼び出され、そこではシリアルモニタへ「loop()」が出力されます。
タイマがloop()とは別に動作することが分かると思います。

os2_timer.ino
/*
 *  Copyright(C) 2018 by Yukiya Ishioka
 */

#include <freertos/FreeRTOS.h>    /* FreeRTOSを用いるためのヘッダファイル */
#include <freertos/timers.h>      /* タイマを用いるためのヘッダファイル */

TimerHandle_t thand_test;


void timer_handler( void *param )
{
  Serial.print("timer_handler()\n");
}


void setup()
{
  Serial.begin(115200);
  Serial.print("setup()\n");

  /* create timer */
  thand_test = xTimerCreate( "TIM_TEST",  /* タイマの名称 */
                             2000,        /* 遅延時間 */
                             pdTRUE,      /* 自動繰り返しの有無 */
                             NULL,        /* ID変数のポインタ */
                             timer_handler ); /* タイマハンドラの関数名 */

  xTimerStart( thand_test, 0 ); /* タイマの開始 */
  vTaskDelay(500);
}


void loop()
{
  Serial.print("loop()\n");
  vTaskDelay(1000);
}

(3)メッセージ・キュー

メッセージ・キューはタスクやハンドラが送信したメッセージ文字列を他のタスクで受信することができます。
受信側のタスクではメッセージを受信するまでタスクをスリープして待ち、メッセージが来たら起床してタスクの動作を再開させることができます。
サンプルではsetup()内でタスクを1つ生成し、生成されたタスクはメッセージ受信待ちでスリープします。
その後、loop()で5秒ごとにメッセージ文字列を送信します。
受信側のタスクはメッセージを受信するとそのメッセージをシリアルへ出力し、再びメッセージ待ちでスリープします。
5秒ごとに「message 1」「message 2」「message 3」「message 4」「message 5」の各メッセージが順番にシリアルモニタへ出力され、メッセージ・キューの動作が分かります。

os3_msg.ino
/*
 *  Copyright(C) 2018 by Yukiya Ishioka
 */

#include <freertos/FreeRTOS.h> /* FreeRTOSを用いるためのヘッダファイル */
#include <freertos/queue.h>    /* メッセージキューを用いるためのヘッダファイル */

QueueHandle_t  qhand_test;

#define DEF_ITEM_SIZE   32

void  task1( void *param )
{
  char  buff[ DEF_ITEM_SIZE ];

  while( 1 ) {
    /* メッセージ受信待ち */
    xQueueReceive( qhand_test, buff, portMAX_DELAY );
    Serial.printf( "xQueueReceive(): msg=%s\n", buff );
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.print("setup()\n");

  /* create event-group */
  qhand_test = xQueueCreate( 10, 
                             DEF_ITEM_SIZE );

  /* create task */
  xTaskCreatePinnedToCore( task1,
                           "TASK1",
                           0x800,
                           NULL,
                           1,
                           NULL,
                           0 );
}

char  *msg[] = {
    "message 1",
    "message 2",
    "message 3",
    "message 4",
    "message 5",
    NULL
};

void loop()
{
  BaseType_t  ret;
  static int  msg_pnt = 0;

  Serial.print("loop()\n");

  /* メッセージ送信 */
  ret = xQueueSend( qhand_test, msg[msg_pnt], 0 );
  if( ret != pdPASS ) {
    Serial.print("xQueueSend() fail.\n");
  }
  msg_pnt++;
  if( msg[msg_pnt] == NULL ) {
    msg_pnt = 0;
  }

  vTaskDelay(5000);
}

(4)セマフォ

セマフォはタスク間の同期をとるなどの待ち合わせに用いられます。
サンプルではsetup()内でセマフォ1つとタスク2つ生成し、
 タスク1がセマフォを獲得したら2秒間保持してセマフォを解放、
 タスク2がセマフォを獲得したら3秒間保持してセマフォを解放
を繰り返します。
各タスクはセマフォを獲得したら以下のように「task1」「task2」をシリアルモニタへ出力されることでセマフォの獲得・解放の動作が分かります。
なお、loop()がタスク1、タスク2とは関係なく動作していることが分かるよう、1秒ごとに「loop()」がこの出力の中に入り込みます。

「task1」
  ↓ 2秒
「task2」
  ↓ 3秒
「task1」
  ↓ 2秒
「task2」
  :
os4_sem.ino
/*
 *  Copyright(C) 2018 by Yukiya Ishioka
 */

#include <freertos/FreeRTOS.h> /* FreeRTOSを用いるためのヘッダファイル */
#include <freertos/semphr.h>    /* メセマフォを用いるためのヘッダファイル */

SemaphoreHandle_t  shand_test;

void  task1( void *param )
{
  while( 1 ) {
    xSemaphoreTake( shand_test, portMAX_DELAY );
    Serial.print("task1()\n");
    vTaskDelay(2000);
    xSemaphoreGive( shand_test );
  }
}

void  task2( void *param )
{
  while( 1 ) {
    xSemaphoreTake( shand_test, portMAX_DELAY );
    Serial.print("task2()\n");
    vTaskDelay(3000);
    xSemaphoreGive( shand_test );
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.print("setup()\n");

  shand_test = xSemaphoreCreateBinary();
  xSemaphoreGive( shand_test );

  /* create task */
  xTaskCreatePinnedToCore( task1,
                           "TASK1",
                           0x800,
                           NULL,
                           1,
                           NULL,
                           0 );

  /* create task */
  xTaskCreatePinnedToCore( task2,
                           "TASK2",
                           0x800,
                           NULL,
                           1,
                           NULL,
                           0 );
}

void loop()
{
  Serial.print("loop()\n");
  vTaskDelay(1000);
}

(5)イベント・グループ

イベント・グループははタスク間の同期をとるなどの待ち合わせに用いられますが、セマフォとは異なり1回の待ちで複数の要因で待つことができ、また、どの要因が発生したかも知ることができます。
サンプルではsetup()内でイベント・グループ1つとタスク2つ生成し、
 ①タスク1がイベント・グループのビット0とビット1の待ちでスリープします。
 ②タスク1がイベント・グループのビット0とビット1のいずれかを受信したらどのビットを受信したかシリアルへ出力します。
 ③タスク2は1秒周期でイベント・グループのビット1をセットします。
 ④loop()は5秒ごとにイベント・グループのビット0をセットします。
シリアルモニタへ出力されるメッセージでイベント・グループのセットと受信の動作が分かります。

os5_evg.ino
/*
 *  Copyright(C) 2018 by Yukiya Ishioka
 */

#include <freertos/FreeRTOS.h>      /* FreeRTOSを用いるためのヘッダファイル */
#include <freertos/event_groups.h>  /* イベントグループを用いるためのヘッダファイル */

EventGroupHandle_t  ehand_test;

void  task1( void *param )
{
  EventBits_t  ret;

  while( 1 ) {
    /* ビットセット待ち */
    ret = xEventGroupWaitBits( ehand_test, 0x03, pdTRUE, pdFALSE, portMAX_DELAY );

    if( ret & 0x01 ) {
      Serial.print("receive bit 0\n");
    } 
    if( ret & 0x02 ) {
      Serial.print("receive bit 1\n");
    } 
  }
}

void  task2( void *param )
{
  while( 1 ) {
    vTaskDelay(1000);
    xEventGroupSetBits( ehand_test, 0x02 );
  }
}

void setup()
{
  Serial.begin(115200);
  Serial.print("setup()\n");

  /* create event-group */
  ehand_test = xEventGroupCreate();

  /* create task */
  xTaskCreatePinnedToCore( task1,
                           "TASK1",
                           0x800,
                           NULL,
                           1,
                           NULL,
                           0 );

  /* create task */
  xTaskCreatePinnedToCore( task2,
                           "TASK2",
                           0x800,
                           NULL,
                           1,
                           NULL,
                           0 );
}

void loop()
{
  Serial.print("loop()\n");

  /* ビットをセット */
  xEventGroupSetBits( ehand_test, 0x01 );

  vTaskDelay(5000);
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
23
Help us understand the problem. What are the problem?