LoginSignup
9
18

More than 3 years have passed since last update.

ArduinoでFreeRTOS(FreeRTOS理解その1:セマフォ、キューなど)

Last updated at Posted at 2020-06-07

Arduino上でFreeRTOSの基本的な動作を試す

FreeRTOS

本家サイトはこちら

Arduinoでの準備

FreeRTOSライブラリをインストールする。
ライブラリインストール.PNG
インストールするとFreeRTOSに関するいくつかのスケッチサンプルが見つかる。今回は、タスク間通信を試したかったので、次のサンプルを利用した(組み合わた)。
- AnalogRead_DigitalRead セマフォ
- integerQueue       キュー
- TaskStatus        タスクSuspend/Resume

具体的に実現したいこと

  • PCからの入力数値の回数分、LEDのオンオフを行う
  • PCからの入力により、LEDのオンオフを一時的に止める、その後再開する
  • LEDのオンオフのメッセージおよびボタン押下のメッセージをPCに送るが、共有資源(ここではシリアル回線)の競合でメッセージが送れないケースがあることの確認

ソースコード

インクルード、関数定義など

#include <Arduino_FreeRTOS.h>  // Include Arduino FreeRTOS library
#include <queue.h>             // Include queue support
#include <semphr.h>            // add the FreeRTOS functions for Semaphores (or Flags).

//define task handles
TaskHandle_t TaskBlink_Handler;
TaskHandle_t TaskButton_Handler;
TaskHandle_t TaskSerial_Handler;

QueueHandle_t InterTaskQueue; // define Queue
SemaphoreHandle_t SerialSemaphore; // define Semaphore

// define tasks for Blink & Button & Serial
void TaskBlink(void *pvParameters);
void TaskButton(void *pvParameters);
void TaskSerial(void* pvParameters);
  • タスクはArduino上のコンポネートを扱う次の3つ
    1. LED(TaskBlink)
    2. ボタン(TaskButton)
    3. PCとの通信(TaskSerial)
  • タスク間のデータ通信用のキュー(InterTaskQueue)
  • 共有資源(ここではSerial回線)をコントロールするセマフォ(SerialSemaphore)

その他は上記のとおり。

Arduinoお決まりのsetup(),loop()

void setup() {
  Serial.begin(9600);

  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB, on LEONARDO, MICRO, YUN, and other 32u4 based boards.
  }

  if ( SerialSemaphore == NULL )  // Check to confirm that the Serial Semaphore has not already been created.
  {
    SerialSemaphore = xSemaphoreCreateMutex();  // Create a mutex semaphore we will use to manage the Serial Port
    if ( ( SerialSemaphore ) != NULL )
      xSemaphoreGive( ( SerialSemaphore ) );  // Make the Serial Port available for use, by "Giving" the Semaphore.
  }

  // Create a queue
  InterTaskQueue = xQueueCreate(1, // Queue length
                        sizeof(int)); // Queue item size

  if (InterTaskQueue != NULL) {
     xTaskCreate(
      TaskBlink
      ,  "Blink"   // A name just for humans
      ,  128  // This stack size can be checked & adjusted by reading the Stack Highwater
      ,  NULL //Parameters passed to the task function
      ,  2  // Priority, with 2 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
      ,  &TaskBlink_Handler );//Task handle

    xTaskCreate(TaskButton, "Button", 128, NULL, 1, &TaskButton_Handler);
    xTaskCreate(TaskSerial, "Serial", 128, NULL, 0, &TaskSerial_Handler);

    vTaskStartScheduler();  // start scheduler, however, if it doesn't exist, it seems to work.
  }
}  

void loop()
{
  // Empty. Things are done in Tasks.
}
  1. setup()
    • セマフォ作成
    • キュー作成
    • タスク作成(優先度Priorityには注意が必要のようです。)
  2. loop()
    • なし

TaskSerial

