'2020/04/08'에 해당되는 글 1건
- 2020.04.08 Hardware | ESP32 의 Dual core 확인해 보기 2
ESP32 대한 지금까지 글은 아래 포스팅들을 참고해 주세요.
* Hardware | ESP32 스펙 확인해 보기
- https://chocoball.tistory.com/entry/Hardware-ESP32-spec-check
* Hardware | ESP32 간단 사용기
- https://chocoball.tistory.com/entry/Hardware-simple-review-ESP32
1. Dual core 활용에 앞서
ESP32 의 최장점 중 하나인, dual core 활용에 앞서 개념적인 이야기들이 조금 필요하다고 생각되었다.
그 이유는, 제가 dual core 활용을 이해하고 실행해 보기까지 삽질한 흔적들을 공유하기 위해서 이다.
아직 100% 맞는 이야기 인지는 확신이 들지 않지만, 대략 문제는 없어 보이니, 서두를 조금 길게 하겠다.
2. Arduino IDE 와 ESP-IDF
우선 ESP32 에 프로그램을 올리기 위해서는 coding 하는 환경이 필요한데, 이 개발환경이 ESP-IDF 이다.
보통 ESP-IDF framework 라 부른다.
다만, 우리가 친숙한 Arduino IDE 환경에서도 개발할 수 있도록, Arduino IDE 의 Arduino Core 가 ESP-IDF 를 포함하고 있다.
말하자면, ESP-IDF 의 간편한 interface 인 셈이다.
* ESP-Jumpstart: Build ESP32 Products Fast
- https://docs.espressif.com/projects/esp-jumpstart/en/latest/gettingstarted.html
살짝 다른 이야기 이지만, 위의 글에서도 설명 되어 있 듯, ESP32 세계에서 이야기 하는 firmware 는, 흔히 OS 기저에 존재하는 HW/OS 사이를 interfacing 해주는 미들웨어가 아니라, 구동 프로그램 자체를 이야기 한다. 헷갈리지 말자.
이 용어의 혼란 때문에, ESP32 의 firmware 를 업데이트 하고자 답도 없는 웹페이지들을 읽어댓다.
3. ESP-IDF FreeRTOS
그럼 자주 등장하는 FreeRTOS 는 도대체 뭔가?
* Quality RTOS & Embedded Software
- https://www.freertos.org/a00106.html
이름의 약자 부분이 "RTOS - Real Time OS" 인 실시간 처리를 위한 OS이고, 그 중에 가장 유명한 open source 인 RTOS 를 뜻한다.
결국, 아래 발췌한 문구에서 볼 수 있 듯, ESP-IDF framework 는 이 FreeRTOS 실시간 운영 체제를 기반으로 하고 있다.
The ESP-IDF FreeRTOS is a modified version of vanilla FreeRTOS which supports symmetric multiprocessing (SMP).
이 FreeRTOS 덕에, 이 글의 목적인 dual core 를 활용할 수 있게 된 것이다.
처음엔 ESP32 는 Arduino + WiFi 정도로 생각하고 덤볐는데, 완전 deep 하게 들어온 듯 하다.
상하 관계를 정리하면, FreeRTOS + Components > ESP-IDF > Arduino Core > Arduino IDE 되겠다.
AWS 에서도 MQTT 를 위해 FreeRTOS 서비스를 지원하고 있지만, 아래 그림에서 보면 살짤 달라 보인다.
ESP32 에서는 ESP-IDF 하위 layer 에 FreeRTOS 가 위치하지만, AWS FreeRTOS 는 ESP-IDF 상위에 자리하고 있다.
4. SMP
ESP32 에서 dual core 는 CPU 0 - Protocol CPU (PRO_CPU) 와 CPU 1 - Application CPU (APP_CPU)두 가지로 정의되어 사용된다.
보통, PRO_CPU 는 백그라운드 작업이나 WiFi 인터페이싱 쪽을, APP_CPU 는 어플리케이션 쪽에 우선 순위를 둬서 동작하게 된다.
그러다 보니, APP_CPU 는 항상 바쁘고, PRO_CPU 는 많은 시간 놀게 되는, 좋지 않은 효율을 보일 수 있다.
Arduino IDE 에서 코딩하게 되면, setup() / loop() 는 CPU 1 에서 실행되고, 무선관련 동작은 CPU 0 에서 실행된다고 한다.
효율이 좋지 않다고 하더라도, 엄연한 dual core architecture 이며, memory 와 cache 및 외부기기 접근을 공유한다.
Core 이름에서도 알 수 있듯, 통신에 관한 background 처리는 CPU 0 에서 처리하고, 어플리케이션은 CPU 1 에서 하려고 하기 때문에, 효율 좋게 동작할 수 있도록 아래와 같이 CPU core 지정 명령어를 구비해 놓았습니다.
|------------------------------------------------------------------| | instruction | meaning | |------------------------------------------------------------------| | xTaskCreate() | for single core tasks | | xTaskCreatePinnedToCore() | for assigning tasks to specific core | | xTaskCreateUniversal() | for making cores used evenly | |------------------------------------------------------------------|
Single core 에는 "xTaskCreate()" 를 사용하고, 특정 core 에 할당하고 싶을 때에는 "xTaskCreatePinnedToCore()" 을 사용합니다.
Core 수를 확인하여, core 갯수에 알맞게 task 를 할당하는 것이 "xTaskCreateUniversal()" 입니다.
우리는 core 를 편중되지 않고, 조금 강제적으로 균등하게 사용하기 위해 "xTaskCreatePinnedToCore()" 을 사용할 껍니다.
참고로, 함수의 첫번째 부분에 표시되는 "x..." 는 FreeRTOS 네이티브 명령어를 지정하는 것이라 하네요.
5. Source 기본 구조
void task1(void *pvParameters) { while (1) { ... ... delay(10); } } void task2(void *pvParameters) { while (1) { ... ... delay(10); } }
다만, "while(1)" 구문 안에 작업 루틴이 들어가고, 필히 "delay(10)" 가 필요합니다.
Delay 는 10 microseconds 미만이면, 균등하게 core 끼리 일을 나눠 갖지 못합니다. 꼭 10 microseconds 이상으로 설정해야 합니다.
portTickType delay_10 = 10 / portTICK_RATE_MS; ... vTaskDelay(delay_10); ...
void setup() { Serial.begin(115200); delay(50); // wait for initialization xTaskCreatePinnedToCore( task1, "task1", 8192, NULL, 1, NULL, PRO_CPU_NUM ); xTaskCreatePinnedToCore( task2, "task2", 8192, NULL, 0, NULL, APP_CPU_NUM ); } void loop() { }
위에서 언급 했던, core 할당 방법 - xTaskCreate() / xTaskCreatePinnedToCore() / xTaskCreateUniversal() - 과, 어느 CPU - PRO_CPU_NUM / APP_CPU_NUM - 를 사용할 것인지를 정의해 줍니다.
위의 예시는, Core 0 / 1 에 강제적으로 task 를 할당한 예 입니다.
자세한 설명은 아래 FreeRTOS 문서를 참고해 보세요.
* FreeRTOS
- https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/freertos.html
6. Single Core 활용
Core 한개만을 이용하여, 2승을 계산하는 간단한 코드를 만들어 봤습니다.
portTickType delay_10 = 10 / portTICK_RATE_MS; int countX = 0; void task1(void *pvParameters) { while (1) { for ( int i = 0 ; i < 1000 ; i++ ) { pow(2, i); } countX++; vTaskDelay(delay_10); } } void setup() { Serial.begin(115200); delay(50); // wait for initialization xTaskCreatePinnedToCore( task1, "task1", 8192, NULL, 1, NULL, PRO_CPU_NUM ); } void loop() { Serial.println(countX); delay(100); }
2의 1000 승이 완료되면 countX 에 1을 증가하는 방식으로, 일정한 시간 동안 Single 과 Dual 의 계산량 차이를 보기 위한 것입니다.
int countX = 0; ... for ( int i = 0 ; i < 1000 ; i++ ) { pow(2, i); } countX++; ... void loop() { Serial.println(countX); delay(100); }
setup() 에서 xTaskCreatePinnedToCore 를 이용하여 task1 하나만 정의하여 돌려 봤습니다.
... xTaskCreatePinnedToCore( task1, "task1", 8192, NULL, 1, NULL, PRO_CPU_NUM ); ...
위에서 CPU core 지정 부분을 PRO_CPU_NUM 과 APP_CPU_NUM 을 바꿔 해봤지만, 동일한 결과를 보여 줬습니다.
약 50 초 동안, 2의 1000 승을 1600 회 정도 계산했습니다.
7. Dual Core 활용
xTaskCreatePinnedToCore 및 PRO_CPU_NUM / APP_CPU_NUM 를 모두 사용하는 코드 입니다.
portTickType delay_10 = 10 / portTICK_RATE_MS; int countX = 0; void task1(void *pvParameters) { while (1) { for ( int i = 0 ; i < 1000 ; i++ ) { pow(2, i); } countX++; vTaskDelay(delay_10); } } void task2(void *pvParameters) { while (1) { for ( int i = 0 ; i < 1000 ; i++ ) { pow(2, i); } countX++; vTaskDelay(delay_10); } } void setup() { Serial.begin(115200); delay(50); // wait for initialization xTaskCreatePinnedToCore( task1, "task1", 8192, NULL, 1, NULL, PRO_CPU_NUM ); xTaskCreatePinnedToCore( task2, "task2", 8192, NULL, 1, NULL, APP_CPU_NUM ); } void loop() { Serial.println(countX); delay(100); }
약 3200 개 정도를 계산해 냈네요. Single core 사용할 때보다, 딱 2배의 숫자 입니다. 이로써, dual core 가 사용되었다는 것을 알 수 있습니다.
Single core 의 계산 처리값과 한 그래프에서 비교하고 싶어서 index 를 추가하고 EXCEL 에서 그래프를 그려 보았습니다.
int x = 0; ... void loop() { x++; Serial.print(x); Serial.print("\t"); Serial.println(countX); delay(100); }
생각 외로 처리량이 꽤 좋습니다. Arduino Nano 와는 넘사벽인 성능입니다.
그래프에서 선이 겹쳐 보여 잘 보이지 않지만, PRO_CPU_NUM 이 조금 낮게 나올 것이라고 예상 했지만, WiFi 등을 사용하고 있지 않기 때문에, APP_CPU_NUM 과 동일하게 나왔습니다.
Dual core 를 활용하면서, 정확하게 2배의 계산 처리량을 보여 줬습니다.
FIN
CPU 도 Dual core 에다가 내장 센서, 그리고 Arduino IDE 에서 코딩할 수 있다는 점은 엄청나게 큰 장점일 듯 합니다.
거의 왠만한 프로젝트는 ESP32 하나로 커버 가능할 것 같습니다. EPS32 에 대해 다른 기능들도 차차 알아 보겠습니다.
참고 사이트
- https://www.hackster.io/rayburne/esp32-in-love-with-both-cores-8dd948
- https://www.mgo-tec.com/blog-entry-ledc-pwm-arduino-esp32.html/2
- https://qiita.com/makotaka/items/dd035f4b2db94f87b63c
- https://lang-ship.com/blog/work/esp32-freertos-l03-multitask/
- https://techtutorialsx.com/2017/05/16/esp32-dual-core-execution-speedup/
'Hardware' 카테고리의 다른 글
Hardware | EPS32 PWM 기능 확인해 보기 (0) | 2020.04.11 |
---|---|
Hardware | ESP32 의 internal sensor 확인해 보기 (2) | 2020.04.09 |
Hardware | ESP32 스펙 확인해 보기 (0) | 2020.03.23 |
Hardware | ESP32 간단 사용기 (0) | 2020.03.21 |
Hardware | CO2 센서인 MH-Z14A 를 활용해 보자 (2) | 2020.03.18 |