arduino-esp32
ESP32のArduinoは、ESP-IDFで実装されたものであり、そのESP-IDFもまた、FreeRTOSを利用して実装されたものだである。
setup()とloop()
main関数はarduino-esp32/blob/master/cores/esp32/main.cppで定義される。
void loopTask(void *pvParameters)
{
#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
// sets UART0 (default console) RX/TX pins as already configured in boot or as defined in variants/pins_arduino.h
Serial0.setPins(gpioNumberToDigitalPin(SOC_RX0), gpioNumberToDigitalPin(SOC_TX0));
#endif
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
printBeforeSetupInfo();
#else
if(shouldPrintChipDebugReport()){
printBeforeSetupInfo();
}
#endif
setup();
#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
printAfterSetupInfo();
#else
if(shouldPrintChipDebugReport()){
printAfterSetupInfo();
}
#endif
for(;;) {
#if CONFIG_FREERTOS_UNICORE
yieldIfNecessary();
#endif
if(loopTaskWDTEnabled){
esp_task_wdt_reset();
}
loop();
if (serialEventRun) serialEventRun();
}
}
extern "C" void app_main()
{
#if ARDUINO_USB_CDC_ON_BOOT && !ARDUINO_USB_MODE
Serial.begin();
#endif
#if ARDUINO_USB_MSC_ON_BOOT && !ARDUINO_USB_MODE
MSC_Update.begin();
#endif
#if ARDUINO_USB_DFU_ON_BOOT && !ARDUINO_USB_MODE
USB.enableDFU();
#endif
#if ARDUINO_USB_ON_BOOT && !ARDUINO_USB_MODE
USB.begin();
#endif
loopTaskWDTEnabled = false;
initArduino();
xTaskCreateUniversal(loopTask, "loopTask", getArduinoLoopTaskStackSize(), NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE);
}ESP-IDFではmain関数をapp_main()と呼ぶ。
ここでやってることはオリジナルArduinoと大体同じ。
オリジナルArduinoと違うのは、FreeRTOSの機能で実質main関数loopTask()のタスクを作っている。xTaskCreateUniversal()の最後の引数はコアの指定。
Arduino IDEの実行コア選択メニューもここのマクロを弄っているのだろう。
Xtensa系のESP32大体コア二つ付いてるんだけど、Arduinoだと基本的に一つしか使わない感じ。
ちなみにオリジナルArduinoのmain関数は
int main(void)
{
init();
initVariant();
#if defined(USBCON)
USBDevice.attach();
#endif
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
return 0;
}うん。
ESP32のデュアルコアの扱いについても、基本的にFreeRTOSのタスク関連のAPIでやる。
シリアル
本家Arduinoのシリアルライブラリーに加えて、arduino-esp32のシリアルにはコールバック的な機能がある。
void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout)
{
HSERIAL_MUTEX_LOCK();
// function may be NULL to cancel onReceive() from its respective task
_onReceiveCB = function;
// setting the callback to NULL will just disable it
if (_onReceiveCB != NULL) {
// When Rx timeout is Zero (disabled), there is only one possible option that is callback when FIFO reaches 120 bytes
_onReceiveTimeout = _rxTimeout > 0 ? onlyOnTimeout : false;
// in case that onReceive() shall work only with RX Timeout, FIFO shall be high
// this is a work around for an IDF issue with events and low FIFO Full value (< 3)
if (_onReceiveTimeout) {
uartSetRxFIFOFull(_uart, 120);
log_w("OnReceive is set to Timeout only, thus FIFO Full is now 120 bytes.");
}
// this method can be called after Serial.begin(), therefore it shall create the event task
if (_uart != NULL && _eventTask == NULL) {
_createEventTask(this); // Create event task
}
}
HSERIAL_MUTEX_UNLOCK();
}他にはonReceiveError()もある。
#define HSERIAL_MUTEX_LOCK() do {} while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS)
#define HSERIAL_MUTEX_UNLOCK() xSemaphoreGive(_lock)これらもFreeRTOSのタスクで実装している。
void HardwareSerial::_createEventTask(void *args)
{
// Creating UART event Task
xTaskCreateUniversal(_uartEventTask, "uart_event_task", ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE, this, ARDUINO_SERIAL_EVENT_TASK_PRIORITY, &_eventTask, ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE);
if (_eventTask == NULL) {
log_e(" -- UART%d Event Task not Created!", _uart_nr);
}
}Wireについて、オリジナルArduinoもonReceive()があって、それはAVRのISRで実装しているらしいけど、どうやって動いているのかはわからない。
組込み開発わからん。
なんか適当に書いちゃったけど内容が薄ぺらいわりに間違いもあるかもしれん。
申し訳ない。