2018년 7월 2일 월요일

ESP32에서 FreeRTOS 사용 - TASK에 parameter 넘겨주기

FreeRTOS에서 태스크를 만드는 방법은 이전 포스트에서 설명했다. 이때 사용하는 API에 보면 몇개의 파라미터가 있는데 "void *pvParameters", "UBaseType_t uxPriority", "TaskHandle_t *pxCreatedTask"를 사용하는 법을 설명하겠다.

BaseType_t xTaskCreate(  TaskFunction_t pvTaskCode,
                         const char * const pcName,
                         uint16_t usStackDepth,
                         void *pvParameters,
                         UBaseType_t uxPriority,
                         TaskHandle_t *pxCreatedTask)

데모

1) Task Parameter
"void *pvParameters"는 TaskFunction_t에 입력을 전달해주기 위해 사용한다. 예를 들어 void hello_world_task(void *param) 이라는 태스크를 가지고 있으면 "void *pvParameters"는 "void *param"이다. 우리는 이 예제에서 태스크를 만들고 그 태스크에 파라미터를 전달할 때 이 파라미터가 태스크 함수에 전달되고, 터미널에 이 파라미터를 출력한다.

void setup() {
  Serial.begin(115200);
  char task1Param[13] = "task1Param";

  xTaskCreate (task1,   // task function
               "task1", // name of task
               10000,   // stack size of task
               (void *) task1Param,  // parameter of the task
               1,       // priority of the task
               NULL);   // task handle to keep track of created task
}

void loop() {
}

// this function will be invoked when additionalTask was created
void task1 ( void * parameter )
{
  Serial.println((char *)parameter);
  while (1) {    // loop forever
    Serial.println("task1 is running");
    delay(1000);
  }
  vTaskDelete( NULL ); // delete a task when finish,
                       // this will not happen
                       // because this is infinitely loop
}

2) Task Priorities

FreeRTOS는 3가지 종류의 스케쥴링 방식을 지원한다.

- Co-operative scheduling
실행중인 태스크는 running 상태에서 blocked 상태로 바뀌거나 또는 taskYIELD()를 호출할 때 컨택스트 스위칭이 발생한다. 그럼 나머지 태스크 중에 가장 높은 우선순위를 가지고 있는 태스크가 실행되게 된다.

- Prioritized pre-emptive scheduling with time slicing
높은 우선순위(priority)를 가진 태스크가 낮은 우선순위를 가진 태스크를 preempt한다. 같은 우선순위를 가진 태스크 끼리는 time tick 인터럽트가 발생할 때 마다 순서를 바꿔 실행한다. (RTOS는 시간을 측정하기 위한 타이머 인터럽트를 가지고 있는데, 매번 인터럽트가 발생할 때 마다 RTOS는 태스크를 unblock 또는 깨울 시간인지 체크한다.)

- Priotitized pre-emptive scheduling without time slicing
높은 우선순위(priority)를 가진 태스크가 낮은 우선순위를 가진 태스크를 preempt한다. 같은 우선순위를 가진 태스크는 매번 time tick 인터럽트가 발생할 때마다 순서를 바꾸지 않는다. (실행중인 태스크는 더 높은 우선순위를 가진 태스크에 의해 preempt 될 때 까지 계속 실행한다.)

2-1) Prioritized pre-emptive scheduling with time slicing 데모
현재 Arduino ESP32의 FreeRTOS는 Prioritized pre-emptive scheduling with time slicing을 사용하도록 설정되어 있으므로 여기서는 이 방식의 스케쥴링 데모를 만들어 보았다. 2개의 태스크를 만드는데 task1은 우선순위 1, task2는 우선순위 4를 가진다. task1은 "task1 is running"과 "task1 is ending"이라는 글자를 터미널에 출력한다. task2는 "task2 is running"과 "task2 is ending"이라는 글자를 터미널에 출력한다. 그러므로 대부분의 시간은 task2가 터미널에 출력하는데 사용된다.


void setup() {
  Serial.begin(115200);
  char task1Param[12] = "taskParam";
  xTaskCreate(
      task1,        // Task function
      "task1",      // name of task
      10000,        // Stack size of task
      (void *)task1Param,  // parameter of the task
      1,            // priority of the task 
      NULL);        // Task handle to keep track of created task 
  xTaskCreate(
      task2,        // Task function. 
      "task2",      // name of task. 
      10000,        // Stack size of task 
      (void *)task1Param,  // parameter of the task 
      4,            // priority of the task 
      NULL);        // Task handle to keep track of created task 
}

void loop() {
}
 
// this function will be invoked when additionalTask was created 
void task1( void * parameter )
{
  Serial.println((char *)parameter);

  while(1){
    Serial.println("task1 is running");
    Serial.println("task1 is ending");
  }
  vTaskDelete( NULL );
}

