Skip to content

Commit 31b07e0

Browse files
authored
LEDC Fade implementation (#8338)
* fade API + pointer fixes * Add fade api * Add fade example * update ledc docs * remove unused variables * fix path to example
1 parent 4f94154 commit 31b07e0

File tree

4 files changed

+271
-38
lines changed

4 files changed

+271
-38
lines changed

Diff for: cores/esp32/esp32-hal-ledc.c

+124-35
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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-
6040
ledc_periph_t ledc_handle;
6141

42+
static bool fade_initialized = false;
43+
6244
static 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
}
129117
bool 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

151139
uint32_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

162150
uint32_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

176164
uint32_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

223211
bool 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

235223
uint32_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+
265354
static uint8_t analog_resolution = 8;
266355
static int analog_frequency = 1000;
267356
void 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");

Diff for: cores/esp32/esp32-hal-ledc.h

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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.
@@ -26,11 +26,19 @@ typedef enum {
2626
NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G, NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B, NOTE_MAX
2727
} note_t;
2828

29+
typedef void (*voidFuncPtr)(void);
30+
typedef void (*voidFuncPtrArg)(void*);
31+
2932
typedef struct {
3033
uint8_t pin; // Pin assigned to channel
3134
uint8_t channel; // Channel number
3235
uint8_t channel_resolution; // Resolution of channel
33-
} *ledc_channel_handle_t;
36+
voidFuncPtr fn;
37+
void* arg;
38+
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
39+
SemaphoreHandle_t lock; //xSemaphoreCreateBinary
40+
#endif
41+
} ledc_channel_handle_t;
3442

3543
//channel 0-15 resolution 1-16bits freq limits depend on resolution
3644
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);
@@ -42,6 +50,10 @@ uint32_t ledcReadFreq(uint8_t pin);
4250
bool ledcDetach(uint8_t pin);
4351
uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution);
4452

53+
//Fade functions
54+
bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);
55+
bool ledcFadeWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void));
56+
bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void*), void * arg);
4557

4658
#ifdef __cplusplus
4759
}

Diff for: docs/source/api/ledc.rst

+64-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ ESP32 SoC Number of LEDC channels
1414
========= =======================
1515
ESP32 16
1616
ESP32-S2 8
17-
ESP32-C3 6
1817
ESP32-S3 8
18+
ESP32-C3 6
19+
ESP32-C6 6
1920
========= =======================
2021

2122
Arduino-ESP32 LEDC API
@@ -51,6 +52,9 @@ This function is used to set duty for the LEDC pin.
5152
* ``pin`` select LEDC pin.
5253
* ``duty`` select duty to be set for selected LEDC pin.
5354

55+
This function will return ``true`` if setting duty is successful.
56+
If ``false`` is returned, error occurs and duty was not set.
57+
5458
ledcRead
5559
********
5660

@@ -143,6 +147,60 @@ This function is used to set frequency for the LEDC pin.
143147
This function will return ``frequency`` configured for the LEDC channel.
144148
If ``0`` is returned, error occurs and the LEDC channel frequency was not set.
145149

150+
ledcFade
151+
********
152+
153+
This function is used to setup and start fade for the LEDC pin.
154+
155+
.. code-block:: arduino
156+
157+
bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);
158+
159+
* ``pin`` select LEDC pin.
160+
* ``start_duty`` select starting duty of fade.
161+
* ``target_duty`` select target duty of fade.
162+
* ``max_fade_time_ms`` select maximum time for fade.
163+
164+
This function will return ``true`` if configuration is successful.
165+
If ``false`` is returned, error occurs and LEDC fade was not configured / started.
166+
167+
ledcFadeWithInterrupt
168+
*********************
169+
170+
This function is used to setup and start fade for the LEDC pin with interrupt.
171+
172+
.. code-block:: arduino
173+
174+
bool ledcFadeWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void));
175+
176+
* ``pin`` select LEDC pin.
177+
* ``start_duty`` select starting duty of fade.
178+
* ``target_duty`` select target duty of fade.
179+
* ``max_fade_time_ms`` select maximum time for fade.
180+
* ``userFunc`` funtion to be called when interrupt is triggered.
181+
182+
This function will return ``true`` if configuration is successful and fade start.
183+
If ``false`` is returned, error occurs and LEDC fade was not configured / started.
184+
185+
ledcFadeWithInterruptArg
186+
************************
187+
188+
This function is used to setup and start fade for the LEDC pin with interrupt using arguments.
189+
190+
.. code-block:: arduino
191+
192+
bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void*), void * arg);
193+
194+
* ``pin`` select LEDC pin.
195+
* ``start_duty`` select starting duty of fade.
196+
* ``target_duty`` select target duty of fade.
197+
* ``max_fade_time_ms`` select maximum time for fade.
198+
* ``userFunc`` funtion to be called when interrupt is triggered.
199+
* ``arg`` pointer to the interrupt arguments.
200+
201+
This function will return ``true`` if configuration is successful and fade start.
202+
If ``false`` is returned, error occurs and LEDC fade was not configured / started.
203+
146204
analogWrite
147205
***********
148206

@@ -184,6 +242,11 @@ This function is used to set frequency for selected analogWrite pin.
184242
Example Applications
185243
********************
186244

245+
LEDC fade example:
246+
247+
.. literalinclude:: ../../../libraries/ESP32/examples/AnalogOut/LEDCFade/LEDCFade.ino
248+
:language: arduino
249+
187250
LEDC software fade example:
188251

189252
.. literalinclude:: ../../../libraries/ESP32/examples/AnalogOut/LEDCSoftwareFade/LEDCSoftwareFade.ino

0 commit comments

Comments
 (0)