mirror of
https://github.com/esphome/esphome.git
synced 2026-09-16 17:48:40 +00:00
[esp32] Keep millis() out-of-line with IRAM_ATTR, add ISR context check
Revert the inline hal.h approach — millis() must remain IRAM_ATTR because Wiegand and ZyAura call it from IRAM_ATTR ISR handlers on all platforms including ESP32. Use xPortInIsrContext() to dispatch to xTaskGetTickCountFromISR() from ISR or xTaskGetTickCount() from task context, satisfying the FreeRTOS API contract. ISR check is [[unlikely]] so the hot path stays fast. Benchmarked: 686 ns -> 361 ns (1.9x faster). Correctness verified.
This commit is contained in:
@@ -23,13 +23,20 @@ extern "C" __attribute__((weak)) void initArduino() {}
|
||||
namespace esphome {
|
||||
|
||||
void HOT yield() { vPortYield(); }
|
||||
// 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<uint64_t>(esp_timer_get_time())); }
|
||||
// Use xTaskGetTickCount() when tick rate is 1 kHz (ESPHome's default via sdkconfig),
|
||||
// falling back to esp_timer for non-standard rates. IRAM_ATTR is required because
|
||||
// Wiegand and ZyAura call millis() from IRAM_ATTR ISR handlers on ESP32.
|
||||
// xTaskGetTickCountFromISR() is used in ISR context to satisfy the FreeRTOS API contract.
|
||||
uint32_t IRAM_ATTR HOT millis() {
|
||||
#if CONFIG_FREERTOS_HZ == 1000
|
||||
if (xPortInIsrContext()) [[unlikely]] {
|
||||
return xTaskGetTickCountFromISR();
|
||||
}
|
||||
return xTaskGetTickCount();
|
||||
#else
|
||||
return micros_to_millis(static_cast<uint64_t>(esp_timer_get_time()));
|
||||
#endif
|
||||
}
|
||||
uint64_t HOT millis_64() { return micros_to_millis<uint64_t>(static_cast<uint64_t>(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(); }
|
||||
|
||||
@@ -28,23 +28,10 @@
|
||||
|
||||
#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 <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#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);
|
||||
|
||||
Reference in New Issue
Block a user