// this function will be invoked when additionalTask was created 
void task2( void * parameter )
{
  Serial.println((char *)parameter);
  while (1){
    Serial.println("task2 is running");
    Serial.println("task2 is ending");
  }
  vTaskDelete( NULL );
} 
 
 
 
2-2) Task Handle

Task Handle은 만들어 진 테스크의 레퍼런스를 가지고 있기 때문에 나중에 그 태스크를 삭제하거나 우선순위를 바꾸는 등의 작업을 할 수 있다.



2-2-1) Task Handle 데모
위의 2-1) 데모 코드를 재사용하는데 요구사항을 약간 바꿔 보았다. task2가 20번 실행되면 task1의 우선순위를 4로 바꾸고 task2의 우선순위를 1로 바꿔주어 이제부터는 터미널에 task1이 출력하는 내용이 대부분을 차지하게 된다.

// task handle variables to hold instances of tasks
TaskHandle_t t1 = NULL;
TaskHandle_t t2 = NULL;
int count = 0;

void setup() {
  Serial.begin(115200);
  char task1Param[12] = "taskParam"
 
  xTaskCreate(
      task1,     // Task function. 
      "task1",   // name of task. 
      10000,     // Stack size of task 
      (void *)task1Param,  // parameter of the task 
      1,         // priority of the task 
      &t1);      // Task handle to keep track of created task 
  xTaskCreate(
      task2,     // Task function. 
      "task2",   // name of task. 
      10000,     // Stack size of task 
      (void *)task1Param,  // parameter of the task 
      4,         // priority of the task 
      &t2);      // Task handle to keep track of created task 
}

void loop() {
}
 
// this function will be invoked when additionalTask was created 
void task1( void * parameter )
{
  Serial.println((char *)parameter);
  while (1){
    Serial.println("task1 is running");
    Serial.println("task1 is ending");
  }
  vTaskDelete( NULL );
}

// this function will be invoked when additionalTask was created 
void task2( void * parameter )
{
  Serial.println((char *)parameter);

  while (1) {
    count++;
    // if count is 20 then we swap the priority of 2 tasks 
    if(20 == count){
      vTaskPrioritySet( t1, 4 );
      vTaskPrioritySet( t2, 1 );
    }
    Serial.println("task2 is running");
    Serial.println("task2 is ending");
  }
  vTaskDelete( NULL );
}




 2-3) Idle Task Hook Function

Idle 태스크(백그라운드 태스크)는 다음과 같은 특징을 가진다.
- 스케쥴러에 의해 만들어 짐
- 가장 낮은 우선순위를 가짐 (우선순위 0)
- 다른 모든 태스크들이 blocked 상태일 때 실행상태가 됨

Idle 태스크에 함수를 hook하는것이 가능하다. 이 함수는 idle 태스크가 실행될 때 호출된다.
이 태스크를 사용해 다음과 같은 일을 하는데 사용할 수 있다.
 - 백그라운드에서 어떤 작업 수행
- 시스템의 spare time 측정
- 할 일이 없을 떼 마이크로 프로세서를 low power mode로 진입시킴
- 태스크가 삭제될 때 리소스 정리

주의)  태스크 hook 함수내에서 idle 태스크를 block시키는 어떠한 작업도 하면 안된다.

task hook 함수를 사용하려면 단순히 "bool vApplicationIdleHook(void)"  프로토타입의 함수를 구현해 주고 esp_register_freertos_idle_hool(vApplicationIdleHook)을 호출해 hook 함수를 등록해 주면 된다.

2-3-1) 데모
task hook 함수를 만들어 주고 이 함수가 호출될 때 마다 카운터를 1씩 증가시킨다. 생성된 태스크 함수에서는 단지 이 값을 출력한다.

#include "esp_freertos_hooks.h"
int counter = 0;

void setup() {
  Serial.begin(115200);
  esp_register_freertos_idle_hook(vApplicationIdleHook);
  char task1Param[12] = "task1Param";

  xTaskCreate(
      task1,     // Task function. 
      "task1",   // name of task. 
      10000,     // Stack size of task 
      (void *)task1Param,   // parameter of the task 
      1,         // priority of the task 
      NULL);     // Task handle to keep track of created task 
}


void loop() {
}
 
// this function will be invoked when additionalTask was created 
void task1( void * parameter )
{
  Serial.println((char *)parameter);

  while (1) {
    Serial.print("task1 is running and counter : ");
    Serial.println(counter);
    if(counter > 30000)
    {
      counter = 0;  
    }
    // block this task so that Idle task has chance to run 
    delay(10);
  }
  vTaskDelete( NULL );
}

// define task hook function 
bool vApplicationIdleHook(void)
{
  counter++;
}



댓글 없음:

댓글 쓰기