2020년 12월 2일 수요일

ESP32로 알아보는 FreeRTOS

이번 시간에는 Wi-Fi & Bluetooth 기능을 저가로 공급하여 유명해진 Espressif 사의 ESP32 보드를 사용하여, RTOS OS 중에서 현재 가장 많은 사용자 층을 확보하고 있는 FreeRTOS를 소개해 보고자 한다.




목차
1. ESP32와 FreeRTOS
2. ESP-IDF SDK 소개
3. FreeRTOS Programming
4. 아직 못다한 이야기
5. References


IT에 입문한지 어느새 25년째다. AI, Big Data, Cloud, IoT, Robot, Drone, Autonomous Vehicle, Blockchain, 3D printing ... 예전에도 분명히 그랬을 텐데, 최근 몇년 동안의 IT 시장의 눈부신 변화가 유독 더 크게 다가오는 것은 것은 왜일까 ?  변화를 따라잡지 못하면 도태되고 말 것이라는 불안감이 엄습해 온다. 이럴 때는 어찌해야 할까 ? 답은 간단하다. 자신이 그동안 열심히 해 오던 분야를 더 깊이 파고 또 파는 것이다. 아무리 새로운 기술이 등장하고, 그 변화의 속도를 도져히 따라 잡을 수 없을 것 같아도, 이러한 기술들이 과연 어디에서 나왔겠는가 ? 내가 오늘 하는 작은 일을 결코 가볍게 여기지 말자~  😎

1. ESP32와 FreeRTOS
이번 장에서는 ESP32와 FreeRTOS에 관하여 간략히 살펴 보고자 한다.

a) ESP32 overview
ESP32는 ESP8266으로 유명한 Espressif 사에서 만든 Wi-Fi & Bluetooth용 저가의 chip(Wi-Fi/BLE SoC)이다. 그런데 저가의 chip이라고 해서 우습게(?) 볼 일이 아닌 것이, ESP32 자체는 Xtensa LX6(Dual-core 32bit) RISC CPU(160Mhz or 240Mhz)를 기반으로 하고 있는 엄연한 32bit microprocessor이다.

[그림 1.1] Espressif ESP32 SoC [출처 - https://www.espressif.com/]

[그림 1.2] ESP32 SoC 블럭도

[그림 1.3] ESP8266 vs ESP32 스펙 비교 [출처 - 참고문헌 7]

📌 이정도 spec이라면 RAM 크기만 좀 더 보강된다면 linux도 충분히 돌릴 수 있을 것 같다.

따라서 여기에서는 ESP32를 WiFi or Bluetooth용 device로 생각해서 Arduino의 주변 장치로 활용하기 보다는, ESP32를 CPU로 하여 단독으로 동작하는 방식을 활용하고자 한다. 아래 그림은 ESP32를 사용하여 만들어진 보드 중 하나인 ESP32 DeviKit이다. 실제로 (이와 유사한) ESP32를 기반으로 하는 다양한 업체의 다양한 종류의 보드가 저가로 판매되고 있다.

[그림 1.4] ESP32를 사용한 board(device kit) 예

ESP8266 및 ESP32는 Arduino or Raspberry Pi 만큼이나 워낙 유명하기 때문에 여기에서 부연 설명을 할 필요가 없어 보인다. 따라서 다음 장에서는 곧 바로 ESP-IDF 환경 설정으로 넘어가도록 하겠다.

b) FreeRTOS overview
FreeRTOS는 Richard Barry가 만들어 현재까지 가장 많은 사용자 층을 확보하고 있는 open source RTOS로, 아래 그림에서 보는 바와 같이 embedded linux에 이어 사용률 2위(2019년 기준)에 오를 정도로 그 기세가 대단하다(필자도 사실 이정도까지 FreeRTOS가 널리 사용되고 있는 줄은 몰랐다 😅).

[그림 1.5] 2019년 기준 향후 1년간 사용할 것으로 예상되는 Embedded OS 순위 [출처 - 참고문헌 4]

📌 이 자료는 미국(58%), EMEA(21%), APAC(21%)의 엔지니어들을 대상으로 조사한 결과이다. 조사 대상 중 EMEA(유럽 외)나 아시아권의 비중이 낮은 것이 좀 아쉽다.

