LoginSignup
40
39

More than 5 years have passed since last update.

FreeRTOSでマルチタスク (on ESP32)

Posted at

ESP32上でFreeRTOSによるマルチタスクを使う必要があったのでメモ。

利点

  • 複数のタスクを、他の処理時間に影響を受けずに実行できる
  • 優先順位や実行時間の振り分けはRTOSにお任せできる
  • 複数のタスクを、シンプルに記述することができる

RTOSにおける最低限の主なAPI

  • キュー:共有データの送受信
    • キューにデータをFIFOで積む
    • キューにデータがあった場合のみ処理が実行される
  • セマフォ:動作許可とそれによる同期処理
    • バイナリセマフォ:ほぼミューテックスと一緒 (FreeRTOSはこっち)
    • カウンティングセマフォ:アクセス数監視と排他制御
  • ミューテックス:複数スレッドからの共有資源アクセスの排他制御

API

キューデータ通信

  • xQueueCreate()
  • xQueueSend()
  • xQueueReceive()

セマフォ同期

  • xSemaphoreCreateBinary()
  • xSemaphoreGiveFromISR()
  • xSemaphoreTake()

ミューテックス排他制御

  • xSemaphoreCreateMutex()
  • xSemaphoreGive()
  • xSemaphoreTake()

FreeRTOSにおけるセマフォとミューテックス

  • 動作の許可を示すバイナリセマフォを同期で使う時はセマフォ
  • 排他制御で使う時はミューテックスと呼ぶ
  • 使用するAPIは、どちらもxSemaphoreGive()とxSemaphoreTake()
  • 正しくは違うがざっくりいうとこんな感じ

これら2つの明確な違いは、

  • セマフォ同期のvSemaphoreCreateBinary()では、初期値で動作許可が無い
  • ミューティックス排他制御のxSemaphoreCreateMutex()では、初期値で動作許可がある

サンプルコード

マルチタスクを作成するAPI

xtaskCreatePinnedToCore.cpp
 BaseType_t xTaskCreatePinnedToCore(
    TaskFunction_t pvTaskCode,        // task_func_name
    const char *constpcName,          // task_name
    const uint32_t usStackDepth,      // stack_memory_size
    void *constpvParameters,          // ptr that will be used as the param for the task being created
    UBaseType_t uxPriority,           // task_priority (0-25, priority is 0 < 25)
    TaskHandle_t *constpvCreatedTask, // task_handle_pointer
    const BaseType_t xCoreID          // core ID
 )

シンプルなマルチタスク

simple_multitask.cpp
 void task0(void* arg)
 {
     while (1)
     {
         static int count = 0;
         Serial.print("task 0 : ");
         Serial.println(count++);
         delay(1000);
     }
 }

 void task1(void* arg)
 {
     while (1)
     {
         static int count = 0;
         Serial.print("task 1 : ");
         Serial.println(count++);
         delay(2000);
     }
 }

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

     // create tasks
     xTaskCreatePinnedToCore(task0, "Task0", 4096, NULL, 1, NULL, 0);
     xTaskCreatePinnedToCore(task1, "Task1", 4096, NULL, 1, NULL, 1);
 }

 void loop()
 {
     static int count = 0;
     Serial.print("main   : ");
     Serial.println(count++);
     delay(500);
 }

キューによる通信

queue.cpp
 QueueHandle_t xQueue;

 static void vSendTask (void *pvParameters)
 {
     BaseType_t xStatus;
     int32_t SendValue = 10;

     while (1)
     {
         xStatus = xQueueSend(xQueue, &SendValue, 0);

         if(xStatus != pdPASS) // send error check
         {
             while(1)
             {
                 Serial.println("rtos queue send error, stopped");
                 delay(1000);
             }
         }
         if(SendValue == 10) SendValue = 20;
         else                SendValue = 10;

         delay(2000);
     }
 }

 static void vRecvTask(void *pvParameters)
 {
     BaseType_t xStatus;
     int32_t ReceivedValue = 0;
     const TickType_t xTicksToWait = 500U; // [ms]

     while (1)
     {
         // loop in xTicksToWait and
         // when data is received, this func is triggerred
         xStatus = xQueueReceive(xQueue, &ReceivedValue, xTicksToWait);

         Serial.println("check if data is received");

         if(xStatus == pdPASS) // receive error check
         {
             Serial.print("received data : ");
             Serial.println(ReceivedValue);
         }
         else
         {
             if(uxQueueMessagesWaiting(xQueue) != 0)
             {
                 while(1)
                 {
                     Serial.println("rtos queue receive error, stopped");
                     delay(1000);
                 }
             }
         }
     }
 }

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

     // create queue : type = int32_t, size = 5
     xQueue = xQueueCreate(5, sizeof(int32_t));

     if(xQueue != NULL)
     {
         xTaskCreate(vSendTask, "TaskA", configMINIMAL_STACK_SIZE, NULL, 1, (TaskHandle_t *) NULL);
         xTaskCreate(vRecvTask, "TaskB", configMINIMAL_STACK_SIZE, NULL, 2, (TaskHandle_t *) NULL);
         Serial.println("tasks registered");
     }
     else
     {
         while(1)
         {
             Serial.println("rtos queue create error, stopped");
             delay(1000);
         }
     }
 }

 void loop()
 {
 }

