5#include <MycilaDimmerThyristor.h>
8#include <freertos/FreeRTOS.h>
11#include <driver/gpio.h>
12#include <driver/gptimer_types.h>
13#include <esp32-hal-gpio.h>
14#include <hal/gpio_ll.h>
15#include <soc/gpio_struct.h>
18#include <esp32-hal-log.h>
21#include "priv/inlined_gptimer.h"
23#ifdef MYCILA_LOGGER_SUPPORT
24 #include <MycilaLogger.h>
25extern Mycila::Logger logger;
26 #define LOGD(tag, format, ...) logger.debug(tag, format, ##__VA_ARGS__)
27 #define LOGI(tag, format, ...) logger.info(tag, format, ##__VA_ARGS__)
28 #define LOGW(tag, format, ...) logger.warn(tag, format, ##__VA_ARGS__)
29 #define LOGE(tag, format, ...) logger.error(tag, format, ##__VA_ARGS__)
31 #define LOGD(tag, format, ...) ESP_LOGD(tag, format, ##__VA_ARGS__)
32 #define LOGI(tag, format, ...) ESP_LOGI(tag, format, ##__VA_ARGS__)
33 #define LOGW(tag, format, ...) ESP_LOGW(tag, format, ##__VA_ARGS__)
34 #define LOGE(tag, format, ...) ESP_LOGE(tag, format, ##__VA_ARGS__)
37#ifndef GPIO_IS_VALID_OUTPUT_GPIO
38 #define GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) ((gpio_num >= 0) && \
39 (((1ULL << (gpio_num)) & SOC_GPIO_VALID_OUTPUT_GPIO_MASK) != 0))
42#ifndef GPIO_IS_VALID_GPIO
43 #define GPIO_IS_VALID_GPIO(gpio_num) ((gpio_num >= 0) && \
44 (((1ULL << (gpio_num)) & SOC_GPIO_VALID_GPIO_MASK) != 0))
50#define PHASE_DELAY_MIN_US (90)
52#define TAG "ZC_DIMMER"
56 Mycila::ThyristorDimmer* dimmer =
nullptr;
57 EnabledDimmer* prev =
nullptr;
58 EnabledDimmer* next =
nullptr;
59 uint16_t alarm_count = UINT16_MAX;
63static gptimer_handle_t fire_timer =
nullptr;
65#define USE_DIMMER_LOCK 1
67static portMUX_TYPE dimmers_spinlock = portMUX_INITIALIZER_UNLOCKED;
74 if (!GPIO_IS_VALID_OUTPUT_GPIO(_pin)) {
75 LOGE(TAG,
"Disable ZC Dimmer: Invalid pin: %" PRId8, _pin);
79 LOGI(TAG,
"Enable Thyristor Dimmer on pin %" PRId8, _pin);
81 pinMode(_pin, OUTPUT);
82 digitalWrite(_pin, LOW);
83 _registerDimmer(
this);
95 LOGI(TAG,
"Disable ZC Dimmer on pin %" PRId8, _pin);
97 _unregisterDimmer(
this);
98 digitalWrite(_pin, LOW);
103 gptimer_alarm_config_t fire_timer_alarm_cfg = {.alarm_count = UINT16_MAX, .reload_count = 0, .flags = {.auto_reload_on_alarm =
false}};
106 if (inlined_gptimer_set_raw_count(fire_timer, 0) != ESP_OK) {
113 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
118 while (current !=
nullptr) {
120 if (current->dimmer->_delay)
121 gpio_ll_set_level(&GPIO, current->dimmer->_pin, LOW);
126 current->alarm_count = current->dimmer->_delay < PHASE_DELAY_MIN_US ? PHASE_DELAY_MIN_US : current->dimmer->_delay;
128 if (current->alarm_count < fire_timer_alarm_cfg.alarm_count)
129 fire_timer_alarm_cfg.alarm_count = current->alarm_count;
130 current = current->next;
135 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
139 uint64_t fire_timer_count_value;
140 if (inlined_gptimer_get_raw_count(fire_timer, &fire_timer_count_value) != ESP_OK) {
146 if (fire_timer_count_value >= delayUntilZero) {
147 fire_timer_count_value -= delayUntilZero;
150 if (fire_timer_count_value <= fire_timer_alarm_cfg.alarm_count) {
152 if (inlined_gptimer_set_raw_count(fire_timer, fire_timer_count_value) == ESP_OK) {
153 _fireTimerISR(fire_timer,
nullptr,
nullptr);
161 if (inlined_gptimer_set_raw_count(fire_timer, -
static_cast<uint64_t
>(delayUntilZero) + fire_timer_count_value) == ESP_OK) {
163 inlined_gptimer_set_alarm_action(fire_timer, &fire_timer_alarm_cfg);
169bool ARDUINO_ISR_ATTR Mycila::ThyristorDimmer::_fireTimerISR(gptimer_handle_t timer,
const gptimer_alarm_event_data_t* event,
void* arg) {
171 gptimer_alarm_config_t fire_timer_alarm_cfg = {.alarm_count = UINT16_MAX, .reload_count = 0, .flags = {.auto_reload_on_alarm =
false}};
174 uint64_t fire_timer_count_value;
175 if (inlined_gptimer_get_raw_count(fire_timer, &fire_timer_count_value) != ESP_OK) {
181 fire_timer_alarm_cfg.alarm_count = UINT16_MAX;
185 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
189 struct EnabledDimmer* current = dimmers;
190 while (current !=
nullptr) {
191 if (current->alarm_count != UINT16_MAX) {
193 if (current->alarm_count <= fire_timer_count_value) {
195 gpio_ll_set_level(&GPIO, current->dimmer->_pin, HIGH);
197 current->alarm_count = UINT16_MAX;
200 if (current->alarm_count < fire_timer_alarm_cfg.alarm_count)
201 fire_timer_alarm_cfg.alarm_count = current->alarm_count;
204 current = current->next;
209 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
213 inlined_gptimer_get_raw_count(fire_timer, &fire_timer_count_value);
214 }
while (fire_timer_alarm_cfg.alarm_count != UINT16_MAX && fire_timer_alarm_cfg.alarm_count <= fire_timer_count_value);
217 if (fire_timer_alarm_cfg.alarm_count != UINT16_MAX)
218 inlined_gptimer_set_alarm_action(fire_timer, &fire_timer_alarm_cfg);
224void Mycila::ThyristorDimmer::_registerDimmer(Mycila::ThyristorDimmer* dimmer) {
225 if (dimmers ==
nullptr) {
226 LOGI(TAG,
"Starting dimmer firing ISR");
228 gptimer_config_t timer_config;
229 timer_config.clk_src = GPTIMER_CLK_SRC_DEFAULT;
230 timer_config.direction = GPTIMER_COUNT_UP;
231 timer_config.resolution_hz = 1000000;
232 timer_config.flags.intr_shared =
true;
233 timer_config.intr_priority = 0;
234#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
235 timer_config.flags.backup_before_sleep =
false;
237#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
238 timer_config.flags.allow_pd =
false;
241 ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &fire_timer));
242 gptimer_event_callbacks_t callbacks_config;
243 callbacks_config.on_alarm = _fireTimerISR;
244 ESP_ERROR_CHECK(gptimer_register_event_callbacks(fire_timer, &callbacks_config,
nullptr));
245 ESP_ERROR_CHECK(gptimer_enable(fire_timer));
246 ESP_ERROR_CHECK(gptimer_start(fire_timer));
249 LOGD(TAG,
"Register new dimmer %p on pin %d", dimmer, dimmer->
getPin());
252 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
255 if (dimmers ==
nullptr) {
256 dimmers =
new EnabledDimmer();
257 dimmers->dimmer = dimmer;
259 struct EnabledDimmer* first =
new EnabledDimmer();
260 first->next = dimmers;
261 dimmers->prev = first;
266 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
271void Mycila::ThyristorDimmer::_unregisterDimmer(Mycila::ThyristorDimmer* dimmer) {
272 LOGD(TAG,
"Unregister dimmer %p on pin %d", dimmer, dimmer->
getPin());
275 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
278 struct EnabledDimmer* current = dimmers;
279 while (current !=
nullptr) {
280 if (current->dimmer == dimmer) {
281 if (current->prev !=
nullptr) {
282 current->prev->next = current->next;
284 dimmers = current->next;
286 if (current->next !=
nullptr) {
287 current->next->prev = current->prev;
292 current = current->next;
296 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
299 if (dimmers ==
nullptr) {
300 LOGI(TAG,
"Stopping dimmer firing ISR");
301 gptimer_stop(fire_timer);
302 ESP_ERROR_CHECK(gptimer_disable(fire_timer));
303 ESP_ERROR_CHECK(gptimer_del_timer(fire_timer));
304 fire_timer =
nullptr;
bool setDutyCycle(float dutyCycle)
Set the power duty.
gpio_num_t getPin() const
Get the GPIO pin used for the dimmer.
static void onZeroCross(int16_t delayUntilZero, void *args)
virtual void begin()
Enable a dimmer on a specific GPIO pin.
virtual void end()
Disable the dimmer.