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"
35static gptimer_handle_t fire_timer =
nullptr;
36static bool isr_running =
false;
38#ifndef MYCILA_DIMMER_NO_LOCK
39static portMUX_TYPE dimmers_spinlock = portMUX_INITIALIZER_UNLOCKED;
42Mycila::CycleStealingDimmer::RegisteredDimmer* Mycila::CycleStealingDimmer::dimmers =
nullptr;
48 if (!GPIO_IS_VALID_OUTPUT_GPIO(_pin)) {
49 ESP_LOGE(TAG,
"Invalid pin: %" PRId8, _pin);
53 ESP_LOGI(TAG,
"Enable dimmer on pin %" PRId8, _pin);
55 pinMode(_pin, OUTPUT);
56 digitalWrite(_pin, LOW);
57 _registerDimmer(
this);
70 ESP_LOGI(TAG,
"Disable dimmer on pin %" PRId8, _pin);
72 _unregisterDimmer(
this);
73 digitalWrite(_pin, LOW);
76bool Mycila::CycleStealingDimmer::_apply() {
79 if (!_online || !_semiPeriod || _dutyCycleFire == 0) {
81 if (fire_timer !=
nullptr) {
82 ESP_ERROR_CHECK(gptimer_set_alarm_action(fire_timer,
nullptr));
91 gptimer_alarm_config_t fire_timer_alarm_cfg = {
92 .alarm_count = _semiPeriod,
94 .flags = {.auto_reload_on_alarm =
true}};
95 if (fire_timer !=
nullptr) {
96 ESP_ERROR_CHECK(gptimer_set_raw_count(fire_timer, 0));
97 ESP_ERROR_CHECK(gptimer_set_alarm_action(fire_timer, &fire_timer_alarm_cfg));
107 if (inlined_gptimer_set_raw_count(fire_timer, 0) != ESP_OK) {
114bool ARDUINO_ISR_ATTR Mycila::CycleStealingDimmer::_fireTimerISR(gptimer_handle_t timer,
const gptimer_alarm_event_data_t* event,
void* arg) {
124 uint64_t fire_timer_count_value;
125 if (inlined_gptimer_get_raw_count(fire_timer, &fire_timer_count_value) != ESP_OK) {
134#ifndef MYCILA_DIMMER_NO_LOCK
136 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
140 struct RegisteredDimmer* current = dimmers;
141 while (current !=
nullptr) {
155 current = current->next;
158#ifndef MYCILA_DIMMER_NO_LOCK
160 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
168void Mycila::CycleStealingDimmer::_registerDimmer(Mycila::CycleStealingDimmer* dimmer) {
169 if (dimmers ==
nullptr) {
170 ESP_LOGI(TAG,
"Starting dimmer firing ISR");
172 gptimer_config_t timer_config;
173 timer_config.clk_src = GPTIMER_CLK_SRC_DEFAULT;
174 timer_config.direction = GPTIMER_COUNT_UP;
175 timer_config.resolution_hz = 1000000;
176 timer_config.flags.intr_shared =
true;
177 timer_config.intr_priority = 0;
178#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
179 timer_config.flags.backup_before_sleep =
false;
181#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
182 timer_config.flags.allow_pd =
false;
185 ESP_ERROR_CHECK(gptimer_new_timer(&timer_config, &fire_timer));
186 gptimer_event_callbacks_t callbacks_config;
187 callbacks_config.on_alarm = _fireTimerISR;
188 ESP_ERROR_CHECK(gptimer_register_event_callbacks(fire_timer, &callbacks_config,
nullptr));
189 ESP_ERROR_CHECK(gptimer_enable(fire_timer));
190 ESP_ERROR_CHECK(gptimer_start(fire_timer));
193 ESP_LOGD(TAG,
"Register new dimmer %p on pin %d", dimmer, dimmer->
getPin());
195#ifndef MYCILA_DIMMER_NO_LOCK
196 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
199 if (dimmers ==
nullptr) {
200 dimmers =
new RegisteredDimmer();
201 dimmers->dimmer = dimmer;
203 struct RegisteredDimmer* additional =
new RegisteredDimmer();
204 additional->dimmer = dimmer;
205 additional->next = dimmers;
206 additional->prev =
nullptr;
207 dimmers->prev = additional;
208 dimmers = additional;
211#ifndef MYCILA_DIMMER_NO_LOCK
212 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
217void Mycila::CycleStealingDimmer::_unregisterDimmer(Mycila::CycleStealingDimmer* dimmer) {
218 ESP_LOGD(TAG,
"Unregister dimmer %p on pin %d", dimmer, dimmer->
getPin());
220#ifndef MYCILA_DIMMER_NO_LOCK
221 portENTER_CRITICAL_SAFE(&dimmers_spinlock);
224 struct RegisteredDimmer* current = dimmers;
225 while (current !=
nullptr) {
226 if (current->dimmer == dimmer) {
227 if (current->prev !=
nullptr) {
228 current->prev->next = current->next;
230 dimmers = current->next;
232 if (current->next !=
nullptr) {
233 current->next->prev = current->prev;
238 current = current->next;
241#ifndef MYCILA_DIMMER_NO_LOCK
242 portEXIT_CRITICAL_SAFE(&dimmers_spinlock);
245 if (dimmers ==
nullptr) {
246 ESP_LOGI(TAG,
"Stopping dimmer firing ISR");
247 gptimer_stop(fire_timer);
248 ESP_ERROR_CHECK(gptimer_disable(fire_timer));
249 ESP_ERROR_CHECK(gptimer_del_timer(fire_timer));
250 fire_timer =
nullptr;
void end() override
Disable the dimmer.
bool begin() override
Enable a dimmer on a specific GPIO pin.
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.