セマフォによる割り込み同期

semaphore.cpp
 SemaphoreHandle_t xBinarySemaphore;

 void handleIRQ(void)
 {
     BaseType_t xHigherPriorityTaskWoken;
     xHigherPriorityTaskWoken = pdFALSE;

     xSemaphoreGiveFromISR(xBinarySemaphore, &xHigherPriorityTaskWoken); // can be used in ISR
 }

 static void vISRTask (void *pvParameters)
 {
     BaseType_t xStatus;
     const TickType_t xTicksToWait = 1000U; // [ms]

     Serial.println("check for isr");

     while(1)
     {
         xStatus = xSemaphoreTake(xBinarySemaphore, xTicksToWait);

         Serial.println("check for interrupt");

         if(xStatus == pdTRUE)
         {
             static int count = 0;
             Serial.print("take : ");
             Serial.println(count++);
         }
     }
 }


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

     xBinarySemaphore = xSemaphoreCreateBinary();

     if(xBinarySemaphore != NULL)
     {
         xTaskCreate(vISRTask, "Task", configMINIMAL_STACK_SIZE, NULL, 1, (TaskHandle_t *)NULL);
     }
     else
     {
         while(1)
         {
             Serial.println("rtos semaphore create error, stopped");
             delay(1000);
         }
     }
 }

 void loop()
 {
     // virtual interrupt
     Serial.println("give");
     handleIRQ();
     delay(2000);
 }

ミューテックスによる排他制御

mutex.cpp
 SemaphoreHandle_t xMutex = NULL;
 int sharedResource = 0;

 static void vTask1(void *pvParameters)
 {
     BaseType_t xStatus;
     const TickType_t xTicksToWait = 1000UL; //
     xSemaphoreGive(xMutex);

     while(1)
     {
         xStatus = xSemaphoreTake(xMutex, xTicksToWait);

         Serial.println("check for mutex (task1)");

         if(xStatus == pdTRUE)
         {
             sharedResource = 100;
             Serial.print("shared resource change by task1 : ");
             Serial.println(sharedResource);
         }

         xSemaphoreGive(xMutex);
         delay(500);
     }
 }

 static void vTask2(void *pvParameters)
 {
     BaseType_t xStatus;
     const TickType_t xTicksToWait = 500UL;
     xSemaphoreGive(xMutex);

     while(1)
     {
         xStatus = xSemaphoreTake(xMutex, xTicksToWait);

         Serial.println("check for mutex (task2)");

         if(xStatus == pdTRUE )
         {
             sharedResource = 1;
             Serial.print("shared resource change by task2 : ");
             Serial.println(sharedResource);
         }

         xSemaphoreGive(xMutex);
         delay(1000);
     }
 }

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

     xMutex = xSemaphoreCreateMutex();

     if( xMutex != NULL )
     {
         // higher priority task is done before lower priority task
         // if there are some same priority tasks, they are kicked in order of registration
         xTaskCreate(vTask1, "Task1", configMINIMAL_STACK_SIZE, NULL, 1,(TaskHandle_t *) NULL);
         xTaskCreate(vTask2, "Task2", configMINIMAL_STACK_SIZE, NULL, 2,(TaskHandle_t *) NULL);
     }
     else {
         while(1)
         {
             Serial.println("rtos mutex create error, stopped");
             delay(1000);
         }
     }
 }

 void loop()
 {
 }

その他:マルチタスクにおけるdelay()について

  • esp32-arduinoでは、delay() は vTaskDelay() で実装されてるので、タスク内でも使える

その他:関数などのプレフィックスについて

参考

40
39
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
40
39