사실 FreeRTOS 자체는 ZephyrmbedOS와는 달리 kernel core 자체만을 제공하고 있기 때문에, 나머지 부분(device driver, network stack, 주변 library 등)은 칩 제조사에서 자신들의 환경에 맞게 작업한 후, SDK 형태로 배포하는 방식을 따른다(예: Espressif ESP-IDF).

이런 상황에서 Amazon은 최근에 FreeRTOS를 인수하여 (kernel 이외의 기능을 보강하여) Amazon FreeRTOS 형태로 배포하고 있다. 왜 Cloud 업체인 Amazon에서 뜬금없이 FreeRTOS를 인수했을까 ? 답은 간단하다. 이는 IoT 시장의 폭발은 Cloud 시장의 그것과 직결되기 때문이다. 쉽게 말해 IoT 기기를 효과적으로 사용/관리하기 위해서는 Cloud(서버)와의 연결이 필수적인데, 현재 가장 많은 사용자 층을 확보한 FreeRTOS를 AWS와 잘 연동하도록 만들어(Amazon FreeRTOS를 사용할 수 밖에 없도록 만들어) 재 배포하는 것은 그만큼 AW$ cloud 시장을 확장하는 효과를 가져올 것이 자명하기 때문이다. 이는 M$에서 발빠르게 Azure cloud에 맞는 Azure RTOS를 밀고 있는 것과도 맥을 같이한다고 보아야 할 것이다. 근데, Google은 뭐하나 ? 😂

[그림 1.6] Amazon FreeRTOS [출처 - 참고문헌 5]

<앞으로 눈여겨 보아야 할 RTOS>
1. Amazon's AWS FreeRTOS  : 목적은 AWS Cloud 😓
2. Microsoft's Azure RTOS : 목적은 Azure Cloud  😓
3. ARM's mbedOS : 목적은 ARM Cortex-M series chips  😒
4. Linux foundation's Zephyr Project : Open source project 😃
5. Apache's NuttX : Open source project 😃

앞으로 누가 RTOS 시장의 패권을 거머쥘지는 예측하기 어려우나, 위의 조사 결과로 보아 당분간 FreeRTOS의 아성을 무너뜨리기는 쉽지 않아 보인다. 그래도 개인적으로는 Zephyr의 비상을 기대해 본다 ...



2. ESP-IDF SDK 소개
이번 장에서는 Espressif 사에서 만든 ESP-IDF 개발 환경(or SDK)을 소개해 보고자 한다. 

a) ESP-IDF 개요
ESP-IDF는 마치 Amazon FreeRTOS  처럼, FreeRTOS kernel과 주변 기능(device driver, network stack, security 기능, ...)을 하나로 통합하여 ESP series 보드에 맞게 최적화 시킨 RTOS(or SDK)이다.

[그림 2.1] Espressif ESP-IDF 아키텍쳐 [출처 - https://www.espressif.com/]

📌 Espressif에서는 FreeRTOS를 그대로 사용하지 않고 자신들의 CPU에 맞게 최적화(예: dual core CPU 상황을 고려하여 API 기능 확장)하여 배포하고 있다.

Espressif사는 ESP-IDF 외에도 아래와 같이 몇가지 흥미로운 framework & library 개발 환경을 제공하고 있다.

[그림 2.2] Espressif frameworks and libraries [출처 - https://www.espressif.com/en/products/sdks/esp-idf]

b) Target board
이번 posting에서 사용할 target board는 ESP32 기반 보드 중 주변에서 쉽게 구할 수 있는 NodeMCU-32S이다.

 
[그림 2.3] NodeMCU-32S
📌 (국내에서) 배송비 포함하여 11,800원에 구매하였다.

c) ESP-IDF 환경 설정하기
자, 그럼 이제 부터 본격적으로 ESP-IDF 환경 설정에 들어가 보도록 하자. 언제나 그렇듯 Ubuntu 환경(18.04 desktop)을 기준으로 하자. ESP-IDF는 당연히 Windows, macOS에서도 환경 설정이 가능하다. 하지만 필자는 언제나 그렇듯 Ubuntu에서 작업한다. 😎


<install전 사전 준비 작업>
$ sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 10
  => ESP-IDF에 포함된 python은 version 3를 기준으로 동작한다. 따라서 python3을 default로 변경하도록 하자.
update-alternatives: using /usr/bin/python3 to provide /usr/bin/python (python) in auto mode
$ python
Python 3.6.9 (default, Oct  8 2020, 12:12:24) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit
>>> exit()

