Generación de señal PWM con ESP32: conceptos básicos
Si alguna vez has trabajado con Arduino, ya sabes lo fácil que es generar una señal PWM usando la función analogWrite()
. Sin embargo, al utilizar el ESP32, el proceso se vuelve un poco más complejo, pero a la vez más poderoso. Esta guía te llevará a través de los conceptos básicos de la modulación por ancho de pulso (PWM) en el ESP32, cubriendo desde los pines disponibles hasta ejemplos prácticos que puedes implementar en tus proyectos.
Prepárate para explorar cómo este versátil microcontrolador puede mejorar tus proyectos electrónicos. Aprenderás a manejar la PWM de manera efectiva, aprovechando al máximo las capacidades del ESP32.
- Pines de PWM en el ESP32
- Frecuencia PWM
- Resolución PWM
- Ciclo de trabajo
- Canales PWM
- Elección de Frecuencia y Resolución PWM
- Generando una señal PWM con la biblioteca LEDC
- Ejemplo 1 – Atenuando un LED
- Ejemplo 2 – Mismas señales PWM en múltiples GPIOs
- Ejemplo 3 – Atenuando un LED usando un potenciómetro
Pines de PWM en el ESP32
El ESP32 permite la salida de PWM en casi todos los pines GPIO, excepto en cuatro que son de solo entrada. Esto significa que puedes utilizar una amplia gama de pines para tus proyectos.
En el corazón del ESP32 hay dos periféricos de PWM: el Periférico de Control de LED (LEDC) y el Periférico de Modulación de Ancho de Pulso para Control de Motores (MCPWM).
- MCPWM: Especialmente diseñado para el control de motores, ofrece características adicionales como zonas muertas y frenado automático.
- LEDC: Optimizado para el control de LEDs, incluye funciones como atenuación automática y es versátil para otros usos de PWM.
Este tutorial se centrará principalmente en el periférico LEDC, que es ideal para aplicaciones de bajo consumo energético y control de iluminación.
Frecuencia PWM
El periférico LEDC utiliza un temporizador para generar señales PWM. Este temporizador cuenta hasta un valor máximo y luego se reinicia, comenzando un nuevo ciclo. El tiempo que toma para llegar a este valor máximo define la frecuencia PWM, medida en Hertz (Hz).
Por ejemplo:
- Una frecuencia de 1 Hz significa que el temporizador tarda 1 segundo en contar de 0 al valor máximo.
- Una frecuencia de 1000 Hz implica que solo necesita 1 milisegundo para completar el mismo ciclo.
El ESP32 puede generar señales PWM con frecuencias de hasta 40 MHz, lo que permite aplicaciones de alta velocidad y precisión.
Resolución PWM
La resolución PWM determina el valor máximo que el temporizador puede contar antes de reiniciarse. Si la resolución es de “n” bits, el temporizador cuenta de 0 a 2n-1. Por ejemplo:
- Con una frecuencia de 1 Hz y una resolución de 8 bits, el temporizador cuenta de 0 a 255.
- Con una frecuencia de 1 Hz y una resolución de 16 bits, cuenta de 0 a 65,535.
A mayor resolución, mayor precisión en la modulación del ciclo de trabajo, lo que se traduce en un control más fino sobre la intensidad de los LEDs o la velocidad de los motores. La resolución PWM del ESP32 puede ajustarse de 1 a 16 bits, permitiendo hasta 65,536 niveles de ciclo de trabajo.
Ciclo de trabajo
El ciclo de trabajo indica cuántos ticks del temporizador el PWM se mantendrá alto antes de caer a bajo. Este valor se almacena en el registro de captura/comparación del temporizador (CCR).
El proceso es el siguiente:
- Cuando el temporizador se reinicia, la salida PWM se establece en alto.
- Cuando el temporizador alcanza el valor en el CCR, la salida PWM se pone baja.
- El temporizador sigue contando, y una vez que llega al valor máximo, la salida vuelve a alta.
Por ejemplo, para un PWM de 1000 Hz, con 8 bits de resolución y un ciclo de trabajo del 75%, el valor en el CCR sería 192 (75% de 255). Esto significa que la salida estará alta durante 192 ticks y baja durante 63 ticks.
Canales PWM
Los canales representan señales de salida PWM únicas. El ESP32 cuenta con 16 canales, permitiendo generar hasta 16 formas de onda PWM diferentes. Estos canales se dividen en dos grupos: 8 de alta velocidad y 8 de baja velocidad.
- Canales de alta velocidad: Implementados en hardware, permiten cambios automáticos y sin errores en el ciclo de trabajo.
- Canales de baja velocidad: Dependen del software para modificar el ciclo de trabajo.
Cada grupo de canales comparte 4 temporizadores, lo que significa que cada par de canales utilizará el mismo temporizador para la frecuencia, aunque el ciclo de trabajo puede ajustarse independientemente. Esto permite flexibilidad en la asignación de pines a canales específicos.
Elección de Frecuencia y Resolución PWM
El ESP32 puede generar señales PWM de hasta 40 MHz y la resolución puede ser de 1 a 16 bits. Sin embargo, no puedes establecer ambas características al máximo simultáneamente, debido a las limitaciones del reloj.
Para ilustrarlo:
- Si el reloj está a 40 MHz, la frecuencia PWM también estará limitada a 40 MHz.
- La resolución determina cuántas divisiones se pueden hacer en un ciclo PWM.
Las relaciones importantes a considerar son:
- La frecuencia PWM multiplicada por 2resolución no debe exceder la velocidad del reloj.
- La frecuencia y la resolución son interdependientes; a mayor frecuencia, menor resolución y viceversa.
Como regla general, el reloj de baja velocidad del LEDC es de 80 MHz. Debes mantener la relación mencionada por debajo de este valor.
Generando una señal PWM con la biblioteca LEDC
La biblioteca LEDC incluida en el núcleo de Arduino para ESP32 facilita la gestión del PWM. Aunque fue diseñada para controlar LEDs, también se puede utilizar en otras aplicaciones, como motores y altavoces piezoeléctricos.
Los pasos para generar una señal PWM son:
- Seleccionar un canal PWM: Hay 16 canales disponibles, del 0 al 15.
- Determinar la frecuencia PWM: Para la aplicación de atenuación de LED, 500 Hz es suficiente.
- Definir la resolución PWM: Esta puede ser de 1 a 16 bits, con la cantidad de niveles de ciclo de trabajo determinada por 2resolución.
- Elegir los pines GPIO: Selecciona uno o más pines en el ESP32 para la salida de la señal PWM.
- Unir los pines al canal PWM: Usa la función
ledcAttachChannel(pin, freq, resolution, channel)
para enlazarlos. - Establecer el ciclo de trabajo: Utiliza
ledcWriteChannel(channel, dutycycle)
para definir el ciclo de trabajo deseado.
Ejemplo 1 – Atenuando un LED
Este ejemplo te muestra cómo atenuar un LED utilizando PWM, perfecto para iniciar con la generación de señales en el ESP32.
Conexión
Conecta un LED con una resistencia limitadora de corriente (330 Ω) a la placa. El ánodo del LED va a un pin GPIO (por ejemplo, el 18) y el cátodo a tierra.
Código
Introduce el siguiente código en tu IDE de Arduino:
const int PWM_CHANNEL = 0; // 16 canales disponibles
const int PWM_FREQ = 500; // Frecuencia de 500 Hz
const int PWM_RESOLUTION = 8; // Resolución de 8 bits
const int MAX_DUTY_CYCLE = (int)(pow(2, PWM_RESOLUTION) - 1);
const int LED_OUTPUT_PIN = 18;
const int DELAY_MS = 4; // Retardo entre incrementos
void setup() {
// Conecta el GPIO al canal
ledcAttachChannel(LED_OUTPUT_PIN, PWM_FREQ, PWM_RESOLUTION, PWM_CHANNEL);
}
void loop() {
// Aumenta el ciclo de trabajo
for (int dutyCycle = 0; dutyCycle = 0; dutyCycle--) {
ledcWriteChannel(PWM_CHANNEL, dutyCycle);
delay(DELAY_MS);
}
}
Prueba del ejemplo
Sube el código a tu ESP32 y observa cómo la intensidad del LED cambia suavemente de apagado a encendido y viceversa.
Explicación del código:
Se definen varias constantes para configurar las características de PWM. Por ejemplo, PWM_CHANNEL
se establece en 0, indicando el primer canal. La frecuencia se establece en 500 Hz, y la resolución en 8 bits.
La constante MAX_DUTY_CYCLE
se calcula como 2PWM_RESOLUTION-1. Así, el valor máximo posible de ciclo de trabajo se calcula como 255.
El pin del LED se define como LED_OUTPUT_PIN
, y el retardo entre incrementos se establece en 4 ms.
La función ledcAttachChannel()
se utiliza en la configuración para conectar el pin GPIO al canal PWM correspondiente.
Ejemplo 2 – Mismas señales PWM en múltiples GPIOs
Es posible obtener la misma señal PWM en varios pines GPIO al adjuntarlos al mismo canal. Esto es útil si deseas controlar varios LEDs de manera sincronizada.
Conexión de tres LEDs
Agrega dos LEDs más a tu circuito, conectándolos a los pines GPIO 19 y 21.
Código para tres LEDs
Modifica el ejemplo anterior para atenuar tres LEDs utilizando la misma señal PWM:
const int PWM_CHANNEL = 0; // 16 canales disponibles
const int PWM_FREQ = 500; // Frecuencia de 500 Hz
const int PWM_RESOLUTION = 8; // Resolución de 8 bits
const int MAX_DUTY_CYCLE = (int)(pow(2, PWM_RESOLUTION) - 1);
const int LED_1_OUTPUT_PIN = 18;
const int LED_2_OUTPUT_PIN = 19;
const int LED_3_OUTPUT_PIN = 21;
const int DELAY_MS = 4; // Retardo entre incrementos
void setup() {
// Conecta los GPIOs al mismo canal
ledcAttachChannel(LED_1_OUTPUT_PIN, PWM_FREQ, PWM_RESOLUTION, PWM_CHANNEL);
ledcAttachChannel(LED_2_OUTPUT_PIN, PWM_FREQ, PWM_RESOLUTION, PWM_CHANNEL);
ledcAttachChannel(LED_3_OUTPUT_PIN, PWM_FREQ, PWM_RESOLUTION, PWM_CHANNEL);
}
void loop() {
// Aumenta el ciclo de trabajo
for (int dutyCycle = 0; dutyCycle = 0; dutyCycle--) {
ledcWriteChannel(PWM_CHANNEL, dutyCycle);
delay(DELAY_MS);
}
}
Prueba del ejemplo con tres LEDs
Sube este código a tu ESP32 y verás que los tres LEDs parpadean simultáneamente, ya que todos los pines están emitiendo la misma señal PWM.
Explicación del código para tres LEDs:
La principal diferencia en este código es la adición de dos constantes adicionales para los pines de los nuevos LEDs. Las funciones ledcAttachChannel()
se llaman tres veces en la sección de configuración para unir cada pin al canal PWM.
Ejemplo 3 – Atenuando un LED usando un potenciómetro
Este ejemplo muestra cómo atenuar un LED utilizando un potenciómetro para controlar el ciclo de trabajo de forma dinámica.
Conexión del potenciómetro
Retira los LEDs adicionales y añade un potenciómetro. Conecta un pin externo a 3.3V, otro a GND, y el pin del medio (wiper) a GPIO 34.
Código para el potenciómetro
El siguiente código te permitirá controlar la intensidad del LED con el potenciómetro:
const int PWM_CHANNEL = 0; // 16 canales disponibles
const int PWM_FREQ = 500; // Frecuencia de 500 Hz
const int PWM_RESOLUTION = 8; // Resolución de 8 bits
const int MAX_DUTY_CYCLE = (int)(pow(2, PWM_RESOLUTION) - 1);
const int LED_OUTPUT_PIN = 18;
const int POT_PIN = 34;
const int DELAY_MS = 100; // Retardo entre ajustes
void setup() {
// Conecta el GPIO al canal
ledcAttachChannel(LED_OUTPUT_PIN, PWM_FREQ, PWM_RESOLUTION, PWM_CHANNEL);
}
void loop() {
int dutyCycle = map(analogRead(POT_PIN), 0, 4095, 0, MAX_DUTY_CYCLE);
ledcWriteChannel(PWM_CHANNEL, dutyCycle);
delay(DELAY_MS);
}
Prueba del ejemplo con potenciómetro
Prueba girando el potenciómetro en ambas direcciones y observa cómo la intensidad del LED varía suavemente desde apagado hasta completamente encendido.
Explicación del código con potenciómetro:
Se define la constante POT_PIN
que se conecta al GPIO 34 para leer el valor del potenciómetro. En el bucle, el valor analógico leído se transforma en un ciclo de trabajo adecuado utilizando map()
, permitiendo que el LED ajuste su brillo en función de la posición del potenciómetro.
El uso de ledcWriteChannel()
aplica este ciclo de trabajo directamente a la señal PWM, logrando un control dinámico y efectivo sobre la intensidad del LED.
Deja una respuesta
Estos temas te pueden interesar