diff --git a/esphome/components/esp32/core.cpp b/esphome/components/esp32/core.cpp index eaa8bf2d17d..8ce3458ea9c 100644 --- a/esphome/components/esp32/core.cpp +++ b/esphome/components/esp32/core.cpp @@ -23,19 +23,13 @@ extern "C" __attribute__((weak)) void initArduino() {} namespace esphome { void HOT yield() { vPortYield(); } -uint32_t IRAM_ATTR HOT millis() { - // ESPHome sets CONFIG_FREERTOS_HZ=1000 (see esp32/__init__.py), so one tick = one millisecond - // and xTaskGetTickCount() returns ms directly. This is a single volatile memory read (~20ns) - // vs esp_timer_get_time() + micros_to_millis() which does a hardware timer read + 64-bit - // multiply-shift conversion (~686ns measured). millis() is called 1+N times per main loop - // iteration (once at top + once per component for warn_blocking), so this saves ~3.4μs/loop - // on a 5-component device. micros() still uses esp_timer_get_time() for μs precision. -#if CONFIG_FREERTOS_HZ == 1000 - return xTaskGetTickCount(); -#else - return micros_to_millis(static_cast(esp_timer_get_time())); +// millis() is inlined in hal.h when CONFIG_FREERTOS_HZ == 1000 (just xTaskGetTickCount()). +// Fallback for non-standard tick rates. No IRAM_ATTR — the original IRAM placement was +// inherited from ESP8266 where Arduino's millis() is called from ISR handlers (Wiegand, +// ZyAura). ESPHome doesn't call millis() from ISR on ESP32. +#if CONFIG_FREERTOS_HZ != 1000 +uint32_t HOT millis() { return micros_to_millis(static_cast(esp_timer_get_time())); } #endif -} uint64_t HOT millis_64() { return micros_to_millis(static_cast(esp_timer_get_time())); } void HOT delay(uint32_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } uint32_t IRAM_ATTR HOT micros() { return (uint32_t) esp_timer_get_time(); } diff --git a/esphome/core/hal.h b/esphome/core/hal.h index 03a30b7459f..3d615fd395f 100644 --- a/esphome/core/hal.h +++ b/esphome/core/hal.h @@ -28,10 +28,23 @@ #endif +// On ESP32 with 1 kHz FreeRTOS tick rate, millis() is just xTaskGetTickCount() — +// a single volatile read of a DRAM global. Inlining it here eliminates the +// function call entirely at every call site. No IRAM needed (no flash code +// executed), no 64-bit math, no HAL call. +#if defined(USE_ESP32) && CONFIG_FREERTOS_HZ == 1000 +#include +#include +#endif + namespace esphome { void yield(); +#if defined(USE_ESP32) && CONFIG_FREERTOS_HZ == 1000 +inline uint32_t millis() { return xTaskGetTickCount(); } +#else uint32_t millis(); +#endif uint64_t millis_64(); uint32_t micros(); void delay(uint32_t ms);