01 diciembre 2012

Variando la intensidad de un LED en Arduino con PWM

El día de ayer recibí mi Arduino Mega, esta es una pequeña tableta creada especialmente para hacer prototipos electrónicos. En lo personal me llamó mucho la atención porque es sumamente fácil de programar y utilizar. Si son de la gente que les encanta hacer proyectos electrónicos Arduino es una excelente solución. En esta entrada intentaré explicar una aplicación básica de Modulación del Ancho de Pulso (PWM) para el cambio de la intensidad de un LED.


Hoy les intentaré explicar un poco ¿Qué es PWM? y como utilizarlo a travez de la función AnalogWrite de Arduino. Debo advertirles que no soy un experto en electrónica, pero la idea del PWM es la siguiente: Un LED usualmente tiene un voltaje fijo de trabajo y una intensidad de corriente en el cual es más eficiente (desperdicia menos energía en forma de calor de la que convierte en luz). Hay dos formas de modificar la intensidad de un LED, la primera es variando la corriente... Esto es usualmente problemático ya que no es eficiente en cuanto al funcionamiento del LED, además que construir un limitador de corriente que podamos controlar de manera digital no es cosa tan sencilla.

PWM la solución a nuestros problemas

El plan B es utilizar algo que se denomina Modulación del Ancho del Pulso (PWM por sus siglas en inglés). El PWM funciona enviando un tren de pulsos digitales y lo que hace es que en cada ciclo del pulso se varía el tiempo que el 1 lógico se mantiene activo.

La idea detras de esto es que mientras mas angosto el pulso lógico 1 es menor el tiempo que pasa encendido el led, si estos pulsos son sumamente rápidos entonces a nuestros ojos parecerá que la intensidad del led se ha disminuido. De igual manera si el pulso lógico es mas ancho el led se "apagara" durante menos tiempo y nos dará la impresión de que la luz es más intensa.

Aquí hay un video (en inglés) que explica más a detalle como funciona el PWM (Recomiendo activar subtitulos y habilitar la traducción a español):


El Arduino Mega tiene configurado de fábrica con un pequeño LED en la línea 13, esta línea es capaz de utilizar PWM.

Variando la intensidad en base a la función senoidal


La herramienta de escritorio de Arduino trae un ejemplo sencillo que cambia la intensidad del led utilizando PWM, el problema es que el cambio de intensidad es "lineal" y yo quería una transición más suave.

Podemos utilizar la función seno o coseno para generar una variación de intensidad, primero veamos el comportamiento de la función normalmente, en este caso utilizé la función seno (tengo cierta preferencia al seno):


El problema obviamente, es que el Arduino utiliza una modulación de pulsos con un rango desde 0 hasta 255. Y el rango de nuestra función va desde -1.0 a 1.0. ¿Como convertimos esto hasta algo que nos sirva, bueno modificando un poco la formula.

Primero vamos a forzar que nuestra función seno tenga únicamente rangos positivos, si sabemos que nuestro valor menor es -1 pues simplemente sumamos 1 a nuestra función:


¿Bonito no? Pero aún no está en el rango de 0 a 255, así que haremos dos cambios más, primero el tamaño de nuestro rango  es 2, dividamos eso entre 1/2 para que nuestro rango de función sea desde 0 a 1.


Una vez hecho esto solo tenemos que mutiplicar nuestro valor máximo por la función para garantizar que nuestra función varíe de forma senoidal en el rango 0-255:


Convirtiendo la teoría en práctica


Ok, espero aún me esten leyendo porque ahora viene lo bonito. Originalmente tenía pensado utilizar la biblioteca math.h en Arduino para calcular la fórmula anterior "al vuelo", pero luego me puse a pensar que estamos hablando de un dispositivo con recursos muy limitados. Considerando que tengo 126Kbytes de memoria disponible. ¿Por qué no mejor pre-calcular los valores de intensidad y hacer un sencillo ciclo FOR que los recorra?

Así que eso hice con la ayuda de Excel: lo primero es crear nuestro rango 0-255, esto lo hacen fácilmente Agregando llenando dos casillas con 1 y 2 y luego arrastrar la serie hasta completar 255.

El dominio de la función seno para un ciclo completo en X positivo va desde 0 hasta 2*pi, así que lo que siguiente que realizaremos es calcular un incremento que correspondera a 2*pi/255 para cada uno de los pasos, el primer paso será 0 y el siguiente será la suma del paso anterior + el incremento.


Cálculo del ancho del intérvalo

Calculo del incremento de X

Aplicamos nuestra fórmula para cada uno de los pasos:
Cálculo de los valores de Y (intensidad)

Por último cambiamos el formato de número a entero ya que el resultado nos da decimales, estos valores son los que usaremos en nuestro arreglo.


En Notepad2 con ayuda del reemplazo con expresiones regulares puedo sustituir los fines de línea por "," lo que me permitirá crear un sencillo arreglo con todos los valores:



Modificamos un poco el código de ejemplo de PWM de arduino:
const int led = 13;
// SIN FUNCTION VALUES
const int steps[] = {
  128,131,134,137,140,143,146,149,152,156,159,162,165,168,171,174,176,179,182,185,
  188,191,193,196,199,201,204,206,209,211,213,216,218,220,222,224,226,228,230,232,
  234,235,237,239,240,242,243,244,246,247,248,249,250,251,251,252,253,253,254,254,
  254,255,255,255,255,255,255,255,254,254,253,253,252,252,251,250,249,248,247,246,
  245,244,242,241,239,238,236,235,233,231,229,227,225,223,221,219,217,215,212,210,
  207,205,202,200,197,195,192,189,186,184,181,178,175,172,169,166,163,160,157,154,
  151,148,145,142,138,135,132,129,126,123,120,117,113,110,107,104,101,98,95,92,89,
  86,83,80,77,74,71,69,66,63,60,58,55,53,50,48,45,43,40,38,36,
  34,32,30,28,26,24,22,20,19,17,16,14,13,11,10,9,8,7,6,5,
  4,3,3,2,2,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,
  4,4,5,6,7,8,9,11,12,13,15,16,18,20,21,23,25,27,29,31,
  33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,79,81,
  84,87,90,93,96,99,103,106,109,112,115,118,121,124
};
void setup() {
  // set pin 13 as output:
  pinMode(led, OUTPUT);
}
void loop() {
 for (int pos = 0; pos < 255; pos++) {
   analogWrite(led, steps[pos]);
   delay(50);
 } 
}

Por último compilamos y cargamos nuestro código al Arduino y he aquí el resultado:



Como se explicaba en el video al inicio de este post la PWM tiene un sin fín de aplicaciones además de variar la intensidad de LEDS, como por ejemplo variar la velocidad en motores DC. Vamos a ver quién es el primero que se anima a armar un carrito RC con Arduino :)

2 comentarios:

Antonio dijo...

Absolutamente genial.
Espero usarlo pronto. Lo estoy probando y queda de lo más elegante.

Para poder probarlo en un arduino nano, usando el pin 13, que no tiene pwm. Recomiendo adaptar el código como dicen aqui: http://www.luisllamas.es/2015/08/salidas-analogicas-pwm-en-arduino/

Facundo Rezzuti dijo...

Hola, quería hacerte una consulta, cuando calculas el paso de la forma 2*pi/255 y vas sumando, esta suma supera el valor 5, eso quiere decir que va a intentar poner la salida por encima de los 5v?? En ese caso no lo pasa nada al pin de salida?? Desde ya muchas gracias por toda la info.
Estoy intentando hacer un pll para una frecuencia variable entre 1 y 5khz, sabes si es factible de realizarlo con arduino uno??