2019년 4월 12일 금요일

ESP32에서 멀티코어 사용하기 (Use multicore on ESP32)

ESP32는 2개의 Xtensa 32-bit LX6 마이크로 프로세서 코어를 가지고 있다. 아두이노 IDE에서 코드를 실행하면 디폴토로 코어 1에서 실행된다. 여기서는 태스크를 만들어 두번째 코어에서 코드를 실행하는 방법을 설명한다. 그렇게 하면 두개의 코어에서 동시에 코드를 실행해서 멀티태스킹을 할 수 있다.

코드가 어느 코어에서 실행되고 있는가를 확인하려면 xPortGetCoreID() 함수를 사용하면 된다.

아래의 간단한 테스크 코드를 실행시켜 보면 setup(), loop() 함수 모두 코어 1에서 실행되는걸 확인할 수 있다.

void setup()
{
  Serial.begin(115200);
  Serial.print("* setup() is running on core ");
  Serial.println(xPortGetCoreID());
}

void loop()
{
  Serial.print("* loop() is running on core ");
  Serial.println(xPortGetCoreID());
  delay(100);
}




ESP32를 아두이노 IDE에서 사용할 때 기본적으로 리얼타임 OS인 FreeRTOS를 지원한다. 그러므로 FreeRTOS를 사용하면 여러개의 태스크를 독립적으로 병렬로 실행할 수 있다.

태스크는 무언가를 수행하는 코드의 조각이다. 예를 들어 LED를 깜빡이거나 네트웍 통신, 센서 읽기, 센서값을 네트워그로 전송등 다양한 작업을 수행할 수 있다.

코드의 특정 부분이 특정 코어에서 실행되도록 할당하려면 태스크를 만들어 줘야 한다. 태스크를 만들 때 우선순위 뿐 아니고 어느 코어에서 실행할지 고를 수 있다. 우선순위는 0에서 시작하고 0이 가장 우선순위가 낮다. 프로세서는 우선순위가 높은 태스크를 먼저 실행한다.





1. 태스크를 만들려면 먼저 태스크 핸들을 만들어 준다.

TaskHandle_t Task1;

2. setup()에서 xTaskCreatePinnedToCore 함수를 사용해 특정 코어에 고정된 태스크를 만들어 준다.

xTaskCreatePinnedToCore (
  Task1code,      // 태스크를 구현한 함수
  “Task1”,        // 태스크 이름
  10000,          // 스택 크기 (word단위)
  NULL,           // 태스크 파라미터
  0,              // 태스크 우선순위
  &Task1,         // 태스크 핸들
  0 );            // 태스크가 실행될 코어


3. 만들어 진 태스크가 실행할 코드가 들어있는 함수를 만들어 줘야만 한다. 위의 예제에서는 Task1Code 함수를 만들어 줘야 한다.

void Task1Code( void *param)
{
  while (1) {
    // 태스크 1이 실행할 코드가 들어갈 부분
    // 무한루프로 실행됨
  }
}


코드가 실행되는 도중 만들어 진 태스크를 삭제하고 싶으면 vTaskDelete() 함수를 사용하면 된다. 이 함수의 파리미터로 지우고자 하는 태스크의 핸들(위의 예제에서는 Task1)을 넘겨준다.

예제


ESP32의 2번핀에 LED1을, 4번핀에 LED2를 연결하고 두개의 서로 다른 태스크가 각각 LED1은 1000ms마다, LED2는 700ms마다 깜빡이게 만든다. 여기서 Task1은 코어 0에서, Task2는 코어 1에서 실행된다.




#define LED1 2
#define LED2 4

TaskHandle_t Task1;
TaskHandle_t Task2;

void setup()
{
  Serial.begin(115200);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);

  xTaskCreatePinnedToCore(
    blink1000,         // 태스크 함수
    "Task1",           // 테스크 이름
    10000,             // 스택 크기(워드단위)
    NULL,              // 태스크 파라미터
    1,                 // 태스크 우선순위
    &Task1,            // 태스크 핸들
    0);                // 실행될 코어

  xTaskCreatePinnedToCore(
    blink700,          // 태스크 함수
    "Task2",           // 테스크 이름
    10000,             // 스택 크기(워드단위)
    NULL,              // 태스크 파라미터
    1,                 // 태스크 우선순위
    &Task2,            // 태스크 핸들
    1);                // 실행될 코어
}

void blink1000 ( void *param )
{
  Serial.print("# Task 1 running on core ");
  Serial.println(xPortGetCoreID());

  while (1) {
    digitalWrite(LED1, HIGH);
    delay(1000);
    digitalWrite(LED1, LOW);
    delay(1000);
  }
}

void blink700 ( void *param )
{
  Serial.print("# Task 2 running on core ");
  Serial.println(xPortGetCoreID());

  while (1) {
    digitalWrite(LED2, HIGH);
    delay(700);
    digitalWrite(LED2, LOW);
    delay(700);
  }
}

void loop()
{
}






댓글 없음:

댓글 쓰기