From 71502456d3687c9e977c3513942eac6f1c9df58c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Thu, 12 Jun 2025 12:30:39 +0200 Subject: [PATCH 1/4] feat(ledc): Enhance LEDC auto channel/timer selection for multi-group support --- cores/esp32/esp32-hal-ledc.c | 98 +++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 30 deletions(-) diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c index 764c2803b4b..f28f6d3a1a3 100644 --- a/cores/esp32/esp32-hal-ledc.c +++ b/cores/esp32/esp32-hal-ledc.c @@ -56,7 +56,7 @@ static bool find_matching_timer(uint8_t speed_mode, uint32_t freq, uint8_t resol peripheral_bus_type_t type = perimanGetPinBusType(i); if (type == ESP32_BUS_TYPE_LEDC) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC); - if (bus != NULL && (bus->channel / 8) == speed_mode && bus->freq_hz == freq && bus->channel_resolution == resolution) { + if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode && bus->freq_hz == freq && bus->channel_resolution == resolution) { log_d("Found matching timer %u for freq=%u, resolution=%u", bus->timer_num, freq, resolution); *timer_num = bus->timer_num; return true; @@ -78,7 +78,7 @@ static bool find_free_timer(uint8_t speed_mode, uint8_t *timer_num) { peripheral_bus_type_t type = perimanGetPinBusType(i); if (type == ESP32_BUS_TYPE_LEDC) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC); - if (bus != NULL && (bus->channel / 8) == speed_mode) { + if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode) { log_d("Timer %u is in use by channel %u", bus->timer_num, bus->channel); used_timers |= (1 << bus->timer_num); } @@ -110,7 +110,7 @@ static void remove_channel_from_timer(uint8_t speed_mode, uint8_t timer_num, uin peripheral_bus_type_t type = perimanGetPinBusType(i); if (type == ESP32_BUS_TYPE_LEDC) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC); - if (bus != NULL && (bus->channel / 8) == speed_mode && bus->timer_num == timer_num && bus->channel != channel) { + if (bus != NULL && (bus->channel / SOC_LEDC_CHANNEL_NUM) == speed_mode && bus->timer_num == timer_num && bus->channel != channel) { log_d("Timer %u is still in use by channel %u", timer_num, bus->channel); timer_in_use = true; break; @@ -168,8 +168,8 @@ static bool ledcDetachBus(void *bus) { } pinMatrixOutDetach(handle->pin, false, false); if (!channel_found) { - uint8_t group = (handle->channel / 8); - remove_channel_from_timer(group, handle->timer_num, handle->channel % 8); + uint8_t group = (handle->channel / SOC_LEDC_CHANNEL_NUM); + remove_channel_from_timer(group, handle->timer_num, handle->channel % SOC_LEDC_CHANNEL_NUM); ledc_handle.used_channels &= ~(1UL << handle->channel); } free(handle); @@ -206,13 +206,13 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c return false; } - uint8_t group = (channel / 8); + uint8_t group = (channel / SOC_LEDC_CHANNEL_NUM); uint8_t timer = 0; bool channel_used = ledc_handle.used_channels & (1UL << channel); if (channel_used) { log_i("Channel %u is already set up, given frequency and resolution will be ignored", channel); - if (ledc_set_pin(pin, group, channel % 8) != ESP_OK) { + if (ledc_set_pin(pin, group, channel % SOC_LEDC_CHANNEL_NUM) != ESP_OK) { log_e("Attaching pin to already used channel failed!"); return false; } @@ -220,7 +220,7 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c // Find a timer with matching frequency and resolution, or a free timer if (!find_matching_timer(group, freq, resolution, &timer)) { if (!find_free_timer(group, &timer)) { - log_e("No free timers available for speed mode %u", group); + log_w("No free timers available for speed mode %u", group); return false; } @@ -239,12 +239,12 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c } } - uint32_t duty = ledc_get_duty(group, (channel % 8)); + uint32_t duty = ledc_get_duty(group, (channel % SOC_LEDC_CHANNEL_NUM)); ledc_channel_config_t ledc_channel; memset((void *)&ledc_channel, 0, sizeof(ledc_channel_config_t)); ledc_channel.speed_mode = group; - ledc_channel.channel = (channel % 8); + ledc_channel.channel = (channel % SOC_LEDC_CHANNEL_NUM); ledc_channel.timer_sel = timer; ledc_channel.intr_type = LEDC_INTR_DISABLE; ledc_channel.gpio_num = pin; @@ -291,14 +291,36 @@ bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution) { } uint8_t channel = __builtin_ctz(free_channel); // Convert the free_channel bit to channel number - return ledcAttachChannel(pin, freq, resolution, channel); + // Try the first available channel + if (ledcAttachChannel(pin, freq, resolution, channel)) { + return true; + } + +#ifdef SOC_LEDC_SUPPORT_HS_MODE + // If first attempt failed and HS mode is supported, try to find a free channel in group 1 + if ((channel / SOC_LEDC_CHANNEL_NUM) == 0) { // First attempt was in group 0 + log_d("LEDC: Group 0 channel %u failed, trying to find a free channel in group 1", channel); + // Find free channels specifically in group 1 + uint32_t group1_mask = ((1UL << SOC_LEDC_CHANNEL_NUM) - 1) << SOC_LEDC_CHANNEL_NUM; + int group1_free_channel = (~ledc_handle.used_channels) & group1_mask; + if (group1_free_channel != 0) { + uint8_t group1_channel = __builtin_ctz(group1_free_channel); + if (ledcAttachChannel(pin, freq, resolution, group1_channel)) { + return true; + } + } + } +#endif + + log_e("No free timers available for freq=%u, resolution=%u. To attach a new channel, use the same frequency and resolution as an already attached channel to share its timer.", freq, resolution); + return false; } bool ledcWrite(uint8_t pin, uint32_t duty) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); if (bus != NULL) { - uint8_t group = (bus->channel / 8), channel = (bus->channel % 8); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM); //Fixing if all bits in resolution is set = LEDC FULL ON uint32_t max_duty = (1 << bus->channel_resolution) - 1; @@ -307,8 +329,14 @@ bool ledcWrite(uint8_t pin, uint32_t duty) { duty = max_duty + 1; } - ledc_set_duty(group, channel, duty); - ledc_update_duty(group, channel); + if(ledc_set_duty(group, channel, duty) != ESP_OK) { + log_e("ledc_set_duty failed"); + return false; + } + if(ledc_update_duty(group, channel) != ESP_OK) { + log_e("ledc_update_duty failed"); + return false; + } return true; } @@ -321,7 +349,11 @@ bool ledcWriteChannel(uint8_t channel, uint32_t duty) { log_e("Channel %u is not available (maximum %u) or not used!", channel, LEDC_CHANNELS); return false; } - uint8_t group = (channel / 8), timer = ((channel / 2) % 4); + uint8_t group = (channel / SOC_LEDC_CHANNEL_NUM); + ledc_timer_t timer; + + // Get the actual timer being used by this channel + ledc_ll_get_channel_timer(LEDC_LL_GET_HW(), group, (channel % SOC_LEDC_CHANNEL_NUM), &timer); //Fixing if all bits in resolution is set = LEDC FULL ON uint32_t resolution = 0; @@ -333,8 +365,14 @@ bool ledcWriteChannel(uint8_t channel, uint32_t duty) { duty = max_duty + 1; } - ledc_set_duty(group, channel, duty); - ledc_update_duty(group, channel); + if(ledc_set_duty(group, channel, duty) != ESP_OK) { + log_e("ledc_set_duty failed"); + return false; + } + if(ledc_update_duty(group, channel) != ESP_OK) { + log_e("ledc_update_duty failed"); + return false; + } return true; } @@ -343,7 +381,7 @@ uint32_t ledcRead(uint8_t pin) { ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); if (bus != NULL) { - uint8_t group = (bus->channel / 8), channel = (bus->channel % 8); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM); return ledc_get_duty(group, channel); } return 0; @@ -355,8 +393,8 @@ uint32_t ledcReadFreq(uint8_t pin) { if (!ledcRead(pin)) { return 0; } - uint8_t group = (bus->channel / 8), timer = ((bus->channel / 2) % 4); - return ledc_get_freq(group, timer); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM); + return ledc_get_freq(group, bus->timer_num); } return 0; } @@ -370,12 +408,12 @@ uint32_t ledcWriteTone(uint8_t pin, uint32_t freq) { return 0; } - uint8_t group = (bus->channel / 8), timer = ((bus->channel / 2) % 4); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM); ledc_timer_config_t ledc_timer; memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t)); ledc_timer.speed_mode = group; - ledc_timer.timer_num = timer; + ledc_timer.timer_num = bus->timer_num; ledc_timer.duty_resolution = 10; ledc_timer.freq_hz = freq; ledc_timer.clk_cfg = clock_source; @@ -386,7 +424,7 @@ uint32_t ledcWriteTone(uint8_t pin, uint32_t freq) { } bus->channel_resolution = 10; - uint32_t res_freq = ledc_get_freq(group, timer); + uint32_t res_freq = ledc_get_freq(group, bus->timer_num); ledcWrite(pin, 0x1FF); return res_freq; } @@ -427,12 +465,12 @@ uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution) { log_e("LEDC pin %u - resolution is zero or it is too big (maximum %u)", pin, LEDC_MAX_BIT_WIDTH); return 0; } - uint8_t group = (bus->channel / 8), timer = ((bus->channel / 2) % 4); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM); ledc_timer_config_t ledc_timer; memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t)); ledc_timer.speed_mode = group; - ledc_timer.timer_num = timer; + ledc_timer.timer_num = bus->timer_num; ledc_timer.duty_resolution = resolution; ledc_timer.freq_hz = freq; ledc_timer.clk_cfg = clock_source; @@ -442,7 +480,7 @@ uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution) { return 0; } bus->channel_resolution = resolution; - return ledc_get_freq(group, timer); + return ledc_get_freq(group, bus->timer_num); } return 0; } @@ -453,12 +491,12 @@ bool ledcOutputInvert(uint8_t pin, bool out_invert) { gpio_set_level(pin, out_invert); #ifdef CONFIG_IDF_TARGET_ESP32P4 - esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT_PAD_OUT0_IDX + ((bus->channel) % 8), out_invert, 0); + esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT_PAD_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0); #else #ifdef SOC_LEDC_SUPPORT_HS_MODE - esp_rom_gpio_connect_out_signal(pin, ((bus->channel / 8 == 0) ? LEDC_HS_SIG_OUT0_IDX : LEDC_LS_SIG_OUT0_IDX) + ((bus->channel) % 8), out_invert, 0); + esp_rom_gpio_connect_out_signal(pin, ((bus->channel / SOC_LEDC_CHANNEL_NUM == 0) ? LEDC_HS_SIG_OUT0_IDX : LEDC_LS_SIG_OUT0_IDX) + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0); #else - esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + ((bus->channel) % 8), out_invert, 0); + esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0); #endif #endif // ifdef CONFIG_IDF_TARGET_ESP32P4 return true; @@ -505,7 +543,7 @@ static bool ledcFadeConfig(uint8_t pin, uint32_t start_duty, uint32_t target_dut } #endif #endif - uint8_t group = (bus->channel / 8), channel = (bus->channel % 8); + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM); // Initialize fade service. if (!fade_initialized) { From e479eeee51d35f45c4bfbfffba7b0ba82317537c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:59:48 +0200 Subject: [PATCH 2/4] feat(ledc): Add Gamma Fade support --- cores/esp32/esp32-hal-ledc.c | 158 +++++++++++++++++- cores/esp32/esp32-hal-ledc.h | 79 +++++++++ .../AnalogOut/LEDCGammaFade/LEDCGammaFade.ino | 117 +++++++++++++ .../examples/AnalogOut/LEDCGammaFade/ci.json | 5 + 4 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino create mode 100644 libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c index f28f6d3a1a3..5189f23fcd9 100644 --- a/cores/esp32/esp32-hal-ledc.c +++ b/cores/esp32/esp32-hal-ledc.c @@ -22,6 +22,9 @@ #include "soc/gpio_sig_map.h" #include "esp_rom_gpio.h" #include "hal/ledc_ll.h" +#if SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED +#include +#endif #ifdef SOC_LEDC_SUPPORT_HS_MODE #define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM << 1) @@ -274,7 +277,7 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c ledc_handle.used_channels |= 1UL << channel; } - if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_LEDC, (void *)handle, group, channel)) { + if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_LEDC, (void *)handle, channel, timer)) { ledcDetachBus((void *)handle); return false; } @@ -600,6 +603,159 @@ bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_ return ledcFadeConfig(pin, start_duty, target_duty, max_fade_time_ms, userFunc, arg); } +#ifdef SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED +// Default gamma factor for gamma correction (common value for LEDs) +static float ledcGammaFactor = 2.8; +// Gamma correction LUT support +static const float *ledcGammaLUT = NULL; +static uint16_t ledcGammaLUTSize = 0; +// Global variable to store current resolution for gamma callback +static uint8_t ledcGammaResolution = 13; + +bool ledcSetGammaTable(const float* gamma_table, uint16_t size) { + if (gamma_table == NULL || size == 0) { + log_e("Invalid gamma table or size"); + return false; + } + ledcGammaLUT = gamma_table; + ledcGammaLUTSize = size; + log_i("Custom gamma LUT set with %u entries", size); + return true; +} + +void ledcClearGammaTable(void) { + ledcGammaLUT = NULL; + ledcGammaLUTSize = 0; + log_i("Gamma LUT cleared, using mathematical calculation"); +} + +void ledcSetGammaFactor(float factor) { + ledcGammaFactor = factor; +} + +// Gamma correction calculator function +static uint32_t ledcGammaCorrection(uint32_t duty) { + if (duty == 0) return 0; + + uint32_t max_duty = (1U << ledcGammaResolution) - 1; + if (duty >= (1U << ledcGammaResolution)) return max_duty; + + // Use LUT if provided, otherwise use mathematical calculation + if (ledcGammaLUT != NULL && ledcGammaLUTSize > 0) { + // LUT-based gamma correction + uint32_t lut_index = (duty * (ledcGammaLUTSize - 1)) / max_duty; + if (lut_index >= ledcGammaLUTSize) lut_index = ledcGammaLUTSize - 1; + + float corrected_normalized = ledcGammaLUT[lut_index]; + return (uint32_t)(corrected_normalized * max_duty); + } else { + // Mathematical gamma correction + double normalized = (double)duty / (1U << ledcGammaResolution); + double corrected = pow(normalized, ledcGammaFactor); + return (uint32_t)(corrected * (1U << ledcGammaResolution)); + } +} + +static bool ledcFadeGammaConfig(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) { + ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); + if (bus != NULL) { + +#ifndef SOC_LEDC_SUPPORT_FADE_STOP +#if !CONFIG_DISABLE_HAL_LOCKS + if (bus->lock == NULL) { + bus->lock = xSemaphoreCreateBinary(); + if (bus->lock == NULL) { + log_e("xSemaphoreCreateBinary failed"); + return false; + } + xSemaphoreGive(bus->lock); + } + //acquire lock + if (xSemaphoreTake(bus->lock, 0) != pdTRUE) { + log_e("LEDC Fade is still running on pin %u! SoC does not support stopping fade.", pin); + return false; + } +#endif +#endif + uint8_t group = (bus->channel / SOC_LEDC_CHANNEL_NUM), channel = (bus->channel % SOC_LEDC_CHANNEL_NUM); + + // Initialize fade service. + if (!fade_initialized) { + ledc_fade_func_install(0); + fade_initialized = true; + } + + bus->fn = (voidFuncPtr)userFunc; + bus->arg = arg; + + ledc_cbs_t callbacks = {.fade_cb = ledcFnWrapper}; + ledc_cb_register(group, channel, &callbacks, (void *)bus); + + // Prepare gamma curve fade parameters + ledc_fade_param_config_t fade_params[SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX]; + uint32_t actual_fade_ranges = 0; + + // Use a moderate number of linear segments for smooth gamma curve + const uint32_t linear_fade_segments = 12; + + // Set the global resolution for gamma correction + ledcGammaResolution = bus->channel_resolution; + + // Fill multi-fade parameter list using ESP-IDF API + esp_err_t err = ledc_fill_multi_fade_param_list( + group, channel, + start_duty, target_duty, + linear_fade_segments, max_fade_time_ms, + ledcGammaCorrection, + SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX, + fade_params, &actual_fade_ranges + ); + + if (err != ESP_OK) { + log_e("ledc_fill_multi_fade_param_list failed: %s", esp_err_to_name(err)); + return false; + } + + // Apply the gamma-corrected start duty + uint32_t gamma_start_duty = ledcGammaCorrection(start_duty); + + // Set multi-fade parameters + err = ledc_set_multi_fade(group, channel, gamma_start_duty, fade_params, actual_fade_ranges); + if (err != ESP_OK) { + log_e("ledc_set_multi_fade failed: %s", esp_err_to_name(err)); + return false; + } + + // Start the gamma curve fade + err = ledc_fade_start(group, channel, LEDC_FADE_NO_WAIT); + if (err != ESP_OK) { + log_e("ledc_fade_start failed: %s", esp_err_to_name(err)); + return false; + } + + log_d("Gamma curve fade started on pin %u: %u -> %u over %dms", pin, start_duty, target_duty, max_fade_time_ms); + + } else { + log_e("Pin %u is not attached to LEDC. Call ledcAttach first!", pin); + return false; + } + return true; +} + +bool ledcFadeGamma(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms) { + return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, NULL, NULL); +} + +bool ledcFadeGammaWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, voidFuncPtr userFunc) { + return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, (voidFuncPtrArg)userFunc, NULL); +} + +bool ledcFadeGammaWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg) { + return ledcFadeGammaConfig(pin, start_duty, target_duty, max_fade_time_ms, userFunc, arg); +} + +#endif /* SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED */ + static uint8_t analog_resolution = 8; static int analog_frequency = 1000; void analogWrite(uint8_t pin, int value) { diff --git a/cores/esp32/esp32-hal-ledc.h b/cores/esp32/esp32-hal-ledc.h index f1a27dd4f7a..3169e10e4b9 100644 --- a/cores/esp32/esp32-hal-ledc.h +++ b/cores/esp32/esp32-hal-ledc.h @@ -232,6 +232,85 @@ bool ledcFadeWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_dut */ bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg); +//Gamma Curve Fade functions - only available on supported chips +#ifdef SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED + +/** + * @brief Set a custom gamma correction lookup table for gamma curve fading. + * The LUT should contain normalized values (0.0 to 1.0) representing + * the gamma-corrected brightness curve. + * + * @param gamma_table Pointer to array of float values (0.0 to 1.0) + * @param size Number of entries in the lookup table + * + * @return true if gamma table was successfully set, false otherwise. + * + * @note The LUT array must remain valid for as long as gamma fading is used. + * Larger tables provide smoother transitions but use more memory. + */ +bool ledcSetGammaTable(const float* gamma_table, uint16_t size); + +/** + * @brief Clear the current gamma correction lookup table. + * After calling this, gamma correction will use mathematical + * calculation with the default gamma factor (2.8). + */ +void ledcClearGammaTable(void); + +/** + * @brief Set the gamma factor for gamma correction. + * + * @param factor Gamma factor to use for gamma correction. + */ +void ledcSetGammaFactor(float factor); + +/** + * @brief Setup and start a gamma curve fade on a given LEDC pin. + * Gamma correction makes LED brightness changes appear more gradual to human eyes. + * + * @param pin GPIO pin + * @param start_duty initial duty cycle of the fade + * @param target_duty target duty cycle of the fade + * @param max_fade_time_ms maximum fade time in milliseconds + * + * @return true if gamma fade was successfully set and started, false otherwise. + * + * @note This function is only available on ESP32 variants that support gamma curve fading. + */ +bool ledcFadeGamma(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms); + +/** + * @brief Setup and start a gamma curve fade on a given LEDC pin with a callback function. + * + * @param pin GPIO pin + * @param start_duty initial duty cycle of the fade + * @param target_duty target duty cycle of the fade + * @param max_fade_time_ms maximum fade time in milliseconds + * @param userFunc callback function to be called after fade is finished + * + * @return true if gamma fade was successfully set and started, false otherwise. + * + * @note This function is only available on ESP32 variants that support gamma curve fading. + */ +bool ledcFadeGammaWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void)); + +/** + * @brief Setup and start a gamma curve fade on a given LEDC pin with a callback function and argument. + * + * @param pin GPIO pin + * @param start_duty initial duty cycle of the fade + * @param target_duty target duty cycle of the fade + * @param max_fade_time_ms maximum fade time in milliseconds + * @param userFunc callback function to be called after fade is finished + * @param arg argument to be passed to the callback function + * + * @return true if gamma fade was successfully set and started, false otherwise. + * + * @note This function is only available on ESP32 variants that support gamma curve fading. + */ +bool ledcFadeGammaWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg); +#endif // SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED + #ifdef __cplusplus } #endif diff --git a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino new file mode 100644 index 00000000000..dc16d94cf09 --- /dev/null +++ b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino @@ -0,0 +1,117 @@ +/* LEDC Gamma Curve Fade Arduino Example + + This example demonstrates gamma curve fading on ESP32 variants that support it. + Gamma correction makes LED brightness changes appear more gradual and natural + to human eyes compared to linear fading. + + Two methods are supported: + 1. Using a pre-computed Gamma Look-Up Table (LUT) for better performance + 2. Using mathematical gamma correction with a gamma factor + + Supported chips: ESP32-C6, ESP32-C5, ESP32-H2, ESP32-P4 and future chips with Gamma Fade support + + Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/) +*/ + +// use 12 bit precision for LEDC timer +#define LEDC_TIMER_12_BIT 12 + +// use 5000 Hz as a LEDC base frequency +#define LEDC_BASE_FREQ 5000 + +// define starting duty, target duty and maximum fade time +#define LEDC_START_DUTY (0) +#define LEDC_TARGET_DUTY (4095) +#define LEDC_FADE_TIME (2000) + +// gamma factor for mathematical calculation +#define LEDC_GAMMA_FACTOR (2.6) + +// use gamma LUT for better performance instead of mathematical calculation (gamma factor) +#define USE_GAMMA_LUT 1 + +// fade LED pins +const uint8_t ledPinR = 4; +const uint8_t ledPinG = 5; +const uint8_t ledPinB = 6; + +uint8_t fade_ended = 0; // status of LED gamma fade +bool fade_in = true; + +#ifdef USE_GAMMA_LUT +// Custom Gamma LUT demonstration with only 10 steps (the default built-in Gamma lut is 101 steps) +// Brightness 0 - 100% gamma correction look up table (gamma = 2.6) +// Y = B ^ 2.6 - Pre-computed LUT to save runtime computation +static const float ledcGammaLUT[101] = { + 0.000000, 0.000006, 0.000038, 0.000110, 0.000232, 0.000414, 0.000666, 0.000994, 0.001406, 0.001910, + 0.002512, 0.003218, 0.004035, 0.004969, 0.006025, 0.007208, 0.008525, 0.009981, 0.011580, 0.013328, + 0.015229, 0.017289, 0.019512, 0.021902, 0.024465, 0.027205, 0.030125, 0.033231, 0.036527, 0.040016, + 0.043703, 0.047593, 0.051688, 0.055993, 0.060513, 0.065249, 0.070208, 0.075392, 0.080805, 0.086451, + 0.092333, 0.098455, 0.104821, 0.111434, 0.118298, 0.125416, 0.132792, 0.140428, 0.148329, 0.156498, + 0.164938, 0.173653, 0.182645, 0.191919, 0.201476, 0.211321, 0.221457, 0.231886, 0.242612, 0.253639, + 0.264968, 0.276603, 0.288548, 0.300805, 0.313378, 0.326268, 0.339480, 0.353016, 0.366879, 0.381073, + 0.395599, 0.410461, 0.425662, 0.441204, 0.457091, 0.473325, 0.489909, 0.506846, 0.524138, 0.541789, + 0.559801, 0.578177, 0.596920, 0.616032, 0.635515, 0.655374, 0.675610, 0.696226, 0.717224, 0.738608, + 0.760380, 0.782542, 0.805097, 0.828048, 0.851398, 0.875148, 0.899301, 0.923861, 0.948829, 0.974208, + 1.000000, +}; +#endif + +void ARDUINO_ISR_ATTR LED_FADE_ISR() { + fade_ended += 1; +} + +void setup() { + // Initialize serial communication at 115200 bits per second: + Serial.begin(115200); + + // Setup timer with given frequency, resolution and attach it to a led pin with auto-selected channel + ledcAttach(ledPinR, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT); + ledcAttach(ledPinG, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT); + ledcAttach(ledPinB, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT); + + +#if USE_GAMMA_LUT // Use default gamma LUT for better performance + ledcSetGammaTable(ledcGammaLUT, 101); +#else // Use mathematical gamma correction (default, more flexible) + ledcSetGammaFactor(LEDC_GAMMA_FACTOR); // default gamma factor is 2.8 +#endif + + // Setup and start gamma curve fade on led (duty from 0 to 4095) + ledcFadeGamma(ledPinR, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME); + ledcFadeGamma(ledPinG, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME); + ledcFadeGamma(ledPinB, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME); + Serial.println("LED Gamma Fade on started."); + + // Wait for fade to end + delay(LEDC_FADE_TIME); + + // Setup and start gamma curve fade off led and use ISR (duty from 4095 to 0) + ledcFadeGammaWithInterrupt(ledPinR, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinG, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinB, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + Serial.println("LED Gamma Fade off started."); +} + +void loop() { + // Check if fade_ended flag was set to true in ISR + if (fade_ended == 3) { + Serial.println("LED gamma fade ended"); + fade_ended = 0; + + // Check what gamma fade should be started next + if (fade_in) { + ledcFadeGammaWithInterrupt(ledPinR, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinG, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinB, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + Serial.println("LED Gamma Fade in started."); + fade_in = false; + } else { + ledcFadeGammaWithInterrupt(ledPinR, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinG, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + ledcFadeGammaWithInterrupt(ledPinB, LEDC_TARGET_DUTY, LEDC_START_DUTY, LEDC_FADE_TIME, LED_FADE_ISR); + Serial.println("LED Gamma Fade out started."); + fade_in = true; + } + } +} diff --git a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json new file mode 100644 index 00000000000..a9d8603b7bf --- /dev/null +++ b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json @@ -0,0 +1,5 @@ +{ + "requires": [ + "CONFIG_SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED=y" + ] +} From ed72c004f9a50ff5116d7d8b1293e961ebe3667f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Proch=C3=A1zka?= <90197375+P-R-O-C-H-Y@users.noreply.github.com> Date: Mon, 16 Jun 2025 11:22:45 +0200 Subject: [PATCH 3/4] fix(example): Update comments --- .../ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino index dc16d94cf09..3873d2b44ec 100644 --- a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino +++ b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino @@ -39,8 +39,7 @@ uint8_t fade_ended = 0; // status of LED gamma fade bool fade_in = true; #ifdef USE_GAMMA_LUT -// Custom Gamma LUT demonstration with only 10 steps (the default built-in Gamma lut is 101 steps) -// Brightness 0 - 100% gamma correction look up table (gamma = 2.6) +// Custom Gamma LUT demonstration with 101 steps (Brightness 0 - 100% gamma correction look up table (gamma = 2.6)) // Y = B ^ 2.6 - Pre-computed LUT to save runtime computation static const float ledcGammaLUT[101] = { 0.000000, 0.000006, 0.000038, 0.000110, 0.000232, 0.000414, 0.000666, 0.000994, 0.001406, 0.001910, @@ -74,7 +73,7 @@ void setup() { #if USE_GAMMA_LUT // Use default gamma LUT for better performance ledcSetGammaTable(ledcGammaLUT, 101); #else // Use mathematical gamma correction (default, more flexible) - ledcSetGammaFactor(LEDC_GAMMA_FACTOR); // default gamma factor is 2.8 + ledcSetGammaFactor(LEDC_GAMMA_FACTOR); // This is optional to set custom gamma factor (default is 2.8) #endif // Setup and start gamma curve fade on led (duty from 0 to 4095) From ca7ad626d4deb2203dc7f69d92096500ee10f9c9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 09:42:35 +0000 Subject: [PATCH 4/4] ci(pre-commit): Apply automatic fixes --- cores/esp32/esp32-hal-ledc.c | 54 +++++++++++-------- cores/esp32/esp32-hal-ledc.h | 4 +- .../AnalogOut/LEDCGammaFade/LEDCGammaFade.ino | 29 +++++----- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c index 5189f23fcd9..18b722f80a2 100644 --- a/cores/esp32/esp32-hal-ledc.c +++ b/cores/esp32/esp32-hal-ledc.c @@ -315,7 +315,11 @@ bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution) { } #endif - log_e("No free timers available for freq=%u, resolution=%u. To attach a new channel, use the same frequency and resolution as an already attached channel to share its timer.", freq, resolution); + log_e( + "No free timers available for freq=%u, resolution=%u. To attach a new channel, use the same frequency and resolution as an already attached channel to " + "share its timer.", + freq, resolution + ); return false; } @@ -332,11 +336,11 @@ bool ledcWrite(uint8_t pin, uint32_t duty) { duty = max_duty + 1; } - if(ledc_set_duty(group, channel, duty) != ESP_OK) { + if (ledc_set_duty(group, channel, duty) != ESP_OK) { log_e("ledc_set_duty failed"); return false; } - if(ledc_update_duty(group, channel) != ESP_OK) { + if (ledc_update_duty(group, channel) != ESP_OK) { log_e("ledc_update_duty failed"); return false; } @@ -354,7 +358,7 @@ bool ledcWriteChannel(uint8_t channel, uint32_t duty) { } uint8_t group = (channel / SOC_LEDC_CHANNEL_NUM); ledc_timer_t timer; - + // Get the actual timer being used by this channel ledc_ll_get_channel_timer(LEDC_LL_GET_HW(), group, (channel % SOC_LEDC_CHANNEL_NUM), &timer); @@ -368,11 +372,11 @@ bool ledcWriteChannel(uint8_t channel, uint32_t duty) { duty = max_duty + 1; } - if(ledc_set_duty(group, channel, duty) != ESP_OK) { + if (ledc_set_duty(group, channel, duty) != ESP_OK) { log_e("ledc_set_duty failed"); return false; } - if(ledc_update_duty(group, channel) != ESP_OK) { + if (ledc_update_duty(group, channel) != ESP_OK) { log_e("ledc_update_duty failed"); return false; } @@ -497,7 +501,9 @@ bool ledcOutputInvert(uint8_t pin, bool out_invert) { esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT_PAD_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0); #else #ifdef SOC_LEDC_SUPPORT_HS_MODE - esp_rom_gpio_connect_out_signal(pin, ((bus->channel / SOC_LEDC_CHANNEL_NUM == 0) ? LEDC_HS_SIG_OUT0_IDX : LEDC_LS_SIG_OUT0_IDX) + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0); + esp_rom_gpio_connect_out_signal( + pin, ((bus->channel / SOC_LEDC_CHANNEL_NUM == 0) ? LEDC_HS_SIG_OUT0_IDX : LEDC_LS_SIG_OUT0_IDX) + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0 + ); #else esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + ((bus->channel) % SOC_LEDC_CHANNEL_NUM), out_invert, 0); #endif @@ -612,7 +618,7 @@ static uint16_t ledcGammaLUTSize = 0; // Global variable to store current resolution for gamma callback static uint8_t ledcGammaResolution = 13; -bool ledcSetGammaTable(const float* gamma_table, uint16_t size) { +bool ledcSetGammaTable(const float *gamma_table, uint16_t size) { if (gamma_table == NULL || size == 0) { log_e("Invalid gamma table or size"); return false; @@ -635,17 +641,23 @@ void ledcSetGammaFactor(float factor) { // Gamma correction calculator function static uint32_t ledcGammaCorrection(uint32_t duty) { - if (duty == 0) return 0; - + if (duty == 0) { + return 0; + } + uint32_t max_duty = (1U << ledcGammaResolution) - 1; - if (duty >= (1U << ledcGammaResolution)) return max_duty; - + if (duty >= (1U << ledcGammaResolution)) { + return max_duty; + } + // Use LUT if provided, otherwise use mathematical calculation if (ledcGammaLUT != NULL && ledcGammaLUTSize > 0) { // LUT-based gamma correction uint32_t lut_index = (duty * (ledcGammaLUTSize - 1)) / max_duty; - if (lut_index >= ledcGammaLUTSize) lut_index = ledcGammaLUTSize - 1; - + if (lut_index >= ledcGammaLUTSize) { + lut_index = ledcGammaLUTSize - 1; + } + float corrected_normalized = ledcGammaLUT[lut_index]; return (uint32_t)(corrected_normalized * max_duty); } else { @@ -694,21 +706,17 @@ static bool ledcFadeGammaConfig(uint8_t pin, uint32_t start_duty, uint32_t targe // Prepare gamma curve fade parameters ledc_fade_param_config_t fade_params[SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX]; uint32_t actual_fade_ranges = 0; - + // Use a moderate number of linear segments for smooth gamma curve const uint32_t linear_fade_segments = 12; - + // Set the global resolution for gamma correction ledcGammaResolution = bus->channel_resolution; // Fill multi-fade parameter list using ESP-IDF API esp_err_t err = ledc_fill_multi_fade_param_list( - group, channel, - start_duty, target_duty, - linear_fade_segments, max_fade_time_ms, - ledcGammaCorrection, - SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX, - fade_params, &actual_fade_ranges + group, channel, start_duty, target_duty, linear_fade_segments, max_fade_time_ms, ledcGammaCorrection, SOC_LEDC_GAMMA_CURVE_FADE_RANGE_MAX, fade_params, + &actual_fade_ranges ); if (err != ESP_OK) { @@ -734,7 +742,7 @@ static bool ledcFadeGammaConfig(uint8_t pin, uint32_t start_duty, uint32_t targe } log_d("Gamma curve fade started on pin %u: %u -> %u over %dms", pin, start_duty, target_duty, max_fade_time_ms); - + } else { log_e("Pin %u is not attached to LEDC. Call ledcAttach first!", pin); return false; diff --git a/cores/esp32/esp32-hal-ledc.h b/cores/esp32/esp32-hal-ledc.h index 3169e10e4b9..1663b884a3f 100644 --- a/cores/esp32/esp32-hal-ledc.h +++ b/cores/esp32/esp32-hal-ledc.h @@ -248,7 +248,7 @@ bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_ * @note The LUT array must remain valid for as long as gamma fading is used. * Larger tables provide smoother transitions but use more memory. */ -bool ledcSetGammaTable(const float* gamma_table, uint16_t size); +bool ledcSetGammaTable(const float *gamma_table, uint16_t size); /** * @brief Clear the current gamma correction lookup table. @@ -309,7 +309,7 @@ bool ledcFadeGammaWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t targe * @note This function is only available on ESP32 variants that support gamma curve fading. */ bool ledcFadeGammaWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void *), void *arg); -#endif // SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED +#endif // SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED #ifdef __cplusplus } diff --git a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino index 3873d2b44ec..4ca6c136ddf 100644 --- a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino +++ b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/LEDCGammaFade.ino @@ -25,10 +25,10 @@ #define LEDC_FADE_TIME (2000) // gamma factor for mathematical calculation -#define LEDC_GAMMA_FACTOR (2.6) +#define LEDC_GAMMA_FACTOR (2.6) // use gamma LUT for better performance instead of mathematical calculation (gamma factor) -#define USE_GAMMA_LUT 1 +#define USE_GAMMA_LUT 1 // fade LED pins const uint8_t ledPinR = 4; @@ -42,17 +42,13 @@ bool fade_in = true; // Custom Gamma LUT demonstration with 101 steps (Brightness 0 - 100% gamma correction look up table (gamma = 2.6)) // Y = B ^ 2.6 - Pre-computed LUT to save runtime computation static const float ledcGammaLUT[101] = { - 0.000000, 0.000006, 0.000038, 0.000110, 0.000232, 0.000414, 0.000666, 0.000994, 0.001406, 0.001910, - 0.002512, 0.003218, 0.004035, 0.004969, 0.006025, 0.007208, 0.008525, 0.009981, 0.011580, 0.013328, - 0.015229, 0.017289, 0.019512, 0.021902, 0.024465, 0.027205, 0.030125, 0.033231, 0.036527, 0.040016, - 0.043703, 0.047593, 0.051688, 0.055993, 0.060513, 0.065249, 0.070208, 0.075392, 0.080805, 0.086451, - 0.092333, 0.098455, 0.104821, 0.111434, 0.118298, 0.125416, 0.132792, 0.140428, 0.148329, 0.156498, - 0.164938, 0.173653, 0.182645, 0.191919, 0.201476, 0.211321, 0.221457, 0.231886, 0.242612, 0.253639, - 0.264968, 0.276603, 0.288548, 0.300805, 0.313378, 0.326268, 0.339480, 0.353016, 0.366879, 0.381073, - 0.395599, 0.410461, 0.425662, 0.441204, 0.457091, 0.473325, 0.489909, 0.506846, 0.524138, 0.541789, - 0.559801, 0.578177, 0.596920, 0.616032, 0.635515, 0.655374, 0.675610, 0.696226, 0.717224, 0.738608, - 0.760380, 0.782542, 0.805097, 0.828048, 0.851398, 0.875148, 0.899301, 0.923861, 0.948829, 0.974208, - 1.000000, + 0.000000, 0.000006, 0.000038, 0.000110, 0.000232, 0.000414, 0.000666, 0.000994, 0.001406, 0.001910, 0.002512, 0.003218, 0.004035, 0.004969, 0.006025, + 0.007208, 0.008525, 0.009981, 0.011580, 0.013328, 0.015229, 0.017289, 0.019512, 0.021902, 0.024465, 0.027205, 0.030125, 0.033231, 0.036527, 0.040016, + 0.043703, 0.047593, 0.051688, 0.055993, 0.060513, 0.065249, 0.070208, 0.075392, 0.080805, 0.086451, 0.092333, 0.098455, 0.104821, 0.111434, 0.118298, + 0.125416, 0.132792, 0.140428, 0.148329, 0.156498, 0.164938, 0.173653, 0.182645, 0.191919, 0.201476, 0.211321, 0.221457, 0.231886, 0.242612, 0.253639, + 0.264968, 0.276603, 0.288548, 0.300805, 0.313378, 0.326268, 0.339480, 0.353016, 0.366879, 0.381073, 0.395599, 0.410461, 0.425662, 0.441204, 0.457091, + 0.473325, 0.489909, 0.506846, 0.524138, 0.541789, 0.559801, 0.578177, 0.596920, 0.616032, 0.635515, 0.655374, 0.675610, 0.696226, 0.717224, 0.738608, + 0.760380, 0.782542, 0.805097, 0.828048, 0.851398, 0.875148, 0.899301, 0.923861, 0.948829, 0.974208, 1.000000, }; #endif @@ -69,13 +65,12 @@ void setup() { ledcAttach(ledPinG, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT); ledcAttach(ledPinB, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT); - -#if USE_GAMMA_LUT // Use default gamma LUT for better performance +#if USE_GAMMA_LUT // Use default gamma LUT for better performance ledcSetGammaTable(ledcGammaLUT, 101); #else // Use mathematical gamma correction (default, more flexible) - ledcSetGammaFactor(LEDC_GAMMA_FACTOR); // This is optional to set custom gamma factor (default is 2.8) + ledcSetGammaFactor(LEDC_GAMMA_FACTOR); // This is optional to set custom gamma factor (default is 2.8) #endif - + // Setup and start gamma curve fade on led (duty from 0 to 4095) ledcFadeGamma(ledPinR, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME); ledcFadeGamma(ledPinG, LEDC_START_DUTY, LEDC_TARGET_DUTY, LEDC_FADE_TIME);