$ sudo apt-get install git wget flex bison gperf python python-pip python-setuptools cmake ninja-build ccache libffi-dev libssl-dev
  => 추가로 필요한 ubuntu package를 설치하자.
$ sudo usermod -a -G dialout $YOURID
  => super user 권한 없이 flash(firmware writing) 명령 실행을 위해 반드시 필요하다. 즉, /dev/ttyUSB* 파일에 접근할 수 있도록 설정해 주도록 한다.

<ESP-IDF 설치>
$ mkdir esp32; cd esp32
$ git clone -b v4.1 --recursive https://github.com/espressif/esp-idf.git
  => git으로 부터 source code를 내려 받는다. 양이 좀 많아 오래 걸린다.
$ ls -la
합계 12
drwxr-xr-x 3 chyi chyi 4096 11월 20 11:03 .
drwxr-xr-x 3 chyi chyi 4096 11월 20 11:03 ..
drwxr-xr-x 9 chyi chyi 4096 11월 20 11:05 esp-idf
$ cd esp-idf/
$ ls -la
합계 176
drwxr-xr-x  9 chyi chyi  4096 11월 20 11:05 .
drwxr-xr-x  3 chyi chyi  4096 11월 20 11:03 ..
-rw-r--r--  1 chyi chyi   682 11월 20 11:05 .editorconfig
-rw-r--r--  1 chyi chyi  8916 11월 20 11:05 .flake8
drwxr-xr-x  9 chyi chyi  4096 11월 20 11:05 .git
drwxr-xr-x  4 chyi chyi  4096 11월 20 11:05 .github
-rw-r--r--  1 chyi chyi  1197 11월 20 11:05 .gitignore
-rw-r--r--  1 chyi chyi  4870 11월 20 11:05 .gitlab-ci.yml
-rw-r--r--  1 chyi chyi  2389 11월 20 11:05 .gitmodules
-rw-r--r--  1 chyi chyi   538 11월 20 11:05 .readthedocs.yml
-rw-r--r--  1 chyi chyi  4537 11월 20 11:05 CMakeLists.txt
-rw-r--r--  1 chyi chyi  2406 11월 20 11:05 CONTRIBUTING.rst
-rw-r--r--  1 chyi chyi 15087 11월 20 11:05 Kconfig
-rw-r--r--  1 chyi chyi 11358 11월 20 11:05 LICENSE
-rw-r--r--  1 chyi chyi  5780 11월 20 11:05 README.md
-rw-r--r--  1 chyi chyi  5669 11월 20 11:05 README_CN.md
-rw-r--r--  1 chyi chyi  2821 11월 20 11:05 SUPPORT_POLICY.md
-rw-r--r--  1 chyi chyi  2516 11월 20 11:05 SUPPORT_POLICY_CN.md
-rw-r--r--  1 chyi chyi   723 11월 20 11:05 add_path.sh
drwxr-xr-x 71 chyi chyi  4096 11월 20 11:05 components
drwxr-xr-x  5 chyi chyi  4096 11월 20 11:05 docs
drwxr-xr-x 15 chyi chyi  4096 11월 20 11:05 examples
-rw-r--r--  1 chyi chyi  2059 11월 20 11:05 export.bat
-rw-r--r--  1 chyi chyi  2099 11월 20 11:05 export.ps1
-rw-r--r--  1 chyi chyi  4816 11월 20 11:05 export.sh
-rw-r--r--  1 chyi chyi   529 11월 20 11:05 install.bat
-rw-r--r--  1 chyi chyi   657 11월 20 11:05 install.ps1
-rwxr-xr-x  1 chyi chyi   357 11월 20 11:05 install.sh
drwxr-xr-x  2 chyi chyi  4096 11월 20 11:05 make
-rw-r--r--  1 chyi chyi   971 11월 20 11:05 requirements.txt
-rw-r--r--  1 chyi chyi  1958 11월 20 11:05 sdkconfig.rename
drwxr-xr-x 21 chyi chyi  4096 11월 20 11:05 tools

<toolchain 설치>
$ ./install.sh
  => Xtensa LX6 RISC CPU에 맞는 toolchain을 자동으로 설치한다.
