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#ifndef GPIO_IS_VALID_OUTPUT_GPIO
24 #define GPIO_IS_VALID_OUTPUT_GPIO(gpio_num) ((gpio_num >= 0) && \
25 (((1ULL << (gpio_num)) & SOC_GPIO_VALID_OUTPUT_GPIO_MASK) != 0))
28#ifndef GPIO_IS_VALID_GPIO
29 #define GPIO_IS_VALID_GPIO(gpio_num) ((gpio_num >= 0) && \
30 (((1ULL << (gpio_num)) & SOC_GPIO_VALID_GPIO_MASK) != 0))
36#define PHASE_DELAY_MIN_US (90)
38#define TAG "Thyristor"
40static gptimer_handle_t fire_timer =
nullptr;
42#ifndef MYCILA_DIMMER_NO_LOCK
43static portMUX_TYPE dimmers_spinlock = portMUX_INITIALIZER_UNLOCKED;
46Mycila::ThyristorDimmer::RegisteredDimmer* Mycila::ThyristorDimmer::dimmers =
nullptr;
52 if (!GPIO_IS_VALID_OUTPUT_GPIO(_pin)) {
53 ESP_LOGE(TAG,
"Invalid pin: %" PRId8, _pin);
57 ESP_LOGI(TAG,
"Enable dimmer on pin %" PRId8, _pin);
59 pinMode(_pin, OUTPUT);
60 digitalWrite(_pin, LOW);
61 _registerDimmer(
this);
74 ESP_LOGI(TAG,
"Disable dimmer on pin %" PRId8, _pin);
76 _unregisterDimmer(
this);
77 digitalWrite(_pin, LOW);
82 gptimer_alarm_config_t fire_timer_alarm_cfg = {.alarm_count = UINT16_MAX, .reload_count = 0, .flags = {.auto_reload_on_alarm =
false}};
85 if (inlined_gptimer_set_raw_count(fire_timer, 0) != ESP_OK) {
90#ifndef MYCILA_DIMMER_NO_LOCK
92 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
96 struct RegisteredDimmer* current = dimmers;
97 while (current !=
nullptr) {
98 if (current->dimmer->_delay) {
100 gpio_ll_set_level(&GPIO, current->dimmer->_pin, LOW);
104 current->alarm_count = current->dimmer->_delay < PHASE_DELAY_MIN_US ? PHASE_DELAY_MIN_US : current->dimmer->_delay;
106 if (current->alarm_count < fire_timer_alarm_cfg.alarm_count)
107 fire_timer_alarm_cfg.alarm_count = current->alarm_count;
110 gpio_ll_set_level(&GPIO, current->dimmer->_pin, HIGH);
112 current->alarm_count = UINT16_MAX;
114 current = current->next;
117#ifndef MYCILA_DIMMER_NO_LOCK
119 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
123 uint64_t fire_timer_count_value;
124 if (inlined_gptimer_get_raw_count(fire_timer, &fire_timer_count_value) != ESP_OK) {
130 if (fire_timer_count_value >= delayUntilZero) {
131 fire_timer_count_value -= delayUntilZero;
134 if (fire_timer_count_value <= fire_timer_alarm_cfg.alarm_count) {
136 if (inlined_gptimer_set_raw_count(fire_timer, fire_timer_count_value) == ESP_OK) {
137 _fireTimerISR(fire_timer,
nullptr,
nullptr);
145 if (inlined_gptimer_set_raw_count(fire_timer, -
static_cast<uint64_t
>(delayUntilZero) + fire_timer_count_value) == ESP_OK) {
147 inlined_gptimer_set_alarm_action(fire_timer, &fire_timer_alarm_cfg);
153bool ARDUINO_ISR_ATTR Mycila::ThyristorDimmer::_fireTimerISR(gptimer_handle_t timer,
const gptimer_alarm_event_data_t* event,
void* arg) {
155 gptimer_alarm_config_t fire_timer_alarm_cfg = {.alarm_count = UINT16_MAX, .reload_count = 0, .flags = {.auto_reload_on_alarm =
false}};
158 uint64_t fire_timer_count_value;
159 if (inlined_gptimer_get_raw_count(fire_timer, &fire_timer_count_value) != ESP_OK) {
165 fire_timer_alarm_cfg.alarm_count = UINT16_MAX;
167#ifndef MYCILA_DIMMER_NO_LOCK
169 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
173 struct RegisteredDimmer* current = dimmers;
174 while (current !=
nullptr) {
175 if (current->alarm_count != UINT16_MAX) {
177 if (current->alarm_count <= fire_timer_count_value) {
179 gpio_ll_set_level(&GPIO, current->dimmer->_pin, HIGH);
181 current->alarm_count = UINT16_MAX;
184 if (current->alarm_count < fire_timer_alarm_cfg.alarm_count)
185 fire_timer_alarm_cfg.alarm_count = current->alarm_count;
188 current = current->next;
191#ifndef MYCILA_DIMMER_NO_LOCK
193 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
197 inlined_gptimer_get_raw_count(fire_timer, &fire_timer_count_value);
198 }
while (fire_timer_alarm_cfg.alarm_count != UINT16_MAX && fire_timer_alarm_cfg.alarm_count <= fire_timer_count_value);
201 if (fire_timer_alarm_cfg.alarm_count != UINT16_MAX)
202 inlined_gptimer_set_alarm_action(fire_timer, &fire_timer_alarm_cfg);
208void Mycila::ThyristorDimmer::_registerDimmer(Mycila::ThyristorDimmer* dimmer) {
209 if (dimmers ==
nullptr) {
210 ESP_LOGI(TAG,
"Starting dimmer firing ISR");
212 gptimer_config_t timer_config;
213 timer_config.clk_src = GPTIMER_CLK_SRC_DEFAULT;
214 timer_config.direction = GPTIMER_COUNT_UP;
215 timer_config.resolution_hz = 1000000;
216 timer_config.flags.intr_shared =
true;
217 timer_config.intr_priority = 0;
218#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
219 timer_config.flags.backup_before_sleep =
false;
221#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
222 timer_config.flags.allow_pd =
false;
225 ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &fire_timer));
226 gptimer_event_callbacks_t callbacks_config;
227 callbacks_config.on_alarm = _fireTimerISR;
228 ESP_ERROR_CHECK(gptimer_register_event_callbacks(fire_timer, &callbacks_config,
nullptr));
229 ESP_ERROR_CHECK(gptimer_enable(fire_timer));
230 ESP_ERROR_CHECK(gptimer_start(fire_timer));
233 ESP_LOGD(TAG,
"Register new dimmer %p on pin %d", dimmer, dimmer->
getPin());
235#ifndef MYCILA_DIMMER_NO_LOCK
236 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
239 if (dimmers ==
nullptr) {
240 dimmers =
new RegisteredDimmer();
241 dimmers->dimmer = dimmer;
243 struct RegisteredDimmer* additional =
new RegisteredDimmer();
244 additional->dimmer = dimmer;
245 additional->next = dimmers;
246 additional->prev =
nullptr;
247 dimmers->prev = additional;
248 dimmers = additional;
251#ifndef MYCILA_DIMMER_NO_LOCK
252 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
257void Mycila::ThyristorDimmer::_unregisterDimmer(Mycila::ThyristorDimmer* dimmer) {
258 ESP_LOGD(TAG,
"Unregister dimmer %p on pin %d", dimmer, dimmer->
getPin());
260#ifndef MYCILA_DIMMER_NO_LOCK
261 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
264 struct RegisteredDimmer* current = dimmers;
265 while (current !=
nullptr) {
266 if (current->dimmer == dimmer) {
267 if (current->prev !=
nullptr) {
268 current->prev->next = current->next;
270 dimmers = current->next;
272 if (current->next !=
nullptr) {
273 current->next->prev = current->prev;
278 current = current->next;
281#ifndef MYCILA_DIMMER_NO_LOCK
282 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
285 if (dimmers ==
nullptr) {
286 ESP_LOGI(TAG,
"Stopping dimmer firing ISR");
287 gptimer_stop(fire_timer);
288 ESP_ERROR_CHECK(gptimer_disable(fire_timer));
289 ESP_ERROR_CHECK(gptimer_del_timer(fire_timer));
290 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)
bool begin() override
Enable a dimmer on a specific GPIO pin.
void end() override
Disable the dimmer.