diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index 9f979e5e11..fb4a0a4ec2 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -13,6 +13,7 @@ #endif #ifdef USE_ESP32_FRAMEWORK_ESP_IDF +#include "esp_idf_version.h" #include "hw_timer_esp_idf.h" #endif @@ -195,7 +196,8 @@ void AcDimmer::setup() { // PWM and AcDimmer can even run at the same time this way setTimer1Callback(&timer_interrupt); #endif -#ifdef USE_ESP32 + +#if USE_ESP32 && (ESP_IDF_VERSION_MAJOR == 4) // 80 Divider -> 1 count=1µs dimmer_timer = timerBegin(0, 80, true); timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true); @@ -205,6 +207,16 @@ void AcDimmer::setup() { timerAlarmWrite(dimmer_timer, 50, true); timerAlarmEnable(dimmer_timer); #endif +#if USE_ESP32 && (ESP_IDF_VERSION_MAJOR == 5) + // 1 MHz -> 1 count=1µs + dimmer_timer = timerBegin(1000000); + timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr); + // For ESP32, we can't use dynamic interval calculation because the timerX functions + // are not callable from ISR (placed in flash storage). + // Here we just use an interrupt firing every 50 µs. + timerAlarm(dimmer_timer, 50, true, 0); + timerStart(dimmer_timer); +#endif } void AcDimmer::write_state(float state) { diff --git a/esphome/components/ac_dimmer/hw_timer_esp_idf.cpp b/esphome/components/ac_dimmer/hw_timer_esp_idf.cpp index 495fb43fd6..113f8bc72d 100644 --- a/esphome/components/ac_dimmer/hw_timer_esp_idf.cpp +++ b/esphome/components/ac_dimmer/hw_timer_esp_idf.cpp @@ -4,7 +4,18 @@ #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" + +#if (ESP_IDF_VERSION_MAJOR == 4) #include "esp32/clk.h" +#endif +#if (ESP_IDF_VERSION_MAJOR == 5) +#include "driver/gptimer.h" +#if defined __has_include && __has_include("clk_tree.h") +#include "clk_tree.h" +#else +#include "esp_clk_tree.h" +#endif +#endif #include "esphome/core/log.h" @@ -13,6 +24,8 @@ static const char *const TAG = "hw_timer_esp_idf"; namespace esphome { namespace ac_dimmer { +#if (ESP_IDF_VERSION_MAJOR == 4) + #define NUM_OF_TIMERS SOC_TIMER_GROUP_TOTAL_TIMERS typedef union { @@ -195,8 +208,125 @@ uint32_t timerGetConfig(hw_timer_t *timer) { return cfg.val; } +#endif // (ESP_IDF_VERSION_MAJOR == 4) + +#if (ESP_IDF_VERSION_MAJOR == 5) + +interrupt_config_t *timer_intr_config = NULL; + +hw_timer_t *timerBegin(uint32_t frequency) { + esp_err_t err = ESP_OK; + uint32_t counter_src_hz = 0; + uint32_t divider = 0; + + // soc_periph_gptimer_clk_src_t clk; + int clk; + + // soc_periph_gptimer_clk_src_t gptimer_clks[] = SOC_GPTIMER_CLKS; + int gptimer_clks[] = SOC_GPTIMER_CLKS; + for (size_t i = 0; i < sizeof(gptimer_clks) / sizeof(gptimer_clks[0]); i++) { + clk = gptimer_clks[i]; +#if defined __has_include && __has_include("clk_tree.h") + clk_tree_src_get_freq_hz((soc_module_clk_t) clk, CLK_TREE_SRC_FREQ_PRECISION_CACHED, &counter_src_hz); +#else + esp_clk_tree_src_get_freq_hz((soc_module_clk_t) clk, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &counter_src_hz); +#endif + divider = counter_src_hz / frequency; + if ((divider >= 2) && (divider <= 65536)) { + break; + } else { + divider = 0; + } + } + + if (divider == 0) { + ESP_LOGE(TAG, "Resolution cannot be reached with any clock source, aborting!"); + return NULL; + } + + gptimer_config_t config = { + .clk_src = (gptimer_clock_source_t) clk, + .direction = GPTIMER_COUNT_UP, + .resolution_hz = frequency, + .intr_priority = 0, + .flags = {.intr_shared = true}, + }; + + hw_timer_t *timer = (hw_timer_t *) malloc(sizeof(hw_timer_t)); + + err = gptimer_new_timer(&config, &timer->timer_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to create a new GPTimer, error num=%d", err); + free(timer); + return NULL; + } + gptimer_enable(timer->timer_handle); + gptimer_start(timer->timer_handle); + timer->timer_started = true; + return timer; +} + +bool IRAM_ATTR timerFnWrapper(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *args) { + interrupt_config_t *isr = (interrupt_config_t *) args; + if (isr->fn) { + if (isr->arg) { + ((voidFuncPtrArg) isr->fn)(isr->arg); + } else { + isr->fn(); + } + } + // some additional logic or handling may be required here to appropriately yield or not + return false; +} + +void timerAttachInterruptFunctionalArg(hw_timer_t *timer, void (*userFunc)(void *), void *arg) { + esp_err_t err = ESP_OK; + gptimer_event_callbacks_t cbs = { + .on_alarm = timerFnWrapper, + }; + + timer->interrupt_handle.fn = (voidFuncPtr) userFunc; + timer->interrupt_handle.arg = arg; + + if (timer->timer_started == true) { + gptimer_stop(timer->timer_handle); + } + gptimer_disable(timer->timer_handle); + err = gptimer_register_event_callbacks(timer->timer_handle, &cbs, &timer->interrupt_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Timer Attach Interrupt failed, error num=%d", err); + } + gptimer_enable(timer->timer_handle); + if (timer->timer_started == true) { + gptimer_start(timer->timer_handle); + } +} + +void timerAttachInterrupt(hw_timer_t *timer, voidFuncPtr userFunc) { + timerAttachInterruptFunctionalArg(timer, (voidFuncPtrArg) userFunc, NULL); +} + +void timerAlarm(hw_timer_t *timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count) { + esp_err_t err = ESP_OK; + gptimer_alarm_config_t alarm_cfg = { + .alarm_count = alarm_value, + .reload_count = reload_count, + .flags = {.auto_reload_on_alarm = autoreload}, + }; + err = gptimer_set_alarm_action(timer->timer_handle, &alarm_cfg); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Timer Alarm Write failed, error num=%d", err); + } +} + +void timerStart(hw_timer_t *timer) { + gptimer_start(timer->timer_handle); + timer->timer_started = true; +} + +#endif // (ESP_IDF_VERSION_MAJOR == 5) } // namespace ac_dimmer } // namespace esphome -#endif \ No newline at end of file +#endif diff --git a/esphome/components/ac_dimmer/hw_timer_esp_idf.h b/esphome/components/ac_dimmer/hw_timer_esp_idf.h index f48afdb23b..7eceeb02c3 100644 --- a/esphome/components/ac_dimmer/hw_timer_esp_idf.h +++ b/esphome/components/ac_dimmer/hw_timer_esp_idf.h @@ -1,9 +1,20 @@ #pragma once #ifdef USE_ESP32_FRAMEWORK_ESP_IDF + +#include "esp_idf_version.h" + +#if (ESP_IDF_VERSION_MAJOR == 4) #include "driver/timer.h" +#endif + +#if (ESP_IDF_VERSION_MAJOR == 5) +#include "driver/gptimer_types.h" +#endif + namespace esphome { namespace ac_dimmer { +#if (ESP_IDF_VERSION_MAJOR == 4) typedef struct hw_timer_s { timer_group_t group; timer_idx_t num; @@ -21,8 +32,32 @@ void timerStop(hw_timer_t *timer); uint32_t timerGetConfig(hw_timer_t *timer); uint16_t timerGetDivider(hw_timer_t *timer); void timerSetDivider(hw_timer_t *timer, uint16_t divider); +#endif +#if (ESP_IDF_VERSION_MAJOR == 5) + +typedef void (*voidFuncPtr)(void); +typedef void (*voidFuncPtrArg)(void *); + +struct interrupt_config_t { + voidFuncPtr fn; + void *arg; +}; + +struct hw_timer_t { + gptimer_handle_t timer_handle; + interrupt_config_t interrupt_handle; + bool timer_started; +}; + +hw_timer_t *timerBegin(uint32_t frequency); + +void timerAttachInterrupt(hw_timer_t *timer, void (*userFunc)(void)); +void timerAlarm(hw_timer_t *timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count); +void timerStart(hw_timer_t *timer); + +#endif } // namespace ac_dimmer } // namespace esphome -#endif \ No newline at end of file +#endif