...
Successfully built future
Installing collected packages: click, pyserial, future, ipaddress, six, pycparser, cffi, enum34, cryptography, pyparsing, pyelftools
Successfully installed cffi-1.14.3 click-7.1.2 cryptography-3.2.1 enum34-1.1.10 future-0.18.2 ipaddress-1.0.23 pycparser-2.20 pyelftools-0.27 pyparsing-2.3.1 pyserial-3.4 six-1.15.0
All done! You can now run:

  . ./export.sh

$ .   ./export.sh
  => 환경 설정(file path, toolchain path 지정 등)을 역시 자동으로 해 준다.
📌 시스템 재부팅 시마다 해 주어야 하므로 ~/.bashrc에 넣어주면 편리할 수 있다.

d) hello world 예제 돌려 보기
환경 설정이 마무리 되었으니 hello world 예제를 돌려 보도록 하자. 그전에 아래 그림과 같이 USB cable을 이용하여 target board와 PC를 연결해 주도록 한다.

[그림 2.4] Target board에 firmware image 올리기 [출처 - 참고문헌 1]
📌 위 그림은 예제 program을 하나 build하여 target board에 설치하는 과정을 보여준다.

ESP-IDF의 example은 일반적으로 아래와 같은 형태로 구성되어 있다.

[그림 2.5] example project 구성 [출처 - 참고문헌 1]

📌 ESP-IDF의 build system은 이전 posting에서 소개한 zephyr project의 그것과 유사하게 CMake와 Ninja/Make로 구성되어 있다. 또한 ESP-IDF는 idf.py로 build하고, zephyr는 west(python script)로 build하는 것도 유사하다.
📌 CMake, Ninja/Make로 build system을 구성하는 것이 최근 trend인 듯하다 - Zephyr, AWS FreeRTOS, Azure RTOS 등이 모두 그렇게 되어있다.

$ cd examples/get-started/hello_world
  => hello_world 디렉토리로 이동한다.

[그림 2.6] hello_world_main.c 파일 내용

📌 ESP-IDF application의 main 함수는 app_main( )이다.

$ idf.py menuconfig
  => compile(build)을 위한 환경 설정을 한다.


[그림 2.7] idf.py menuconfig 명령 실행 모습

📌 ESP-IDF는 linux kernel 처럼 Kconfig를 이용하여 환경 설정을 한다.

menuconfig로 변경된 내용은 현재 directory의 sdskconfig 파일에 저장된다. 이는 linux kernel의 .config와 동일한 방식이라고 볼 수 있다.

[그림 2.8] sdkconfig 파일 내용 확인

$ idf.py build
  => compile을 시도한다. 결과는 build 디렉토리에 생긴다.

[그림 2.9] build 결과물 확인

$ idf.py -p /dev/ttyUSB1 flash
  => build 디렉토리에 생성된 hello-world.bin 파일을 target board에 writing하도록 한다.
  => -p 다음에는 실제 Ubuntu에서 인식된 USB2serial device file 명을 입력해준다.

[그림 2.10] target board에 firmware image write하기

📌 위 명령 실행 시 아래와 같은 메시지를 뿌리며 flash fail이 날 수 있는데, 이는 'sudo usermode -a -G dialout $YOURID' 내용이 반영되지 않았기 때문으로 이를 해결하려면 logout or reboot 후 다시 시도해 주면 된다.
...
serial.serialutil.SerialException: [Errno 13] could not open port /dev/ttyUSB1: [Errno 13] Permission denied: '/dev/ttyUSB1'
...

idf.py -p /dev/ttyUSB1 monitor
  => Serial console을 통해 target board에 올라간 firmware의 동작을 확인한다.
📌 이 명령 대신 minicom(115200, 8N1)등을 이용해도 된다.

[그림 2.11] target board에 올라간 hello world program 동작 확인하기

...
...
W (297) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (307) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
Hello world!
This is ESP32 chip with 2 CPU cores, WiFi/BT/BLE, silicon revision 1, 2MB external flash
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...
Restarting in 7 seconds...
Restarting in 6 seconds...
Restarting in 5 seconds...
Restarting in 4 seconds...
Restarting in 3 seconds...
Restarting in 2 seconds...
Restarting in 1 seconds...
Restarting in 0 seconds...
Restarting now.
ets Jun  8 2016 00:22:57

아래 내용은 debugging(w/ GDB)시, OpenOCD와 JTAG을 이용하고, flash writing & monitoring을 위해서는 esptool.py(idf.py에서 호출)와 USB2UART 장치를 이용할 수 있음을 하나의 그림으로 표현한 것이다.

