¿Qué es la modulación por ancho de pulso (PWM)?
La modulación de ancho de pulso, o PWM, es una técnica para obtener resultados analógicos utilizando dispositivos digitales. El control digital se utiliza para crear una onda cuadrada, una señal que cambia entre encendido y apagado. En una tarjeta como Arduino, este patrón de encendido y apagado puede simular voltajes entre Vcc de la placa (p. ej., 5 V en UNO, 3,3 V en una placa MKR) y apagado (0 voltios) al modificar el periodo de tiempo que pasa la señal encendida respecto del tiempo que la señal pasa apagada.
Una señal pulsada (PWM) es suficiente para emular una señal analógica en muchas aplicaciones. Por ejemplo, podemos variar la intensidad luminosa en un LED mediante un PWM. El LED realmente se enciende y apaga varias veces por segundo, pero este parpadeo es tan rápido que el ojo no lo aprecia. El efecto global percibido es que el LED brilla con menor intensidad.
Otro ejemplo, al variar la velocidad de un motor DC con un PWM, en la mayoría de los casos la inercia del motor se encargará de que el efecto del PWM sea despreciable.

Lecturas sugeridas
Puedes considerar revisar los siguientes tutoriales:
Ciclo de trabajo
Utilizareoms el concepto de ciclo de trabajo (duty cycle) para describir específicamente el porcentaje de tiempo que una señal digital se encuentra en un estado alto en un intervalo o período de tiempo. Este período es el inverso de la frecuencia de la forma de onda. Si una señal digital pasa la mitad del tiempo encendido y la otra mitad apagada, diríamos que la señal digital tiene un ciclo de trabajo del 50% y se asemeja a una onda cuadrada ideal.
A continuación se encuentra una imagen que ilustra estos tres escenarios:

Si el ciclo de trabajo es de un 100%, sería lo mismo que si ajustáramos la tensión de salida a 5 voltios (alto) constante. Con un ciclo de trabajo en un 0%, sería lo mismo que conectáramos la señal a tierra (GND).
Ejemplos
Puedes controlar el brillo de un LED ajustando el ciclo de trabajo de la señal que se conectara a dicho LED.

En un LED RGB (color rojo, verde, azul), puedes controlar la intensidad de luz de los tres colores que desees mediante la modulación por ancho de pulsos.

Si los tres colores se encuentran controlados por el mismo porcentaje de ciclos de trabajo PWM, el resultado será una luz blanca. Igualmente, si mezclamos los colores azul y verde, obtendremos en el LED un color verde azulado. Como ejemplo más complejo, aumenta el ciclo de trabajo en un 100% para el color rojo, un ciclo de trabajo del 50% para el color verde y un 0% para el color azul, con esta configuración conseguirás un color anaranjado.

La frecuencia de la onda cuadrada no necesita ser muy alta cuando se controlan LEDs para conseguir un efecto de atenuación (Dimmer). Una onda de ciclo de trabajo del 20% a 1 Hz se mostrara encendida y apagada a la percepción de tus ojos, un ciclo de trabajo del 20% a 100 Hz o superior solo se percibirá por nosotros como una luz más tenue en comparación a totalmente encendido. Esencialmente, el periodo no debe ser demasiado grande si lo que deseas lograr es un efecto de oscurecimiento de los LEDs.
También puedes utilizar la modulación por ancho de pulsos para controlar el ángulo de un motor servo conectado a algo mecánico como un brazo robótico. Los motores servo tienen un eje que gira a la posición específica basada en su línea de control. Existen motores servos con con angulos de giro que van de 0 a180° y los de giro continuo o 360°. Frecuencia/período son específicos para controlar un motor servo específico. Un motor servo típico espera ser actualizado cada 20 ms con un pulso entre 1 ms y 2 ms, o en otras palabras, entre un ciclo de trabajo 5 y 10% a una frecuencia de 50 Hz. Con un impulso de 1,5 ms, el motor servo estará en la posición de 90 grados natural. Con un pulso de 1 ms, el motor servo estará en la posición de 0 grados, y con un pulso de 2 ms estará en una posición de 180 grados. Si quieres aprender como utilizar un motor servo con Arduino puedes consultar este tutorial

