Muy buenas a todos y todas a la vuelta al canal!!
El periférico LEDC está diseñado principalmente para controlar la intensidad de LEDs, aunque también pero también es muy útil para generar señales PWM para otros propósitos tales como controlar motores DC.
El ESP32-C3 cuenta con 6 canales que pueden generar formas de onda independientes, ideales para controlar LEDs o dispositivos que necesiten PWM tan solo cambiando algunos parámetros de la configuración como el temporizador,la confirmación del canal y el cambio de la señal PWM.
¿Cuando se implementó ledc?
La API de LEDC para Arduino-ESP32 está documentada y disponible como parte del núcleo (core) de ESP32 en Arduino.
La versión actual (a partir de la 3.0.0) incluye cambios importantes en la sintaxis, como la sustitución de:
ledcAttachPin() // V < 3.0.0
por
ledcAttach() // Versión > 3.0.0
Para la versión 3.0.0 del core de ESP32 para Arduino, ya no se usa la función ledcSetup() ni la función ledcAttachPin(), sino que se recomienda utilizar la función ledcAttach(pin, freq, resolution)![]() |
| ESP32 C3 mini 0.42" pinout |
Funciones y definiciones relacionadas
1. ledcAttach()
Función:
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);
La función ledcAttach configura un pin para PWM con la frecuencia (uint32_t freq) y resolución (uint8_t resolution)indicadas.
El canal LEDC se selecciona automáticamente.
Devuelve true si la configuración es exitosa.
2. ledcAttachChannel()
Función:
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);
Con ledcAttachChannel configuramos un pin para PWM con la frecuencia, resolución y canal específicos.
Devuelve true si la configuración es exitosa.
3. ledcWrite()
Función:
void ledcWrite(uint8_t pin, uint32_t duty);
Establece el ciclo de trabajo (duty cycle) para el pin LEDC.
El valor de duty depende de la resolución: para 10 bits, el rango es de 0 a 1023.
4. ledcRead()
Función:
uint32_t ledcRead(uint8_t pin);
Lee el ciclo de trabajo configurado para el pin.
5. ledcReadFreq()
Función:
uint32_t ledcReadFreq(uint8_t pin);
Con ledcReadFreq lee la frecuencia configurada para el pin que hemos seleccionado.
Relación entre frecuencia y resolución
6. ledcDetach()
Función:
bool ledcDetach(uint8_t pin);
Desvincula el pin del periférico LEDC para usarlo como GPIO digital.
7. ledcChangeFrequency()
Función:
uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution);
Cambia la frecuencia y resolución de un pin LEDC ya configurado.
8. ledcFade()
Función:
bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);
Configura e inicia un desvanecimiento (fade) automático por hardware desde un valor inicial hasta un valor objetivo.
Devuelve true si se inicia correctamente.
9. ledcWriteTone()
Función:
uint32_t ledcWriteTone(uint8_t pin, uint32_t freq);
Genera un tono PWM al 50% en la frecuencia especificada.
10. ledcWriteNote()
Función:
uint32_t ledcWriteNote(uint8_t pin, note_t note, uint8_t octave);
Por último tenemos ledcWriteNote que genera un tono a partir de una nota musical y una octava.
LEDC ¿es mejor o peor que analogWrite?
En ESP32, analogWrite() es una capa de abstracción sobre LEDC.
La principal diferencia es que analogWrite() simplifica el uso, pero limita el control.
| Característica | analogWrite() | LEDC nativo |
|---|---|---|
| Resolución | Fija a 8 bits (0-255) | Configurable (1-14 bits en ESP32-C3) |
| Frecuencia | Fija (~5 kHz) | Totalmente configurable |
| Gestión de canales | Automática, puede causar conflictos | Manual, 6 canales independientes |
| Funciones avanzadas | No disponible | Fade por hardware, interrupciones |
| Complejidad de código | Muy baja | Media |
Conclusión: analogWrite() es perfecto para prototipos rápidos y proyectos simples.
LEDC es necesario cuando se necesita precisión, frecuencias personalizadas o múltiples canales PWM sin interferencias .
Relación entre frecuencia y resolución
En el ESP32-C3, la frecuencia y la resolución están interrelacionadas. A mayor frecuencia, menor resolución máxima disponible, y viceversa.
Ejemplos prácticos:
- 5 kHz con 13 bits: 8192 niveles de intensidad.
- 40 MHz con 1 bit: Solo 50% fijo.
Si se intenta configurar una combinación no soportada, la API mostrará un error:
E (196) ledc: requested frequency and duty resolution cannot be achieved, try reducing freq_hz or duty_resolution. div_param=128
Ejemplos para ESP32-C3 con pantalla OLED 0.42"
La placa "ESP32-C3 OLED 0.42" tiene la pantalla conectada por I2C: en el pin GPIO5 corresponde a SDA y el pin GPIO6 para SCL.
Ejemplo 1: Uso de analogWrite (simplificado)
Primero veamos un ejemplo clásico programa con analogWrite()
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define OLED_SDA 5
#define OLED_SCL 6
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define LED_PIN 8
void setup() {
Wire.begin(OLED_SDA, OLED_SCL);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
pinMode(LED_PIN, OUTPUT);
}
void loop() {
for (int brightness = 0; brightness <= 255; brightness += 5) {
analogWrite(LED_PIN, brightness);
display.clearDisplay();
display.setCursor(30, 30);
display.print("AnalogWrite: ");
display.println(brightness);
int porcentaje = map(brightness, 0, 255, 0, 100);
display.setCursor(30, 40);
display.print("Inten.: ");
display.print(porcentaje);
display.println("%");
display.display();
delay(30);
}
for (int brightness = 255; brightness >= 0; brightness -= 5) {
analogWrite(LED_PIN, brightness);
display.clearDisplay();
display.setCursor(30, 30);
display.print("AnalogWrite: ");
display.println(brightness);
int porcentaje = map(brightness, 0, 255, 0, 100);
display.setCursor(30, 40);
display.print("Inten.: ");
display.print(porcentaje);
display.println("%");
display.display();
delay(30);
}
}
Ejemplo 2: Ledc PWM en LED y mostrar valor en OLED (I2C)
En este ejemplo tendremos la salida del valor Ledc en la pantalla OLED 0.42". El pin 8 del LED builtin el estado de Ledc.
NOTA: el encendido del LED builtin esta invertido, es decir cuando la señal Ledc esta en 0 el LED builtin estará a su máxima intensidad y apagado en su valor más alto.
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define OLED_SDA 5
#define OLED_SCL 6
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define LED_PIN 8
#define PWM_FREQ 5000
#define PWM_RES 10
#define MAX_DUTY ((1 << PWM_RES) - 1) // 1023 para 10 bits
int duty = 0;
int step = 5;
void setup() {
Wire.begin(OLED_SDA, OLED_SCL);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
ledcAttach(LED_PIN, PWM_FREQ, PWM_RES);
ledcWrite(LED_PIN, 0);
}
void loop() {
duty += step;
if (duty >= MAX_DUTY || duty <= 0) step = -step;
ledcWrite(LED_PIN, duty);
display.clearDisplay();
display.setCursor(30, 30);
display.print("Duty: ");
display.setCursor(30, 40);
display.print(duty);
display.print(" / ");
display.println(MAX_DUTY);
int porcentaje = map(duty, 0, MAX_DUTY, 0, 100);
display.setCursor(30, 50);
display.print("Intensidad: ");
display.print(porcentaje);
display.println("%");
display.display();
delay(20);
}
Ejemplo 3: Ledc fade automático por hardware y mostrar estado
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define OLED_SDA 5
#define OLED_SCL 6
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define LED_PIN 8
#define PWM_CHANNEL 0
#define PWM_FREQ 5000
#define PWM_RES 10
#define MAX_DUTY ((1 << PWM_RES) - 1)
void setup() {
Wire.begin(OLED_SDA, OLED_SCL);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
ledcAttachChannel(LED_PIN, PWM_FREQ, PWM_RES, PWM_CHANNEL);
ledcWrite(LED_PIN, 0);
display.display();
delay(1000);
}
void loop() {
display.clearDisplay();
display.setCursor(30, 30);
display.println("Fade IN");
display.display();
ledcFade(LED_PIN, 0, MAX_DUTY, 2000);
delay(2500);
display.clearDisplay();
display.setCursor(30, 30);
display.println("Fade OUT");
display.display();
ledcFade(LED_PIN, MAX_DUTY, 0, 2000);
delay(2500);
}
Ejemplo 4: Ledc PWM para ESC de motor brushed
Para controlar un ESC, se necesita una señal PWM de 50 Hz con pulsos entre 1000 µs y 2000 µs.
#define ESC_PIN 8
#define PWM_FREQ 50
#define PWM_RES 14
#define MAX_DUTY ((1 << PWM_RES) - 1)
// Para 14 bits: 16384 = 100% (20 ms)
// 1000 µs = 1 ms → (1/20)16384 = 819
// 2000 µs = 2 ms → (2/20)16384 = 1638
void setup() {
ledcAttach(ESC_PIN, PWM_FREQ, PWM_RES);
ledcWrite(ESC_PIN, 819); // Neutro
delay(3000);
}
void loop() {
ledcWrite(ESC_PIN, 819); // Neutro
delay(2000);
ledcWrite(ESC_PIN, 1638); // Máximo
delay(2000);
}
También puedes revisar el repositorio supermini_esp32c3_sketches para ejemplos muy variados y completos.
Conclusión sobre LEDC en el ESP32-C3
El periférico LEDC del ESP32-C3 es una herramienta extremadamente versátil y potente para generar señales PWM, muy superior a la función analogWrite() que muchos conocen de Arduino.
Mientras que analogWrite() está limitada a 8 bits de resolución y frecuencias fijas, LEDC ofrece un control total con resoluciones de hasta 14 bits y frecuencias completamente configurables.
Esto hace que Ledc sea ideal para aplicaciones que requieren precisión, como el control de motores DC, servomotores, atenuación de LEDs profesionales o incluso generación de tonos y notas musicales.
Lo bueno:
El ESP32-C3 dispone de 6 canales independientes, lo que permite manejar múltiples dispositivos PWM simultáneamente sin interferencias.
Una de sus características más destacadas es la capacidad de realizar desvanecimientos (fade) automáticos por hardware, lo que descarga al procesador de esta tarea y permite transiciones suaves sin necesidad de interrupciones constantes.
Lo malo:
Sin embargo, hay que tener en cuenta algunas limitaciones importantes. El ESP32-C3 solo funciona en modo "low speed", a diferencia de otros ESP32 que también tienen modo "high speed".
Además, existe una relación de compromiso entre frecuencia y resolución: a mayor frecuencia, menor resolución disponible, y viceversa. Por ejemplo, a 5 kHz se puede alcanzar 13 bits de resolución, pero a 40 MHz apenas se obtiene 1 bit.
Aquí tienes otros enlaces de este blog relacionados con el microcontrolador ESP32 y Arduino:
- ESP32-C3 con pantalla Oled 0.42" con ejemplos
- ESP32-C3 Super Mini con OLED SH1106: Tutorial de menús y controles físicos
- ESP32 con OLED 128x32: Velocímetro y dashboard para vehículos RC


