[그림 2.12] JTAG debugging 및 flash writing & monitoring [출처 - 참고문헌 1]

e) application startup flow
ESP32 SoC는 일반적인 32bit microprocessor와 마찬가지로 아래와 같은 (전형적인) 부팅 방식을 따른다.

bootloader(ROM) -> application(SPI flash)

📌 참고로 ESP32는 secure boot(flash encryption)도 지원한다(편의상 여기에서는 소개하지 않음).

Application image는 flash에 배치되는데, 아래 두개의 그림은 4MB SPI flash의 파티션 table의 구성 예를 보여준다.

먼저 아래 그림은 가장 기본적인 예(table layout)로 1개의 application(= factory app)이 0x10000(64KB) offset에 배치된 모습을 나타낸다.

[그림 2.13] ESP32 flash 파티션 테이블 예 - factory app 영역 할당 [출처 - 참고문헌 1]

한편, 다음 그림은 factory app 이외에도 2개의 OTA app 영역이 할당된 모습을 보여준다. 이때 어떤 녀석으로 부팅할지는 otadata 파티션 정보를 참조하여 bootloader가 결정하게 된다.

[그림 2.14] ESP32 flash 파티션 테이블 예 -  1개의 factory app 및 2개의 OTA 영역 할당 [출처 - 참고문헌 1]

📌 OTA(Over The Air)가 의미하는 것은 network(예: wifi)으로 firmware image를 download 받아 flash에 설치한다는 뜻이다. 

다음으로 알아볼 내용은 system reset 부터 application main 함수 - app_main( ) - 까지의 코드 흐름에 관한 것이다. 특별한 설명 보다는 하나의 그림으로 이를 대신하고자 한다.

[그림 2.15] ESP32의 booting flow - app_main( ) 함수 호출 과정
___________________________________________________________________

이상으로 ESP32용 SDK인 ESP-IDF를 설치하고, 간단한 예제 program 하나를 target board에서 돌려 보았다. 하나의 시스템을 제대로 이해하기 위해서는 파악해야 할 내용이 참으로 많다. (여기서는 수박 겉핥기 식으로 간략히 살펴 보았으니) 보다 자세한 사항은 아래 page 내용을 참조하기 바란다.



3. FreeRTOS Programming
이번 장에서는 ESP32 target board 상에서 몇가지 FreeRTOS 예제 program을 돌려 보고, 이를 분석해 보도록 하겠다. 

당초 계획은 아래 문서에서 설명하는 예제를 중심으로 FreeRTOS의 주요 개념(task, queue, timer, interrupt, event group 등)을 하나씩 상세히 소개하는 것이었으나, 시간 & 지면 관계상 ESP-IDF 예제 중 freertos API를 다양하게 사용하는 것을 골라 분석해 보는 것으로 이를 대신하고자 한다. 😂

FreeRTOS를 제대로 이해하기 위해서는 우선적으로 아래 문서를 정독할 필요가 있다.


1장에서 언급한 바와 같이 FreeRTOS는 kernel core만으로 구성되어 있다. Code도 아주 간결하여 아래에 보이는 C(header file 포함) 파일이 전부이다.

[그림 3.1] FreeRTOS source codes

📌 FreeRTOS는 지난 15년간 (마치 street fighter 처럼) field에서 직접 부딪치며 튼튼하게 발전한 OS이다.

a) FreeRTOS 주요 개념
RTOS에서 task의 개념은 매우 중요하다. RTOS application은 task의 묶음이라고 해도 과언이 아닌데, 각각의 task는 자신만의 context 내에서 실행된다. 특정 시점에 동작하는 task는 오직 한개 뿐이며, task scheduler가 각각의 task가 언제 실행될 지를 결정하게 된다. Task는 또한 자신만의 stack을 할당 받게 되는데, 만일 task swap(교체)이 발생하여 다른 task가 실행된다면, 현재 task의 실행 context 정보는 자신의 stack에 저장되게 된다. 그래야 이 정보를 토대로 나중에 다시 실행될 때 이전 상태를 유지할 수 있는 것이다. 

<Task and Scheduling>
FreeRTOS task는 두가지 상태 즉, Running state와 Not Running state(= Blocked state)를 갖게 되며, 상황에 따라 아래 [그림 3.2]와 같이 상태 전환(천이) 과정을 겪게 된다.

[그림 3.2] freertos task states [출처 - 참고문헌2]


