ESP32 RTOSでイベントグループを使ってみる (Arduino)

ESP32 RTOSでイベントグループを使ってみる (Arduino)

今回も Arduino のフレームワークで RTOS を使います。
今回はイベントグループを使ってみます。

この記事は JTAG でデバッグすることを前提にして書いています。
環境構築については こちら をご覧になってください。

投稿時の開発環境を記しておきます。

PC:
Windows10 OS

開発ボード :
ESP32-DevKitCーVE
(Soc : ESP32-D0WD-V3)

デバッガー(H/W):
FT2232D

デバッガー (S/W) :
Visual Studio Code + PlatformIO + Arduino Framework

FreeRTOS

こちら が本家ですが、ESP32用としては このへん を見るのが良さそうです。

イベントグループとは

イベントグループは例えばタスクBの処理は他のタスクAの処理が完了してから実行したい場合等、タスク間の同期をとるのに使われます。
その他に時間経過を知らせたり、スイッチが押されたことを知らせたりすることもできます。

今回はイベントグループを使ってタイマーの時間経過を知らせるコードを書いてみます。
タイマー割り込みを使って1秒毎にイベントグループのビット0をセットします。
待機側でイベントグループのビット0がセットされるたびにLEDを点滅させます。

Lチカ用に追加する回路

今回はイベントを待つ側でLチカすることで動作確認できるようにします。

LEDをGPIO18につなぐ例です。

プロジェクトをつくる

VSCodeで、もし使っていたプロジェクトを開いていたら、File – Close Folder して閉じておきます。

その後にVSCodeからPlatformIOをOpenします。

以下の内容でプロジェクトを新規に作成します。

Name : ESP32A-RTOS-EventGroup
Board : Espressif ESP32 Dev Module
Framework : Arduino Framework

Name : ESP32A の “A” は Framework (Arduino Framework)の頭文字を示しています。

(後から見てわかるように、Arduinoを使うことを明示しています)

platformio.ini に以下の4行を追加して、 Ctrl + s で保存しておきます。

COM[8]の8の部分はデバイスマネージャーのポート(COMとLPT)で Silicon Labs CP210x から始まるCOMの番号を記述します。

upload_port はプログラムをアップロードする時に使うCOMポートを指定します。
monitor_port は printf()出力をモニターするCOMポートを指定します。

debug_tool = minimodule
upload_port = COM[8]
monitor_port = COM[8]
monitor_speed = 115200

今回使うイベントグループに関連するAPI

xEventGroupCreate()

機能:イベントグループを作成する
引数:なし
戻り値:作成されたイベントグループへのハンドルが返る。

xEventGroupSetBitsFromISR()

機能:イベントグループのビットをセットする
第1引数:イベントグループへのハンドル
第2引数:セットするビットを指定する
第3引数:pxHigherPriorityTaskWokenへのポインタ
戻り値:pdPASSで正常、キューがいっぱいの場合pdFALSEが返る

補足:この関数を呼び出すと、タイマー デーモン タスクにメッセージが送信されます。タイマー デーモン タスクの優先度が、現在実行中のタスク (割り込みが中断されたタスク) の優先度よりも高い場合、xEventGroupSetBitsFromISR () によって *pxHigherPriorityTaskWoken が pdTRUE に設定され、割り込みが終了する前にコンテキスト スイッチを要求する必要があることを示します。

xEventGroupWaitBits()

機能:イベントを待つ
第1引数:イベントグループへのハンドル
第2引数:待機するビット
第3引数:pdTRUEの場合、待機条件が満たされるとセットされたビットはクリアされる
第4引数:pdTRUEの場合、待機指定した全てのビットがセットされるか指定した時間が経過すると戻る。pdFALSEの場合、指定したいずれかのビットがセットされるか指定した時間が経過すると戻る。
第5引数:ビットが設定されるまで待機する時間(0を指定するとすぐに戻ってくる)
戻り値:待機中のビットが設定された時点、またはブロック時間が満了した時点のイベント グループの値

コーディングする

main.cppを以下のようにコーディングします。

#include <Arduino.h>

EventGroupHandle_t xCreatedEventGroup;

#define IO18 18
#define BIT_0 (1 << 0)

TaskHandle_t taskHandle[4];
hw_timer_t *timer = NULL;

//void IRAM_ATTR onTimer1000ms() {
//  xEventGroupSetBits(xCreatedEventGroup, BIT_0);
//}

void IRAM_ATTR onTimer1000ms() {
  BaseType_t xHigherPriorityTaskWoken, xResult;
  xHigherPriorityTaskWoken = pdFALSE;
  xResult = xEventGroupSetBitsFromISR(xCreatedEventGroup, BIT_0,
                                      &xHigherPriorityTaskWoken);
  if (xResult == pdPASS) {
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  }
}

void eventReceiver(void *args) {
  int ex = 0;
  while (true) {
    EventBits_t bits =
        xEventGroupWaitBits(xCreatedEventGroup, BIT_0, pdTRUE, pdFALSE, 0);
    if (bits & BIT_0) {
      if (ex & 1) {
        digitalWrite(IO18, HIGH);
      } else {
        digitalWrite(IO18, LOW);
      }
      ex ^= 1;
    }
    vTaskDelay(5);
  }
}

void setup() {
  xCreatedEventGroup = xEventGroupCreate();
  pinMode(IO18, OUTPUT);
  digitalWrite(IO18, LOW);

  xTaskCreatePinnedToCore(eventReceiver, "eventReceiver", 4096, NULL, 1,
                          &taskHandle[0], APP_CPU_NUM);

  // 1secのタイマー割り込み
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer1000ms, true);
  timerAlarmWrite(timer, 1000000, true);
  timerAlarmEnable(timer);
}

void loop() {}

ビルドして実行する

ビルドしてエラーがないことを確認して、メニューから Run – Start Debugging (またはF5キー)すると少ししてから

loopTaskWDTEnabled = false; の行で停止します。

もう一度 F5キーを押すとプログラムを実行します。

こちらの環境では1秒毎にLEDが点滅することが確認できました。

いかがでしたか?
イベントグループをうまく使うことができましたか。

EventGroupカテゴリの最新記事