Salidas Analógicas (PWM) en Arduino
Arduino implementa por hardware salidas PWM en varios de sus pines, los cuales aparecen identificados en la serigrafía de la placa con el símbolo “~” junto al número del pin. También podemos emular señales PWM por software, sin embargo, esto generará una carga de trabajo adicional para el procesador.
En Arduino Uno, disponemos de 6 salidas PWM de 8bits en los pines 3, 5, 6, 9, 10 y 11.
En Arduino Mega disponemos de 15 salidas PWM de 8bis en los pines 2 a 13 y 44 a 46
Arduino Due cuenta con 13 salidas PWM de 8bits en los pins 2 a 13. Además, esta placa incorpora dos salidas analogicas discretizadas (DAC) con resolución de 12bits (4096 niveles)
Una resolución de 8bits en una salida PWM significa que tenemos 256 niveles. Es decir, indicamos el Duty cycle mediante un número de 0 a 255.
Los Timer PWM por Hardware
Los pines PWM del Arduino utilizan los Timer para generar la onda de salida. Cada Timer da servicio a 2 o 3 salidas PWM. Para ello el microcontrolador dispone de un registro de comparación por cada salida. Cuando el tiempo alcanza el valor del registro de comparación, la salida invierte su valor.
Cada salida conectada a un mismo Timer comparte la misma frecuencia, aunque pueden tener distintos ciclos de carga (Duty cycles), dependiendo del valor de su registro de comparación.
Asociación entre Timer y Salidas PWM
En el caso de Arduino Uno
- El Timer0 controla las salidas PWM 5 y 6.
- El Timer1 controla las salidas PWM 9 y 10.
- El Timer2 controla las salidas PWM 3 y 11.
Mientras que en Arduino Mega
- El Timer0 controla las salidas PWM 4 y 13.
- El Timer1 controla las salidas PWM 11 y 12.
- El Timer2 controla las salidas PWM 9 y 10.
- El Timer3 controla las salidas PWM 2, 3 y 5.
- El Timer4 controla las salidas PWM 6, 7 y 8.
- El Timer5 controla las salidas PWM 44, 45 y 46.
Frecuencia del PWM
La frecuencia de cada PWM depende de las características del temporizador al que está conectado, y de un registro de preescalado, que divide el tiempo por un número entero.
La frecuencia de los PWM se puede modificar cambiando el preescalado de los Timer correspondientes.
En el Arduino Uno, se disponen de tres temporizadores.
- Timer0, con una frecuencia de 62500Hz, y preescalados de 1, 8, 64, 256 y 1024.
- Timer1, con una frecuencia de 31250Hz, y preescalados de 1, 8, 64, 256, y 1024.
- Timer2, con una frecuencia de 31250Hz, y preescalados de 1, 8, 32, 64, 128, 256, y 1024.
Arduino Mega añade tres temporizadores más
- Timer3, 4 y 5, con una frecuencia de 31250Hz, y preescalados de 1, 8, 64, 256, and 1024.
La frecuencia estándar para las salidas PWM en Arduino Uno es de 490Hz para todos los pines, excepto para el 5 y 6 cuya frecuencia es de 980Hz. En cuanto a Arduino Mega, la frecuencia estándar es de 490Hz para todos los pines, excepto para el 4 y 13 cuya frecuencia es de 980Hz.
El uso de los Timer no es exclusivo de las salidas PWM, si no que es compartido con otras funciones. Emplear funciones que requieren el uso de estos Timer supondrá que no podremos emplear de forma simultánea alguno de los pines PWM.
Ejemplo de código PWM para Arduino
El código necesario para encender una salida PWM es muy sencillo gracias a las bibliotecas de Arduino, que configuran por defecto las salidas de PWM en la función Setup(), ocultando la dificultad de manipulación de los Timer.
En el ejemplo mas básico, simplemente definimos el pin PWM que queremos emplear, y usamos la función analogWrite para escribir el valor del Duty Cycle, medido de 0 a 255.
El siguiente código establece la salida PWM a la cual está conectada el LED proporcional al valor leído del potenciómetro conectado al pin 3. Nota que el valor de lectura del potenciometro va entre 0 a 1024 mientras que el valor que podemos usar para el control del ciclo de carga puede ir entre 0 y 255 es por eso que el valor de la lectura del potenciometro se divide en 4.
int ledPin = 9; // LED connected to digital pin 9
int analogPin = 0; // potentiometer connected to analog pin 0
int val = 0; // variable to store the read value
void setup()
{
pinMode(ledPin, OUTPUT); // sets the pin as output
}
void loop()
{
val = analogRead(analogPin); // read the input pin
analogWrite(ledPin, val / 4); // analogRead values go from 0 to 1023, analogWrite values from 0 to 255
}