[그림 3.3] freertos task state machine [출처 - 참고문헌2]

📌 task state & transition 부분은 Linux kernel의 그것과 크게 다르지 않다.

FreeRTOS는 동일한 우선순위를 갖는 task가 일정한 시간 간격(time slice)를 두고 CPU를 사용할 수 있도록 하기 위해 tick interrupt(h/w timer를 이용하여 구현)라는 개념을 사용한다. Task scheduler는 tick interrupt가 발생할 때마다 next task가 누가될 지를 결정하게 된다. 아래 그림은 우선 순위가 동일한 2개의 task(Task1, Task2) 중 Task1이 실행되던 상태에서 tick interrupt가 발생했고, Kernel(task scheduler)이 다음번 task로 Task2를 선택한 모습을 보여준다.

[그림 3.4] freertos task management 1 - scheduler의 역할 [출처 - 참고문헌2]

한편 아래 그림은 idle task의 개념을 설명하기 위한 것으로, 2개의 Task2, Task1이 차례로 vTaskDelay( ) 함수(일정시간 지연시 호출함)를 호출하여 각각 Blocked state로 전환될 경우, CPU를 사용하는 task가 하나도 남지 않아 program이 종료될 수 있으므로, 이때 idle task를 사용하여 이 문제를 해결할 수 있게 된다. Idle task는 CPU에 부담을 주지 않는 task를 말한다. 

[그림 3.5] freertos task management 2 - idle task의 역할 [출처 - 참고문헌2]

(FreeRTOSConfig.h 파일 설정에 따라 달라질 수 있지만) FreeRTOS는 기본적으로 preemptive scheduling을 사용한다. 즉, 우선 순위가 높은 task가 그렇지 못한 task로 부터 CPU를 가로챌 수 있다는 뜻이다. FreeRTOS는 real-time behavior를 위해 각각의 task에 부여한 우선순위를 엄격하게 다룬다.

[그림 3.6] freertos task management 3 - task 우선 순위 및 preemption [출처 - 참고문헌2]

📌 역시 정도의 차이는 있으나 task scheduling 부분도 (개념으로 볼 때) Linux kernel의 그것과 별반 다르지 않다. 물론 Real-time이라는 부분에서는 엄격한 차이를 보이긴 하지만 말이다. 예를 들어 linux의 경우 work queue로 던져진 task의 경우 언제 실행될 지 예측할 수가 없다. FreeRTOS에서는 이러한 방식을 절대로 용납하지 않는다.

<Queue>
FreeRTOS는 둘 이상의 task 간 or interrupt와 task 간의 message 전달 및 동기화 등의 목적으로 Queue(List) 개념을 사용한다. 
Queue API에는 block time을 지정할 수 있는 parameter가 있는데, task가 비어 있는 queue에서 message를 읽으려 할 때, task는 읽을 수 있는 message가 새로 추가되거나, block time이 경과하게 될 때까지 Blocked state로 들어가게 된다. 이와 유사하게 queue가 꽉 차 있는 상태에서 task가 queue에 message가 쓰려고 할 경우에도 queue에 쓸 공간이 생기거나, block time이 경과할 때까지 해당 task는 Blocked state로 들어가게 된다.

[그림 3.7] freertos queue management [출처 - 참고문헌2]

아래 그림은 Queue 1개를 만든 상태에서 이를 사용하는 sender task가 여러 개(예: 3개)이고, receiver task가 1개인 예를 보여준다. 또한 Queue를 통해 전달하는 내용이 구조체 형태임도 알 수  있다.

[그림 3.8] freertos queue management  - multiple sender, 1 receiver [출처 - 참고문헌2]

<Timer>
Timer는 정해진 시간(period)이 경과하면 원하는 action(function - callback function)을 수행하도록 하는 s/w 기능이다. FreeRTOS는 한차례 정해진 action만 실행하는 one-shot timer와 일정한 주기마다 자동으로 action을 반복 수행하는 auto-reload timer 두가지를 제공한다.

[그림 3.9] freertos s/w timer management  1 - one-shot, auto-reload timer [출처 - 참고문헌2]

[그림 3.10] freertos s/w timer management  2 - auto-reload timer의 상태도 [출처 - 참고문헌2]