void TaskSerial(void* pvParameters){
  (void) pvParameters;

   for (;;) // A Task shall never return or exit.
   {
    while(Serial.available()>0){
      String str = Serial.readString();
      if (str[0] == 's') {
        vTaskSuspend(TaskBlink_Handler); // Make TaskBlink Suspend
        Serial.println("Suspend!");
      } else if (str[0] == 'r') {
        vTaskResume(TaskBlink_Handler);  // Make TaskBlink Resume
        Serial.println("Resume!");
      } else { // not considering all error cases
        int val = str.toInt();
        xQueueSend(InterTaskQueue, &val, portMAX_DELAY);  // Send value to TaskBlink
      }
      vTaskDelay(1);
    }
   }
}

シリアル回線から受け取るデータによるアクションの定義
- 's'を受け取ったら、TaskBlinkを一時中断
- 'r'を受け取ったら、TaskBlinkを再開
- それ以外(ここでは数値、細かいエラー制御は省略)は数値に変換してキューに積む

vTaskDelay()で他のタスクに制御が確実移行する(ようだ)。

TaskButton

void TaskButton(void *pvParameters)
{
  (void) pvParameters;
  const int DIN_PIN = 7;  // Pin from 5V is connected to one leg of button. The other leg is connected to GND.
  int value;

  pinMode( DIN_PIN, INPUT_PULLUP );  // External resistor is not necessary. 
  for (;;) // A Task shall never return or exit.
  {
    value = digitalRead( DIN_PIN );
    if ( value == LOW ){
      // If the semaphore is not available, wait 5 ticks of the Scheduler to see if it becomes free.
      if ( xSemaphoreTake( SerialSemaphore, ( TickType_t ) 5 ) == pdTRUE )
      {
        Serial.println("Button Pushed");
        delay(1500); // sleep at purpose to make other tasks delay getting the Serial Semaphore (prevention of Serial.println)
        xSemaphoreGive( SerialSemaphore ); // Now free or "Give" the Serial Port for others.
      }
    }
    vTaskDelay(1); // one tick delay (15ms) in between reads for stability??
  }
}
  • PIN 7をPULL_UPとして入力に設定
  • PIN 7から入力があったらセマフォ取得
  • シリアル回線にメッセージを送る
  • わざと1500ms眠る(TaskBlinkのセマフォ取得を妨げるため)
  • セマフォ解放

TaskBlink

void TaskBlink(void *pvParameters)
{
  (void) pvParameters;
  pinMode(LED_BUILTIN, OUTPUT);

  for (;;) // A Task shall never return or exit.
  {
    int count;
    if (xQueueReceive(InterTaskQueue, &count, portMAX_DELAY) == pdPASS) {
      for (int i=0; i<count; i++) {
        if ( xSemaphoreTake( SerialSemaphore, ( TickType_t ) 5 ) == pdTRUE )
        {
          char buf[10];
          sprintf(buf, "N=%d", i+1);
          Serial.println(buf);
          xSemaphoreGive( SerialSemaphore ); // Now free or "Give" the Serial Port for others.
        }
        digitalWrite(LED_BUILTIN, HIGH);
        vTaskDelay( 500 / portTICK_PERIOD_MS ); // wait for 500ms
        digitalWrite(LED_BUILTIN, LOW);
        vTaskDelay( 500 / portTICK_PERIOD_MS ); // wait for 500ms
      }
    }
  }
}
  • Arduino搭載LEDの設定
  • キューからデータ(LEDのオンオフを行う回数)を受け取る
  • セマフォ取得
  • 何回目のLEDオンオフかのメッセージをシリル回線に送る
  • LEDのオンオフ

実験

TaskBlinkの一時中断および再開

Suspend&Resume.PNG
再開(Resume)されるまで、TaskBlinkのメッセージ送信が止まる。

セマフォロック中のためメッセージが送れないケース

資源ロック中のSleepの結果.PNG
TaskButtonにてボタン押下後1500msもSleepし、TaskBlinkにてセマフォ取得できず、"N=4"および"N=6"メッセージが送ることができていない。

おわりに

次は、タスク優先度(Priority)の実験を行う。

9
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
18