5#include <MycilaDimmerCycleStealing.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))
33#define TAG "CycleStealing"
43static gptimer_handle_t fire_timer =
nullptr;
44static bool isr_running =
false;
46#ifndef MYCILA_DIMMER_NO_LOCK
47static portMUX_TYPE dimmers_spinlock = portMUX_INITIALIZER_UNLOCKED;
54 if (!GPIO_IS_VALID_OUTPUT_GPIO(_pin)) {
55 ESP_LOGE(TAG,
"Invalid pin: %" PRId8, _pin);
59 ESP_LOGI(TAG,
"Enable dimmer on pin %" PRId8, _pin);
61 pinMode(_pin, OUTPUT);
62 digitalWrite(_pin, LOW);
63 _registerDimmer(
this);
75 ESP_LOGI(TAG,
"Disable dimmer on pin %" PRId8, _pin);
77 _unregisterDimmer(
this);
78 digitalWrite(_pin, LOW);
81bool Mycila::CycleStealingDimmer::_apply() {
84 if (!_online || !_semiPeriod || _dutyCycleFire == 0) {
86 if (fire_timer !=
nullptr) {
87 ESP_ERROR_CHECK(gptimer_set_alarm_action(fire_timer,
nullptr));
96 gptimer_alarm_config_t fire_timer_alarm_cfg = {
97 .alarm_count = _semiPeriod,
99 .flags = {.auto_reload_on_alarm =
true}};
100 if (fire_timer !=
nullptr) {
101 ESP_ERROR_CHECK(gptimer_set_raw_count(fire_timer, 0));
102 ESP_ERROR_CHECK(gptimer_set_alarm_action(fire_timer, &fire_timer_alarm_cfg));
112 if (inlined_gptimer_set_raw_count(fire_timer, 0) != ESP_OK) {
119bool ARDUINO_ISR_ATTR Mycila::CycleStealingDimmer::_fireTimerISR(gptimer_handle_t timer,
const gptimer_alarm_event_data_t* event,
void* arg) {
129 uint64_t fire_timer_count_value;
130 if (inlined_gptimer_get_raw_count(fire_timer, &fire_timer_count_value) != ESP_OK) {
139#ifndef MYCILA_DIMMER_NO_LOCK
141 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
145 struct RegisteredDimmer* current = dimmers;
146 while (current !=
nullptr) {
160 current = current->next;
163#ifndef MYCILA_DIMMER_NO_LOCK
165 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
173void Mycila::CycleStealingDimmer::_registerDimmer(Mycila::CycleStealingDimmer* dimmer) {
174 if (dimmers ==
nullptr) {
175 ESP_LOGI(TAG,
"Starting dimmer firing ISR");
177 gptimer_config_t timer_config;
178 timer_config.clk_src = GPTIMER_CLK_SRC_DEFAULT;
179 timer_config.direction = GPTIMER_COUNT_UP;
180 timer_config.resolution_hz = 1000000;
181 timer_config.flags.intr_shared =
true;
182 timer_config.intr_priority = 0;
183#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
184 timer_config.flags.backup_before_sleep =
false;
186#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
187 timer_config.flags.allow_pd =
false;
190 ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &fire_timer));
191 gptimer_event_callbacks_t callbacks_config;
192 callbacks_config.on_alarm = _fireTimerISR;
193 ESP_ERROR_CHECK(gptimer_register_event_callbacks(fire_timer, &callbacks_config,
nullptr));
194 ESP_ERROR_CHECK(gptimer_enable(fire_timer));
195 ESP_ERROR_CHECK(gptimer_start(fire_timer));
198 ESP_LOGD(TAG,
"Register new dimmer %p on pin %d", dimmer, dimmer->
getPin());
200#ifndef MYCILA_DIMMER_NO_LOCK
201 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
204 if (dimmers ==
nullptr) {
205 dimmers =
new RegisteredDimmer();
206 dimmers->dimmer = dimmer;
208 struct RegisteredDimmer* first =
new RegisteredDimmer();
209 first->next = dimmers;
210 dimmers->prev = first;
214#ifndef MYCILA_DIMMER_NO_LOCK
215 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
220void Mycila::CycleStealingDimmer::_unregisterDimmer(Mycila::CycleStealingDimmer* dimmer) {
221 ESP_LOGD(TAG,
"Unregister dimmer %p on pin %d", dimmer, dimmer->
getPin());
223#ifndef MYCILA_DIMMER_NO_LOCK
224 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
227 struct RegisteredDimmer* current = dimmers;
228 while (current !=
nullptr) {
229 if (current->dimmer == dimmer) {
230 if (current->prev !=
nullptr) {
231 current->prev->next = current->next;
233 dimmers = current->next;
235 if (current->next !=
nullptr) {
236 current->next->prev = current->prev;
241 current = current->next;
244#ifndef MYCILA_DIMMER_NO_LOCK
245 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
248 if (dimmers ==
nullptr) {
249 ESP_LOGI(TAG,
"Stopping dimmer firing ISR");
250 gptimer_stop(fire_timer);
251 ESP_ERROR_CHECK(gptimer_disable(fire_timer));
252 ESP_ERROR_CHECK(gptimer_del_timer(fire_timer));
253 fire_timer =
nullptr;
void begin() override
Enable a dimmer on a specific GPIO pin.
void end() override
Disable the dimmer.
static void onZeroCross(int16_t delayUntilZero, void *args)
gpio_num_t getPin() const
Get the GPIO pin used for the dimmer.
bool setDutyCycle(float dutyCycle)
Set the power duty.