그렇다면 Timer의 action(function)은 누가 실행해 주는 것일까 ? 아래 그림에서 보는 것 처럼 RTOS Daemon task(scheduler 시작 시 자동으로 생성되는 task)라는 녀석이 있어서 timer function을 실행해 주게 된다. 아래 그림과 같이 Daemon task는 timer function을 실행하거나 timer를 중지하거나 등의 요청을 Timer Command Queue를 통해 다른 task(예: main routine)로 부터 전달받는 형태를 취한다.

[그림 3.11] freertos s/w timer management  3 - daemon task의  역할 [출처 - 참고문헌2]

📌 timer도 역시 linux의 그것과 별반 다르지 않다.

<Interrupt and synchronization>
마지막으로 아래 그림은 ISR(Interrupt Service Routine)과 지연처리 task(deferring interrupt processing task)의 개념을 보여준다. ISR 내에서는 수행시간이 오래 걸리거나 Blocked state로 전환되는 API(함수)를 호출해서는 안된다. 따라서 이 때에는 시간이 많이 걸리는 부분을 별도의 task로 분리하여 실행(deferring task)해 줌으로써 ISR을 간소화시켜 주게 된다. 또한 별도로 분리된 task의 우선 순위를 여타 task에 비해 높게 설정해 줌으로써, interrupt service routine이 연속적으로 실행되는 효과를 가져오게 만들어 줄 수 있다. 

[그림 3.12] freertos s/w interrupt service routine & deferring interrupt processing [출처 - 참고문헌2]

📌 위의 개념은 Linux의 interrupt(top half)와 bottom half(tasklet, workqueue, thread) 개념과 동일한 내용이라고 볼 수 있다.
📌 ISR내에서는 기존의 API 대신 "FromISR"로 끝나는 API만을 사용할 수 있다.

FreeRTOS는 mutual exclusion(상호배제) 및 synchronization(동기화)을 위해 binary semaphore, couting semaphore 및 mutex 기능을 제공한다. Binary semaphore는 2가지 값만을 가지고 있어서 task와 task 간 or task와 interrupt 간의  동기화를 맞추고자 할 때 쓰면 편리하다. Counting semaphore는 2개 이상의 값을 가지게 되므로, 2개 이상의 task간의 동기화를 맞추고자 할 때 사용할 수  있다. 마지막으로 mutex는 우선 순위 상속 메카니즘이 있는 일종의 binary semaphore라고 말할 수 있다. 참고로 위의 그림(3.12)에는 Semaphore를 사용하여 ISR(semaphore give)과 지연처리 task인 Task2(semaphore take)를 동기화 시켜 주는 내용이 포함되어 있다.

이상으로 FreeRTOS의 개념과 관련하여 아주 기본적은 사항만을 요약해 보았다. 거의 370여 page에 달하는 FreeRTOS 관련 내용을 11개의 그림으로 응축해 놓았기 때문에, 당연히 추가로 확인해 보아야 할 내용(Heap management, Event group, Task notification 등)이 많이 있을 것으로 보인다. 부족한 설명은 독자 여러분의 몫으로 남겨두도록 한다. 😋

b) FreeRTOS 예제 소개
앞서 FreeRTOS의 기본 동작 원리를 살펴 보았으니, 이제 부터는 간단한 예제를 하나 분석해 보도록 하자.

esp-idf/examples/system/freertos/real_time_stats

$ cd esp-idf/examples/system/freertos/real_time_stats
$ idf.py menuconfig
$ idf.py build
$ idf.py -p /dev/ttyUSB1 flash
$ idf.py -p /dev/ttyUSB1 monitor

[그림 3.13] real_time_stats application 실행 모습

--------------------------------------------------- ............................ (A)
Getting real time stats over 100 ticks
| Task | Run Time | Percentage
| stats | 945 | 0%                                     # stats task
| spin1 | 225416 | 11%                          # spin task 1
| IDLE1 | 390994 | 19%                         # idle task 1(for cpu1)
| IDLE0 | 288281 | 14%                         # idle task 1(for cpu0)
| spin3 | 225379 | 11%                          # spin task 3
| spin0 | 225412 | 11%                          # spin task 0
| spin2 | 217254 | 10%                          # spin task 2
| spin5 | 200956 | 10%                          # spin task 5
| spin4 | 225362 | 11%                          # spin task 4
| Tmr Svc | 0 | 0%                                    # daemon task(for timer)
| ipc1 | 0 | 0%                                          # queue(for cpu1) ???
| ipc0 | 0 | 0%                                          # queue(for cpu0) ???
| esp_timer | 0 | 0%                                # esp timer
Real time stats obtained
---------------------------------------------------

