1- // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
1+ // Copyright 2015-2023 Espressif Systems (Shanghai) PTE LTD
22//
33// Licensed under the Apache License, Version 2.0 (the "License");
44// you may not use this file except in compliance with the License.
@@ -37,33 +37,19 @@ typedef struct {
3737 int used_channels : LEDC_CHANNELS ; // Used channels as a bits
3838} ledc_periph_t ;
3939
40- /*
41- * LEDC Chan to Group/Channel/Timer Mapping
42- ** ledc: 0 => Group: 0, Channel: 0, Timer: 0
43- ** ledc: 1 => Group: 0, Channel: 1, Timer: 0
44- ** ledc: 2 => Group: 0, Channel: 2, Timer: 1
45- ** ledc: 3 => Group: 0, Channel: 3, Timer: 1
46- ** ledc: 4 => Group: 0, Channel: 4, Timer: 2
47- ** ledc: 5 => Group: 0, Channel: 5, Timer: 2
48- ** ledc: 6 => Group: 0, Channel: 6, Timer: 3
49- ** ledc: 7 => Group: 0, Channel: 7, Timer: 3
50- ** ledc: 8 => Group: 1, Channel: 0, Timer: 0
51- ** ledc: 9 => Group: 1, Channel: 1, Timer: 0
52- ** ledc: 10 => Group: 1, Channel: 2, Timer: 1
53- ** ledc: 11 => Group: 1, Channel: 3, Timer: 1
54- ** ledc: 12 => Group: 1, Channel: 4, Timer: 2
55- ** ledc: 13 => Group: 1, Channel: 5, Timer: 2
56- ** ledc: 14 => Group: 1, Channel: 6, Timer: 3
57- ** ledc: 15 => Group: 1, Channel: 7, Timer: 3
58- */
59-
6040ledc_periph_t ledc_handle ;
6141
42+ static bool fade_initialized = false;
43+
6244static bool ledcDetachBus (void * bus ){
63- ledc_channel_handle_t handle = (ledc_channel_handle_t )bus ;
45+ ledc_channel_handle_t * handle = (ledc_channel_handle_t * )bus ;
6446 ledc_handle .used_channels &= ~(1UL << handle -> channel );
6547 pinMatrixOutDetach (handle -> pin , false, false);
6648 free (handle );
49+ if (ledc_handle .used_channels == 0 ){
50+ ledc_fade_func_uninstall ();
51+ fade_initialized = false;
52+ }
6753 return true;
6854}
6955
@@ -77,7 +63,7 @@ bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution)
7763 }
7864
7965 perimanSetBusDeinit (ESP32_BUS_TYPE_LEDC , ledcDetachBus );
80- ledc_channel_handle_t bus = (ledc_channel_handle_t )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
66+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
8167 if (bus != NULL && !perimanSetPinBus (pin , ESP32_BUS_TYPE_INIT , NULL )){
8268 return false;
8369 }
@@ -111,12 +97,14 @@ bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution)
11197 };
11298 ledc_channel_config (& ledc_channel );
11399
114- ledc_channel_handle_t handle = malloc (sizeof (ledc_channel_handle_t ));
115-
116- handle -> pin = pin ,
117- handle -> channel = channel ,
118- handle -> channel_resolution = resolution ,
100+ ledc_channel_handle_t * handle = (ledc_channel_handle_t * )malloc (sizeof (ledc_channel_handle_t ));
119101
102+ handle -> pin = pin ;
103+ handle -> channel = channel ;
104+ handle -> channel_resolution = resolution ;
105+ #ifndef SOC_LEDC_SUPPORT_FADE_STOP
106+ handle -> lock = NULL ;
107+ #endif
120108 ledc_handle .used_channels |= 1UL << channel ;
121109
122110 if (!perimanSetPinBus (pin , ESP32_BUS_TYPE_LEDC , (void * )handle )){
@@ -128,7 +116,7 @@ bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution)
128116}
129117bool ledcWrite (uint8_t pin , uint32_t duty )
130118{
131- ledc_channel_handle_t bus = (ledc_channel_handle_t )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
119+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
132120 if (bus != NULL ){
133121
134122 uint8_t group = (bus -> channel /8 ), channel = (bus -> channel %8 );
@@ -150,7 +138,7 @@ bool ledcWrite(uint8_t pin, uint32_t duty)
150138
151139uint32_t ledcRead (uint8_t pin )
152140{
153- ledc_channel_handle_t bus = (ledc_channel_handle_t )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
141+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
154142 if (bus != NULL ){
155143
156144 uint8_t group = (bus -> channel /8 ), channel = (bus -> channel %8 );
@@ -161,7 +149,7 @@ uint32_t ledcRead(uint8_t pin)
161149
162150uint32_t ledcReadFreq (uint8_t pin )
163151{
164- ledc_channel_handle_t bus = (ledc_channel_handle_t )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
152+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
165153 if (bus != NULL ){
166154 if (!ledcRead (pin )){
167155 return 0 ;
@@ -175,7 +163,7 @@ uint32_t ledcReadFreq(uint8_t pin)
175163
176164uint32_t ledcWriteTone (uint8_t pin , uint32_t freq )
177165{
178- ledc_channel_handle_t bus = (ledc_channel_handle_t )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
166+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
179167 if (bus != NULL ){
180168
181169 if (!freq ){
@@ -222,7 +210,7 @@ uint32_t ledcWriteNote(uint8_t pin, note_t note, uint8_t octave){
222210
223211bool ledcDetach (uint8_t pin )
224212{
225- ledc_channel_handle_t bus = (ledc_channel_handle_t )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
213+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
226214 if (bus != NULL ){
227215 // will call ledcDetachBus
228216 return perimanSetPinBus (pin , ESP32_BUS_TYPE_INIT , NULL );
@@ -234,7 +222,7 @@ bool ledcDetach(uint8_t pin)
234222
235223uint32_t ledcChangeFrequency (uint8_t pin , uint32_t freq , uint8_t resolution )
236224{
237- ledc_channel_handle_t bus = (ledc_channel_handle_t )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
225+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
238226 if (bus != NULL ){
239227
240228 if (resolution > LEDC_MAX_BIT_WIDTH ){
@@ -262,12 +250,113 @@ uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution)
262250 return 0 ;
263251}
264252
253+ static IRAM_ATTR bool ledcFnWrapper (const ledc_cb_param_t * param , void * user_arg )
254+ {
255+ if (param -> event == LEDC_FADE_END_EVT ) {
256+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )user_arg ;
257+ #ifndef SOC_LEDC_SUPPORT_FADE_STOP
258+ portBASE_TYPE xTaskWoken = 0 ;
259+ xSemaphoreGiveFromISR (bus -> lock , & xTaskWoken );
260+ #endif
261+ if (bus -> fn ) {
262+ if (bus -> arg ){
263+ ((voidFuncPtrArg )bus -> fn )(bus -> arg );
264+ } else {
265+ bus -> fn ();
266+ }
267+ }
268+ }
269+ return true;
270+ }
271+
272+ static bool ledcFadeConfig (uint8_t pin , uint32_t start_duty , uint32_t target_duty , int max_fade_time_ms , void (* userFunc )(void * ), void * arg ){
273+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
274+ if (bus != NULL ){
275+
276+ #ifndef SOC_LEDC_SUPPORT_FADE_STOP
277+ #if !CONFIG_DISABLE_HAL_LOCKS
278+ if (bus -> lock == NULL ){
279+ bus -> lock = xSemaphoreCreateBinary ();
280+ if (bus -> lock == NULL ){
281+ log_e ("xSemaphoreCreateBinary failed" );
282+ return false;
283+ }
284+ xSemaphoreGive (bus -> lock );
285+ }
286+ //acquire lock
287+ if (xSemaphoreTake (bus -> lock , 0 ) != pdTRUE ){
288+ log_e ("LEDC Fade is still running on pin %u! SoC does not support stopping fade." , pin );
289+ return false;
290+ }
291+ #endif
292+ #endif
293+ uint8_t group = (bus -> channel /8 ), channel = (bus -> channel %8 );
294+
295+ // Initialize fade service.
296+ if (!fade_initialized ){
297+ ledc_fade_func_install (0 );
298+ fade_initialized = true;
299+ }
300+
301+ bus -> fn = (voidFuncPtr )userFunc ;
302+ bus -> arg = arg ;
303+
304+ ledc_cbs_t callbacks = {
305+ .fade_cb = ledcFnWrapper
306+ };
307+ ledc_cb_register (group , channel , & callbacks , (void * ) bus );
308+
309+ //Fixing if all bits in resolution is set = LEDC FULL ON
310+ uint32_t max_duty = (1 << bus -> channel_resolution ) - 1 ;
311+
312+ if ((target_duty == max_duty ) && (max_duty != 1 )){
313+ target_duty = max_duty + 1 ;
314+ }
315+ else if ((start_duty == max_duty ) && (max_duty != 1 )){
316+ start_duty = max_duty + 1 ;
317+ }
318+
319+ #if SOC_LEDC_SUPPORT_FADE_STOP
320+ ledc_fade_stop (group , channel );
321+ #endif
322+
323+ if (ledc_set_duty_and_update (group , channel , start_duty , 0 ) != ESP_OK ){
324+ log_e ("ledc_set_duty_and_update failed" );
325+ return false;
326+ }
327+ // Wait for LEDCs next PWM cycle to update duty (~ 1-2 ms)
328+ while (ledc_get_duty (group ,channel ) != start_duty );
329+
330+ if (ledc_set_fade_time_and_start (group , channel , target_duty , max_fade_time_ms , LEDC_FADE_NO_WAIT ) != ESP_OK ){
331+ log_e ("ledc_set_fade_time_and_start failed" );
332+ return false;
333+ }
334+ }
335+ else {
336+ log_e ("Pin %u is not attached to LEDC. Call ledcAttach first!" , pin );
337+ return false;
338+ }
339+ return true;
340+ }
341+
342+ bool ledcFade (uint8_t pin , uint32_t start_duty , uint32_t target_duty , int max_fade_time_ms ){
343+ return ledcFadeConfig (pin , start_duty , target_duty , max_fade_time_ms , NULL , NULL );
344+ }
345+
346+ bool ledcFadeWithInterrupt (uint8_t pin , uint32_t start_duty , uint32_t target_duty , int max_fade_time_ms , voidFuncPtr userFunc ){
347+ return ledcFadeConfig (pin , start_duty , target_duty , max_fade_time_ms , (voidFuncPtrArg )userFunc , NULL );
348+ }
349+
350+ bool ledcFadeWithInterruptArg (uint8_t pin , uint32_t start_duty , uint32_t target_duty , int max_fade_time_ms , void (* userFunc )(void * ), void * arg ){
351+ return ledcFadeConfig (pin , start_duty , target_duty , max_fade_time_ms , userFunc , arg );
352+ }
353+
265354static uint8_t analog_resolution = 8 ;
266355static int analog_frequency = 1000 ;
267356void analogWrite (uint8_t pin , int value ) {
268357 // Use ledc hardware for internal pins
269358 if (pin < SOC_GPIO_PIN_COUNT ) {
270- ledc_channel_handle_t bus = (ledc_channel_handle_t )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
359+ ledc_channel_handle_t * bus = (ledc_channel_handle_t * )perimanGetPinBus (pin , ESP32_BUS_TYPE_LEDC );
271360 if (bus == NULL && perimanSetPinBus (pin , ESP32_BUS_TYPE_INIT , NULL )){
272361 if (ledcAttach (pin , analog_frequency , analog_resolution ) == 0 ){
273362 log_e ("analogWrite setup failed (freq = %u, resolution = %u). Try setting different resolution or frequency" );
0 commit comments