From 8a077fbe40a4cf76330ff2f62a94d470eb87e13d Mon Sep 17 00:00:00 2001 From: Anton Viktorov Date: Wed, 10 Jul 2024 07:20:39 +0200 Subject: [PATCH] upd --- esphome/components/ac_dimmer/ac_dimmer.cpp | 16 +- .../components/ac_dimmer/esp32-hw-timer.cpp | 110 ---------- esphome/components/ac_dimmer/esp32-hw-timer.h | 24 --- .../components/ac_dimmer/hw_timer_esp_idf.cpp | 202 ++++++++++++++++++ .../components/ac_dimmer/hw_timer_esp_idf.h | 28 +++ 5 files changed, 234 insertions(+), 146 deletions(-) delete mode 100644 esphome/components/ac_dimmer/esp32-hw-timer.cpp delete mode 100644 esphome/components/ac_dimmer/esp32-hw-timer.h create mode 100644 esphome/components/ac_dimmer/hw_timer_esp_idf.cpp create mode 100644 esphome/components/ac_dimmer/hw_timer_esp_idf.h diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index 43e95a7fa0..9f979e5e11 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -13,7 +13,7 @@ #endif #ifdef USE_ESP32_FRAMEWORK_ESP_IDF -#include "esp32-hw-timer.h" +#include "hw_timer_esp_idf.h" #endif namespace esphome { @@ -195,7 +195,7 @@ void AcDimmer::setup() { // PWM and AcDimmer can even run at the same time this way setTimer1Callback(&timer_interrupt); #endif -#ifdef USE_ESP32_FRAMEWORK_ARDUINO +#ifdef USE_ESP32 // 80 Divider -> 1 count=1µs dimmer_timer = timerBegin(0, 80, true); timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true); @@ -205,17 +205,8 @@ void AcDimmer::setup() { timerAlarmWrite(dimmer_timer, 50, true); timerAlarmEnable(dimmer_timer); #endif -#ifdef USE_ESP32_FRAMEWORK_ESP_IDF - // 80 Divider -> 1 count=1µs - dimmer_timer = EspIdfTimer::timerBegin(0, 80, true); - EspIdfTimer::timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true); - // 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. - EspIdfTimer::timerAlarmWrite(dimmer_timer, 50, true); - EspIdfTimer::timerAlarmEnable(dimmer_timer); -#endif } + void AcDimmer::write_state(float state) { state = std::acos(1 - (2 * state)) / 3.14159; // RMS power compensation auto new_value = static_cast(roundf(state * 65535)); @@ -223,6 +214,7 @@ void AcDimmer::write_state(float state) { this->store_.init_cycle = this->init_with_half_cycle_; this->store_.value = new_value; } + void AcDimmer::dump_config() { ESP_LOGCONFIG(TAG, "AcDimmer:"); LOG_PIN(" Output Pin: ", this->gate_pin_); diff --git a/esphome/components/ac_dimmer/esp32-hw-timer.cpp b/esphome/components/ac_dimmer/esp32-hw-timer.cpp deleted file mode 100644 index 4e54b803b9..0000000000 --- a/esphome/components/ac_dimmer/esp32-hw-timer.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "esp32-hw-timer.h" -// #include "soc/soc_caps.h" -// #include "esp_clk.h" -// #include "esp32/clk.h" - -#include "esphome/core/log.h" - -static const char *const TAG = "esp32-hal-timer"; - -namespace esphome { -namespace ac_dimmer { - -typedef union { - struct { - uint32_t reserved0 : 10; - uint32_t alarm_en : 1; /*When set alarm is enabled*/ - uint32_t level_int_en : 1; /*When set level type interrupt will be generated during alarm*/ - uint32_t edge_int_en : 1; /*When set edge type interrupt will be generated during alarm*/ - uint32_t divider : 16; /*Timer clock (T0/1_clk) pre-scale value.*/ - uint32_t autoreload : 1; /*When set timer 0/1 auto-reload at alarming is enabled*/ - uint32_t increase : 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter - decrement.*/ - uint32_t enable : 1; /*When set timer 0/1 time-base counter is enabled*/ - }; - uint32_t val; -} timer_cfg_t; - -#define NUM_OF_TIMERS SOC_TIMER_GROUP_TOTAL_TIMERS - -// Works for all chips -static hw_timer_t timer_dev[4] = { - {TIMER_GROUP_0, TIMER_0}, {TIMER_GROUP_1, TIMER_0}, {TIMER_GROUP_0, TIMER_1}, {TIMER_GROUP_1, TIMER_1}}; - -// NOTE: (in IDF 5.0 there wont be need to know groups/numbers -// timer_init() will list thru all timers and return free timer handle) - -// uint32_t getApbFrequency() { -// // rtc_clk_apb_freq_get(); -// return esp_clk_apb_freq(); -// } - -// static void _on_apb_change(void *arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb) { -// hw_timer_t *timer = (hw_timer_t *) arg; -// if (ev_type == APB_BEFORE_CHANGE) { -// timerStop(timer); -// } else { -// old_apb /= 1000000; -// new_apb /= 1000000; -// uint16_t divider = (new_apb * timerGetDivider(timer)) / old_apb; -// timerSetDivider(timer, divider); -// timerStart(timer); -// } -// } - -hw_timer_t *EspIdfTimer::timerBegin(uint8_t num, uint16_t divider, bool countUp) { - if (num >= NUM_OF_TIMERS) { - ESP_LOGE(TAG, "Timer number %u exceeds available number of Timers.", num); - return NULL; - } - - hw_timer_t *timer = &timer_dev[num]; // Get Timer group/num from 0-3 number - - timer_config_t config = { - .alarm_en = TIMER_ALARM_DIS, - .counter_en = TIMER_PAUSE, - .intr_type = TIMER_INTR_LEVEL, - .counter_dir = static_cast(countUp), - .auto_reload = timer_autoreload_t::TIMER_AUTORELOAD_DIS, - .divider = divider, - }; - - timer_init(timer->group, timer->num, &config); - timer_set_counter_value(timer->group, timer->num, 0); - EspIdfTimer::timerStart(timer); - // addApbChangeCallback(timer, _on_apb_change); - return timer; -} - -bool IRAM_ATTR timerFnWrapper(void *arg) { - void (*fn)(void) = (void (*)()) arg; - fn(); - - // some additional logic or handling may be required here to approriately yield or not - return false; -} - -void EspIdfTimer::timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge) { - if (edge) { - ESP_LOGW(TAG, "EDGE timer interrupt is not supported! Setting to LEVEL..."); - } - timer_isr_callback_add(timer->group, timer->num, (bool (*)(void *)) timerFnWrapper, (void *) fn, 0); -} - -void EspIdfTimer::timerStart(hw_timer_t *timer) { timer_start(timer->group, timer->num); } - -void EspIdfTimer::timerAlarmEnable(hw_timer_t *timer) { timer_set_alarm(timer->group, timer->num, TIMER_ALARM_EN); } - -void EspIdfTimer::timerAlarmDisable(hw_timer_t *timer) { timer_set_alarm(timer->group, timer->num, TIMER_ALARM_DIS); } - -void EspIdfTimer::timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload) { - timer_set_alarm_value(timer->group, timer->num, alarm_value); - timerSetAutoReload(timer, autoreload); -} - -void EspIdfTimer::timerSetAutoReload(hw_timer_t *timer, bool autoreload) { - timer_set_auto_reload(timer->group, timer->num, (timer_autoreload_t) autoreload); -} - -} // namespace ac_dimmer -} // namespace esphome \ No newline at end of file diff --git a/esphome/components/ac_dimmer/esp32-hw-timer.h b/esphome/components/ac_dimmer/esp32-hw-timer.h deleted file mode 100644 index 88270a7682..0000000000 --- a/esphome/components/ac_dimmer/esp32-hw-timer.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "driver/timer.h" -namespace esphome { -namespace ac_dimmer { - -typedef struct hw_timer_s { - timer_group_t group; - timer_idx_t num; -} hw_timer_t; - -class EspIdfTimer { - public: - static hw_timer_t *timerBegin(uint8_t timer, uint16_t divider, bool countUp); - static void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge); - - static void timerAlarmEnable(hw_timer_t *timer); - static void timerAlarmDisable(hw_timer_t *timer); - static void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload); - static void timerSetAutoReload(hw_timer_t *timer, bool autoreload); - - static void timerStart(hw_timer_t *timer); -}; -} // namespace ac_dimmer -} // namespace esphome \ No newline at end of file diff --git a/esphome/components/ac_dimmer/hw_timer_esp_idf.cpp b/esphome/components/ac_dimmer/hw_timer_esp_idf.cpp new file mode 100644 index 0000000000..495fb43fd6 --- /dev/null +++ b/esphome/components/ac_dimmer/hw_timer_esp_idf.cpp @@ -0,0 +1,202 @@ +#ifdef USE_ESP32_FRAMEWORK_ESP_IDF + +#include "hw_timer_esp_idf.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp32/clk.h" + +#include "esphome/core/log.h" + +static const char *const TAG = "hw_timer_esp_idf"; + +namespace esphome { +namespace ac_dimmer { + +#define NUM_OF_TIMERS SOC_TIMER_GROUP_TOTAL_TIMERS + +typedef union { + struct { + uint32_t reserved0 : 10; + uint32_t alarm_en : 1; /*When set alarm is enabled*/ + uint32_t level_int_en : 1; /*When set level type interrupt will be generated during alarm*/ + uint32_t edge_int_en : 1; /*When set edge type interrupt will be generated during alarm*/ + uint32_t divider : 16; /*Timer clock (T0/1_clk) pre-scale value.*/ + uint32_t autoreload : 1; /*When set timer 0/1 auto-reload at alarming is enabled*/ + uint32_t increase : 1; /*When set timer 0/1 time-base counter increment. When cleared timer 0 time-base counter + decrement.*/ + uint32_t enable : 1; /*When set timer 0/1 time-base counter is enabled*/ + }; + uint32_t val; +} timer_cfg_t; + +// Works for all chips +static hw_timer_t timer_dev[4] = { + {TIMER_GROUP_0, TIMER_0}, {TIMER_GROUP_1, TIMER_0}, {TIMER_GROUP_0, TIMER_1}, {TIMER_GROUP_1, TIMER_1}}; +// NOTE: (in IDF 5.0 there wont be need to know groups/numbers +// timer_init() will list thru all timers and return free timer handle) + +typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE } apb_change_ev_t; +typedef void (*apb_change_cb_t)(void *arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb); +typedef struct apb_change_cb_s { + struct apb_change_cb_s *prev; + struct apb_change_cb_s *next; + void *arg; + apb_change_cb_t cb; +} apb_change_t; + +static apb_change_t *apb_change_callbacks = NULL; +static xSemaphoreHandle apb_change_lock = NULL; + +// Hz +uint32_t getApbFrequency() { return esp_clk_apb_freq(); } + +static void _on_apb_change(void *arg, apb_change_ev_t ev_type, uint32_t old_apb, uint32_t new_apb) { + hw_timer_t *timer = (hw_timer_t *) arg; + if (ev_type == APB_BEFORE_CHANGE) { + timerStop(timer); + } else { + old_apb /= 1000000; + new_apb /= 1000000; + uint16_t divider = (new_apb * timerGetDivider(timer)) / old_apb; + timerSetDivider(timer, divider); + timerStart(timer); + } +} + +static void initApbChangeCallback() { + static volatile bool initialized = false; + if (!initialized) { + initialized = true; + apb_change_lock = xSemaphoreCreateMutex(); + if (!apb_change_lock) { + initialized = false; + } + } +}; + +static bool addApbChangeCallback(void *arg, apb_change_cb_t cb) { + initApbChangeCallback(); + apb_change_t *c = (apb_change_t *) malloc(sizeof(apb_change_t)); + if (!c) { + ESP_LOGE(TAG, "Callback Object Malloc Failed"); + return false; + } + c->next = NULL; + c->prev = NULL; + c->arg = arg; + c->cb = cb; + xSemaphoreTake(apb_change_lock, portMAX_DELAY); + if (apb_change_callbacks == NULL) { + apb_change_callbacks = c; + } else { + apb_change_t *r = apb_change_callbacks; + // look for duplicate callbacks + while ((r != NULL) && !((r->cb == cb) && (r->arg == arg))) + r = r->next; + if (r) { + ESP_LOGE(TAG, "duplicate func=%8p arg=%8p", c->cb, c->arg); + free(c); + xSemaphoreGive(apb_change_lock); + return false; + } else { + c->next = apb_change_callbacks; + apb_change_callbacks->prev = c; + apb_change_callbacks = c; + } + } + xSemaphoreGive(apb_change_lock); + return true; +} + +hw_timer_t *timerBegin(uint8_t num, uint16_t divider, bool countUp) { + if (num >= NUM_OF_TIMERS) { + ESP_LOGE(TAG, "Timer number %u exceeds available number of Timers.", num); + return NULL; + } + + hw_timer_t *timer = &timer_dev[num]; // Get Timer group/num from 0-3 number + + timer_config_t config = { + .alarm_en = TIMER_ALARM_DIS, + .counter_en = TIMER_PAUSE, + .intr_type = TIMER_INTR_LEVEL, + .counter_dir = static_cast(countUp), + .auto_reload = timer_autoreload_t::TIMER_AUTORELOAD_DIS, + .divider = divider, + }; + + timer_init(timer->group, timer->num, &config); + timer_set_counter_value(timer->group, timer->num, 0); + timerStart(timer); + addApbChangeCallback(timer, _on_apb_change); + return timer; +} + +bool IRAM_ATTR timerFnWrapper(void *arg) { + void (*fn)(void) = (void (*)()) arg; + fn(); + + // some additional logic or handling may be required here to approriately yield or not + return false; +} + +void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge) { + if (edge) { + ESP_LOGW(TAG, "EDGE timer interrupt is not supported! Setting to LEVEL..."); + } + timer_isr_callback_add(timer->group, timer->num, (bool (*)(void *)) timerFnWrapper, (void *) fn, 0); +} + +void timerStart(hw_timer_t *timer) { timer_start(timer->group, timer->num); } + +void timerStop(hw_timer_t *timer) { timer_pause(timer->group, timer->num); } + +void timerAlarmEnable(hw_timer_t *timer) { timer_set_alarm(timer->group, timer->num, TIMER_ALARM_EN); } + +void timerAlarmDisable(hw_timer_t *timer) { timer_set_alarm(timer->group, timer->num, TIMER_ALARM_DIS); } + +void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload) { + timer_set_alarm_value(timer->group, timer->num, alarm_value); + timerSetAutoReload(timer, autoreload); +} + +void timerSetAutoReload(hw_timer_t *timer, bool autoreload) { + timer_set_auto_reload(timer->group, timer->num, (timer_autoreload_t) autoreload); +} + +uint16_t timerGetDivider(hw_timer_t *timer) { + timer_cfg_t config; + config.val = timerGetConfig(timer); + return config.divider; +} + +void timerSetDivider(hw_timer_t *timer, uint16_t divider) { + if (divider < 2) { + ESP_LOGE(TAG, "Timer divider must be set in range of 2 to 65535"); + return; + } + timer_set_divider(timer->group, timer->num, divider); +} + +uint32_t timerGetConfig(hw_timer_t *timer) { + timer_config_t timer_cfg; + timer_get_config(timer->group, timer->num, &timer_cfg); + + // Translate to default uint32_t + timer_cfg_t cfg; + cfg.alarm_en = timer_cfg.alarm_en; + cfg.autoreload = timer_cfg.auto_reload; + cfg.divider = timer_cfg.divider; + cfg.edge_int_en = timer_cfg.intr_type; + cfg.level_int_en = !timer_cfg.intr_type; + cfg.enable = timer_cfg.counter_en; + cfg.increase = timer_cfg.counter_dir; + + return cfg.val; +} + +} // namespace ac_dimmer +} // namespace esphome + +#endif \ No newline at end of file diff --git a/esphome/components/ac_dimmer/hw_timer_esp_idf.h b/esphome/components/ac_dimmer/hw_timer_esp_idf.h new file mode 100644 index 0000000000..f48afdb23b --- /dev/null +++ b/esphome/components/ac_dimmer/hw_timer_esp_idf.h @@ -0,0 +1,28 @@ +#pragma once +#ifdef USE_ESP32_FRAMEWORK_ESP_IDF +#include "driver/timer.h" +namespace esphome { +namespace ac_dimmer { + +typedef struct hw_timer_s { + timer_group_t group; + timer_idx_t num; +} hw_timer_t; + +hw_timer_t *timerBegin(uint8_t timer, uint16_t divider, bool countUp); +void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge); +void timerAlarmEnable(hw_timer_t *timer); +void timerAlarmDisable(hw_timer_t *timer); +void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload); +void timerSetAutoReload(hw_timer_t *timer, bool autoreload); +void timerStart(hw_timer_t *timer); +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); + +} // namespace ac_dimmer +} // namespace esphome + +#endif \ No newline at end of file