예상했겠지만, 코드 자체의 분량은 많지가 않다.

chyi@mars:~/workspace/Boards/ESP/esp32/esp-idf/examples/system/freertos/real_time_stats/main$ ls -la
합계 24
drwxr-xr-x 2 chyi chyi 4096 12월  1 20:57 .
drwxr-xr-x 4 chyi chyi 4096 11월 25 11:44 ..
-rw-r--r-- 1 chyi chyi   98 11월 20 11:05 CMakeLists.txt
-rw-r--r-- 1 chyi chyi  146 11월 20 11:05 component.mk
-rw-r--r-- 1 chyi chyi 6514 11월 20 11:05 real_time_stats_example_main.c

real_time_stats_example_main.c 파일은 아래와 같이 4개의 함수로 이루어진 간단한 program에 불과하다. 이 program이 하는 일은 주기적으로 위의 결과 (A) console에 출력(마치 Linux의 ps 명령 처럼)하는 것이 전부이다.
___________________________________________________________________________________

아래 함수는 stats_task( ) 함수에서 pdMS_TO_TICKS(1000)(= 1 초) 시간마다 한번씩 호출되는 함수로, 이곳에서 위의 결과 (A)를 출력한다.


아래 두개의 함수는 app_main( )에서 생성한 두개의 task 함수이다.
먼저 spin_task는 sync_spin_task semaphore가 해제되기를 기다린 후, 해제될 경우 while loop을 돌며 pdMS_TO_TICKS(100) 간격으로 CPU를 점유(점유 후, 단순히 No operation 수행)하도록 하는 단순한 코드이다.

반면에 stats_task는 역시 sync_stats_task semphore가 해제되기를 기다린 후, 해제 시 앞서 spin_tasks가 기다리는 sync_spin_task semaphore를 해제시켜 준다. 이후, while loop을 돌면서 pdMS_TO_TICKS(1000) 간격으로 print_real_time_stats( ) 함수를 호출해 준다.


아래 코드가 main routine이다. main routine이 하는 일은 간단 명료하다.

1) 1개의 count semaphore sync_spin_task를 생성한다. 얘는 6개의 spin_task에서 사용하게 된다.
2) 1개의 binary semaphore sync_stats_task를 생성한다.
3) 6개의 spin_task를 생성한다. 여기에서는 xTaskCreate( ) 함수 대신 espressif에서 만든 xTaskCreatePinnedToCore( ) 함수를 사용한다.
4) 1개의 stats_task를 생성한다. 이때도 역시 xTaskCreate( ) 함수 대신 espressif에서 만든 xTaskCreatePinnedToCore( ) 함수를 사용한다.
5) 마지막으로, xSemaphoreGive(sync_stats_task)를 호출하여 sync_stats_task semaphore를 해제(give)한다.


📌 xTaskCreatePinnedToCore( ) 함수와 관련해서는 아래 page의 내용을 살펴 보기 바란다.
___________________________________________________________________________________

지금까지 FreeRTOS의 동작 원리를 살펴 보고, FreeRTOS 예제를 하나 선택하여 그 코드를 분석해 보았다(Queue API 사용 부분이 빠져 있어서 좀 아쉽다).


4. 아직 못다한 이야기
ESP32는 Wi-Fi와 BLE를 위한 SoC이다. 따라서 ESP-IDF에는 이와 관련한 많은 예제가 포함되어 있다. 앞으로 시간을 두고 좀 더 살펴 보아야 하겠다. 😜

a) Wi-Fi programming

b) BLE programming

e) Secure Boot

f) Flash encryption


To be continued...


5. References
[1] https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html
[2] Mastering the FreeRTOS Real Time Kernel - A Hands On Tutorial Guide, Richard Barry
[3] The FreeRTOS Reference Manual, Richard Barry
[4] 2019 Embedded Markets Study - Integrating IoT and Advanced Technology Designs,
Application Development & Processing Environments, March 2019, AspenCore
[5] https://aws.amazon.com/ko/blogs/korea/announcing-amazon-freertos/
[6] 사물인터넷을 품은 라즈베리파이 개정판, 김성우, Jpub
[7] https://www.cnx-software.com/2016/03/25/esp8266-and-esp32-differences-in-one-single-table/
[8] And, Google~


Slowboot