Felix Maocho

Para quien le interese lo que a nosotros nos interesa

Curso de Arduino – Salidas con pines “analogicos”. 1ª Parte Pulsos de Ondas Moduladas PWM

digital-realPor Félix Maocho
10/2/2017

Objetivo de este capítulo

Este capítulo es una introducción al uso de pins PWM o salidas analógicas. Pretendemos explicar que son los Pulsos de Onda Modulados, (Pulse-Width Modulation), conocidos como PWM y la utilidad que tienen en los objetos creados con la Tarjeta Arduino.

Material necesario

Tarjeta Arduino y cable de conexión al PC
Tarjeta de prototipado o “protoboard”
5 Cables de conexiones, (Preferiblemente, no imprescindible,  2 rojos y, 3 grises)
2 Resistencia de 220 Ω
2 leda (es importante que sean iguales de tipo y color, es indiferente el color

Conocimientos previos necesarios

  • Saber utilizar los pines digitales de Arduino como OUPUT
  • Uso de las funciones del sistema “pinMode”, “digitalWrite” y “delay”
  • Saber escribir, compilar y cargar en la Tarjeta Arduino un programa

Todos estos conocimientos se explican en detalle en los capítulos anteriores del curso (Busque en el Indice)

Cómo podemos con un equipo digital, modelar un mundo analógico

El mundo real no es digital, es analógico. El mundo no es blanco o negro, sino con todos los matices de grises, ni hay ruido o silencio, hay voces y susurros, ni caliente y frio, es también templado y fresco. Algo hay que inventar para poder dar forma con ceros y unos a las cosas que son 0,76, ó 0,23 y por supuesto, algo tiene la Tarjeta Arduino para hacerlo, se llaman los Pines analógico, que generas salidas que se aproximan a la realidad del mundo físico.

Encendamos una débil lucecita

Supongamos que queremos que un led, no sólo esté encendido y apagado, sino que este con una luz debil, ¿Lo podremos hacer con lo que sabemos?

Pues si. Para ello nos vamos a valer de una propiedad de la vista que es la persistencia retiniana, el fenómeno que nos hace ver moverse algo, que solo es una continuada proyección de imágenes lévemente persistentes, que nuestro ojo une en el cerebro.

Lo que percibimos en una bombilla es la energía luminoso que desprende. Si esa bombilla recibe menos energía, desprenderá menos energía y la veremos menos brillante. El Led luce. o no luce, pero si conseguimos que luzca durante un tiempo y este apagado otro, como hemos hecho en las intermitencias, por unidad de tiempo, se desprende la mitad de enegía. Si hacemos que la intermitencia ocurra a mayor velocidad de 24 veces por segundo. lo que veremos es una luz mas pálida.

Si hacemos 25 parpadeos por segundo no veremos la intermitencia, pero nos parecera la luz mas apagada, porque en un segundo emite menos luz. O sea vamos a tratar de que cada parpadeo tarde un venticincoavo de segundo, como le segundo son 1000 microsegundos la venticinco parte son 40 microsegundos.

Hagámoslo

Manos a la obra, Dejamos una luz encendida siempre como comparación y la otra la encendemos y apagamos 25 veces en un segundo, es decir 40 centesimas de segundo de las cuales 20 está encendida y 20 apagada.

Hardware

 

pines-analogicos

 

El hardware sera como el que se indica en la imagen de la entrada, perfectamente conocido a estas alturas dos circuitos con un led y  una resistencia  como hemos visto anteriormente, Un led “testigo” unido al pin 8 y uno que llamamos “variable” unido al pin 9 con sus respectivas resistencias de 220 homios.

Software

El softweare es un programa muy sencillo, parecido al que hicimos en la primer ejercicio de intermitencias, con la diferencia que para no repetir la apertura de pins inútilmrntr que manda en el primer “loop” encender los dos pin y en los demas encender y apagar el led variable 25 veces, 0 sea, intermitencias de 40 micro segundos, 20 encendidas y 20 apagadaa.

Pues bien, si lo hacen, (sólo tienen que cambiar donde pongo 12 por 20 en el programa que viene a continuación), verán que se ve parpadear las luces.

¿Y eso por qué?.

Porque no hemos tenido en cuenta el tiempo que se pierde en ejecutar las instrucciones que tienen la función “loop” además de los “delay”. En cada “loop”, comprueba  que un “if” no se cumple y manda saltar una serie de mandatos, hace un “delay” (que si hemos tenido en cuenta), cierra un pin, hace otro “delay” (que si hemos tenido en cuenta), y vuelve a abrir al pin. Y en todo eso pierde un tiempo que retrasa el “loop” y por lo tanto, en un segundo no hacemos 25 “loop” sino menos y vemos el parpadeo.

Después de algunos tanteos, he pedido que las intermitencias duren 24 milisegundos y así se ve contínuo. El sketch para intermitencias de 24 milisegundos queda así.

/* Sketch E7.01 Luz tenue
* Encender los dos leds al primera vez
* Intermitencia en el led “variable”de 24 milésimas
* Repetir el ciclo
*/
// Área de definición de variables y parámetros
int pinled1 = 7; // pin en OUTPUT para el LED testigo
int pinled2 = 8; // pin en OUTPUT para el LED variable
int primerloop = 0; // Si 0 se ejecuta primer loop 1 en los otros
int encendido = 12; // encendido 12 milésimas
int apagado = 12; // apagado 12 milésimas
// Función setup
void setup() {
pinMode( pinled1, OUTPUT);
pinMode( pinled2, OUTPUT);
}
// Función loop
void loop() {
// **************** Primer loop ***************
if ( primerloop == 0) {
digitalWrite(pinled1, HIGH);
digitalWrite(pinled2, HIGH);
primerloop = 1 ; // // Para no volverlo a hacerlo
} // Fin lo que hay que hacer en el primer loop
// ********** Intermitencia rápida***************
delay (encendido);
digitalWrite(pinled2, LOW);
delay (apagado);
digitalWrite(pinled2, HIGH);
} // fin funcion loop

Si copiamos, compilamos y subimos el Sketch a la Tarjeta Arduino, veremos algo como lo que muestro en el vídeo

 

Si se fijan, la luz de la derecha es algo mas ténue que la de la izquierda, como estaba previsto. Sin embargo lo más que aguanta sin que se vea el parpadeo son intermitencias de 24 milisegundos, 12 encendidos y 12 apagados. He probado a poner 15” y se nota el parpadeo. Indica que la Tarjeta Arduino UNO es mas lenta que lo que parece, porque, tarda 16 microsegundos en hacer un puñadito de mandatos. No he encontrado documentación sobre el tiempo que se tarda en encender y apagar un pin, pero sospecho que este es el motivo del retardo.

Lo que viene a continuación es cultura general informática, que si quiere se puede saltar

La velocidad del procesador es de 16 Mhz, o lo que es lo mismo, en un segundo ejecuta 16.000 instruciones en “lenguaje máquina”, pero eso no quiere decir mucho, porque el hombre se comunica con la máquina en Ensamblador un lenguaje con las instrucciones más básicas que utilizan todos los lenguajes de progrmación. Por tanto, basta que el fabricante del procesador, establezca con rutinas de “lenguaje máquina” los mandatos que le llegan en “ensamblador”, (asembler), para que automáticamente, ese procesador se pueda utilizar con cualquier lenguaje de programación..

Así capa sobre capa se va llegando a lenguajes específicos para trabajos específicos. Por ejemplo el tratamiento de textos tiene una serie de mandatos, como poner en negrita, saltar de linea, o simplemente escribir una “s” , esto se llama un “lenguaje ” de programación de “alto nivel”, que es el 2que normalmente utilizan los programadores, porque es sencillo de manejar y de alto rendimiento. Tan sencillo que todos lo utilizamos con muy poco aprendizaje e incluso los mas espe4rtos lo manejqn con los 10 dedos y sin mirara teclado.

Un lenguaje de bajo nivel, hoy lo habitual es utilizar C o C**, toma r el mandato “s” que ha recibido y lo guarda en la memoria en firnma de o bits (cerios y unos) y no solo eso busca un dibujito de una s que tien guardado por ahí, (en 0 y 1 que son pixel apagados y encendidos) y lo presenta en pantalla en el sitio que corresponde .·Y para hacer todo eso sus mandatos los transforma en rutinas de “Ensamblador” que la máquina sabe “digerir”  y trasformar en rutinas de “lenguaje máquina”

Si no me equivoco. Arduino tiene una capa iIntemedia que es el Lenguaje C, lenguaje ya cómodo de utilizar pero que aun anda muy próximo a la maquina y puede con facilidad mover bites y cambiar direcciones de memoria. El lengua IDE que utilizo para este curso, no son mas que rutinas en C que simplifican el uso de equipos que se van a utilizar especificamente como microprocontroladores.

Asi que cundo ponemos un expresiontan sencilla como (j= j + 1) estamos diciendo en un lenguje de nivekl alto

  • Lleva a procesador la variable J
  • Cuando este ahi súmale una unidad
  • El resultado lo vuelves a guardar en donde se guarda la variable J

La primer instrucción “Lleva a procesador la variable J “ pasa a ensamblador que lo que hace probablemente, porque no se exactamnte como lo hace es:

  • Busca en el indice donde se guarda la variable j
  • Cuando sepas donde está la direccion de memoria (ROM o de Disco) donde se gusrda j
  • Averigua en otro indice el tipo de fariable que es
  • Cuando sepas la direccion de menoria copia tantos bits como necesuta ese tipo de variable en un área de memoria del procesadoro (Memoria RAM o dinámica, típica de los procesadores)

Por fin llegamos al lenguaje máquina donde “Busca en el indice donde se guarda la variable j “ en una complejísima operación de búsquedas dicotómicas, que consiste en mirar en la mitad de un indice y comparar el nombre de la variable con el que esta eb esa posición del indice. Si es mas grande, buscar en en la mitad de la parte inferios y si no al revés. Comparar, buscar etc, hasta a llegar al valor buscado exactamente, ( suelen necesitarse de 8 a 9 búsquedas para acertar como media) y una vez encontrado, tomar la dirección de memoria asociada, que se dio in el proceso INT, y quizá el tipo de variable, que puede ser aunque no lo sepas aun, de lo mas variada,numerica, textual, de 2 o 4 bits, representar un numero entero, o un número fraccionario con 16 numero significativos, vectores, o matrices,… Vamos de lo mas variado y que cada una necesita un espacio específico par ser guardado en el disco duro. Ya tenemos la direcion y el número de bis que tenemos que copiar,.image038lengaje-mcaquina

El esquena superior, indica mas o menos el proceso. El programador escribe un programa, que el compilador transforma en rutinas de ensamblador en un primer paso y posteriormente la máquina transforma esas rutinas de lenguje máquina que es realmente lo que guarda la memoria del ordenador. En nuestro caso, hay aun un nivel mas  que seria de IDE a C, que seria en este caso que tu escribieras (F4 = RI + F4),

Como ves en estos procesos las lineas de programa se multiplican como conejos y finalmente la máquina que no puede mover mas que, (en esta máquina), 8 bits de golpe, o se 8 ceros y unos, (una de las lineas que se ven en el grñafico)  hace el resto.

Por todo lo contado se darán cuenta que 16.000 instrucioners de 8 bits por segundo, no son ni mucho menos 16,000 mandatos de IDE sino lógicamente muchisimos menos. Razón por la cual, como en este programa salta a la vista, los 16000 MHz ,no son tan rápidos como parecía.

Fin de la cultura Informática

Pese al retardo que producen las funciones, aun nos quedan unos milisegundos para jugar. En este caso, vamos a subir y bajar la intensidad de la luz del led jugando con esos 24 segundos de duración del “loop”, que tenemos antes de que se vea parpadear la luz

Dejaremos el hardware como esta pese, a que en este caso no vamos a utilizar el led “testigo”, pues seguiremos utilizando a continuación el led continuación tal como está, por lo que no merece la pena desmontarlo. En cambio el programa será como sigue

/* Sketch E7.02 Luz de un led suben y bajan
* Contadores j y k inicializados a 0
* Encender un led al primera vez
* si j 0
* tiempo encendido 24 – k
* tempo de apagado k
* k = k -1
* Si (k == 0) j = 1
* Repetir el ciclo
*/
// Área de definición de variables y parámetros
int pinled2 = 8; // pin en OUTPUT para el LED variable
int primerloop = 0; // Si 0 Primer loop 1 en los otros
int j = 1 ; // contador de la bajada
int k= 0 ; // contador de las subidas
// Función setup
void setup() {
pinMode( pinled2, OUTPUT); // enciende led 2
}
// Función loop
void loop() {
// **************** Primer loop ******************************
if ( primerloop == 0) {
digitalWrite(pinled2, HIGH);
primerloop = 1 ; // // Para no volverlo a hacer
} // Fin lo que hay que hacer en el primer loop
// ********** Intermitencia variable***************
// si (j <25) baja la luz poco a poco
if (j < 25) {
delay (25- j); // encendido
digitalWrite(pinled2, LOW);
delay ( j ); // apagados
digitalWrite(pinled2, HIGH);
j = j +1 ;
} // Fin if (j1) {
delay (26- k); // encendido
digitalWrite(pinled2, LOW);
delay ( k ); // apagados
digitalWrite(pinled2, HIGH);
k = k -1 ;
} // Fin if (k >1)
if (k==1) {
k = 0 ; // Para no volverlo a hacer
j = 1 ; // Preparar ela subida
} // Fin bajada
} // fin función loop

Ven que lo he conseguido, la luz sube y baja. La único truco es utilizar dos contadores, una ascenderte y otro descendente. porque si en un momento determinado a “j” le restamos algo, entra en valores por debajo de 25 y el ”loop” se metería nuevamente en el primer “if” y trataría nuevamente de apagar el Led. También hay que cuidar que “k” no llegue a 0. porque no tiene sentido un “delay” con tiempo 0 ó negativo

Veamos el resultado

Lo que hemos realizado se llama técnicamente “pulsos con modulación”, o en inglés “pulse-width modulation”, (PWM), donde se modifica el ciclo de trabajo de una señal periódica. en nuestro caso un señal rectangular, bien para transmitir información o como en nuestro caso para controlar la cantidad de energía que se envía por el cableCuanto menos tiempo esté el voltaje en alto, menos energía mandaremos por el cable, y viceversa.

 

grafica-pwm

 

Sin embargo en nuestro caso nos encontramos con dos limitaciones, La primera que la unidad de medida que tiempo es el milisegundo, por tanto no podemos apagar o encender la luz por menos tiempo que ese, y la segunda limitación es que para que el efecto de parpadeo no aparezca tenemos que emitir como máximo ciclos de 24 milisegundos por lo que no tenemos mucho tiempo para perder en otros menesteres, .

Por ejemplo si no fuera un led sino dos los que quisiéramos aumentar y descender encender, ya no podríamos pues el tiempo perdido en abrir y cerrar el segúndo led, es superior a lo que podemos distraer,. Si en el primer caso, se redujo el tiempo teórico de 40 milisegundos a 24 en este necesitamos otros 14 milisegundos mas por lo menos, y por mucho que tratemos haciendo subir y bajar la luz los 5 peldaños que nos quedan de los 10 milisegundos restantes , muy probablemente el efecto sería inapreciable.

Afortunadamente para la primera limitación hay arreglo. Existe un mandato llamado “delayMicroseconds” donde la parada se contabiliza como su nombre indica en microsegundos, es decir en millonésimas de segundos, por tanto podemos hacer muchos mas ciclos y jugando con la velocidad de crecimiento de los contadores “j” y “k” por ejemplo subir y bajar la luz mas rápida o mas lentamente y sobre todo podemos perder mas tiempo en otras cosas, porque tenemos muchísimos mas ciclos para hacer en un segundo pues las paradas se pueden dosificar mejor

Sintaxis de delayMicroseconds

La sintaxis de “delayMicroseconds” es igual que la de “delay”

delayMicroseconds(value)”

Donde:

  • delayMicroseconds” es el mandato que indica la parada en microsegundos
  • value” es un valor de los milisegundo de parada.

Si multiplicamos todo por 1000 en el programa anterior nada debería cambiar pero por algún motivo en este caso vemos temblar a la luz, no se realmente el motivo, pienso que quizá trabajar con cifras mas grandes retrasa las operaciones, el caso es que me he visto obligado a reducir un poco la duración de los ciclos de y así funciona perfectamente igual que antes.

El programa queda como antes. Sólo cambiamos los “delays” por “delayMicroseconds” y los tiempos los multiplicamos por 1000. Le dejo aqui los cambios como ven he bajado 5000 microsegundos el calculo inicial. Como sale igual no lo presento, pero haz la prueba con 25000 milisegundos y con 2000 y veras la diferencia.

/* Sketch E7.04 Una luz aumenta y la otra reduce
* Contadores j y k inicializados a 0
* Encender un led al primera vez
* si j 0
* tiempo encendido 5000 – k
* tempo de apagado k
* k = k -100
* Si (k == 0) j = 100
* Repetir el ciclo
*/
// Área de definición de variables y parámetros
int pinled1 = 9; // pin en OUTPUT para el LED variable
int pinled2 = 8; // pin en OUTPUT para el LED variable
int j = 10 ; // contador de la bajada
int k= 0 ; // contador de las subidas
// Función setup
void setup() {
pinMode( pinled1, OUTPUT);
pinMode( pinled2, OUTPUT);
}
// Función loop
void loop() {
// si (j <5000) subir luz 1 y bajar luz2
if (j < 5000) {
delayMicroseconds (5000 – j);
digitalWrite(pinled1, HIGH);
digitalWrite(pinled2, LOW);
delayMicroseconds ( j );
digitalWrite(pinled1, LOW);
digitalWrite(pinled2, HIGH);
j = j +10 ;
} // Fin if (j10) {
delayMicroseconds ( k );
digitalWrite(pinled1, LOW);
digitalWrite(pinled2, HIGH);
delayMicroseconds ( 5000 – k );
digitalWrite(pinled1, HIGH);
digitalWrite(pinled2, LOW);
k = k -10 ;
} // Fin if (k >10)
if (k==10) {
k = 0 ; // Para no volverlo a hacer
j = 10 ; // Preparar ela subida
} // Fin bajada
} // fin funcion loop

 

Me ha costado mucho jugar con los valores iniciales de “j” y “k”, así como el incremento en cada “loop”, para que tenga una velocidad adecuado pero al fin lo he conseguido. Vean el resultado, una luz aumenta de brillo y la otra lo reduce hasta apagarse y volver a encenderse mientras la otra baja.

Miren cosas curiosas de este sketch, desaparece las cosas que teníamos que hacer en el primer “loop” y en consecuencia la variable “primerloop” que lo controlaba, porque no hay que abrir previamente los pins que van a trabajar. Por lo demás es muy lioso en el manejo de contadores, creo que la deben copiar y jugar cambiando el incremento de los contadores y sus límites buscan do encontrar una solución que mueva las luces a una velocidad adecuada y que a la vez no se las vea parpadear.

La ventaja de controlar en un mismo ciclo dos ondas PWM

Sin embargo, hay algo que hemos hecho que es fundamental, tener a la vez dos pines que están mandando una señal PWM o lo que es lo mismo, una señal, (hasta cierto punto), analógica, pues es algo fundamental para nuestros aparatos, porque, por ejemplo, para manejar el desplazamiento de un robot tenemos que controlar la velocidad de giro de dos ruedas, la derecha y la izquierda y si sólo tienes capacidad de manejar un pin como PWM, sólo podrás manejar electrónicamente la velocidad de un motor, como ocurre con los coches de gasolina, y como ocurre en los coches tienes que montar en el eje tractor un complicado elemento mecánico llamado diferencial, para permitir que cada rueda gire a diferente velocidad.

Un solo motor, diferencial y dirección de cremallera

Hacelo es posible, pero muy complejo para el taller de un aficionado, porque pasamos de circuitos digitales en los que no se necesita hardware complejo a sistemas mecánicos bastante complicados. Vean por ejemplo una diferencial construido con piezas de Meccano, la cantidad de engranajes y piececitas que necesitas sólo para hacer este elemento mecánico

diferencial

Y aun te falta otro complejo sistema para poder guiar el coche, como es la direccion de un vehículo

direccion

Dos motores, sencillez mecánica

Sin embargo, si somos capaces de gobernar dos motores, colocamos uno en cada rueda, como funcionan la mayoría de los coches eléctricos actuales, no se necesita diferencial, basta con enviar diferente energía a cada rueda, para que cada una vaya a velocidad diferente, y tampoco necesitamos la compleja dirección., Pues mientras que en un coche eléctrico, la orientación de las ruedas delanteras, es la que fuerza la diferente velocidad de las traseras ruedas tractoras, en un coche con dos motores es al revés, las ruedas tractoras fuerzan la dirección de las ruedas de dirección y basta poner ruedas locas, para que sigan solas la trayectoria del vehículo. Hemos sustituido el volante que gira unas ruedas por un aparato que deja pasar mas energía a una rueda que a otra. El coche eléctrico con dos motores es mecánicamente de una sencillez espantosa.

chasis-robot

Por esto todos los robots sencillos funcionan siempre con dos motores y una rueda loca, como ocurre en el chasis que enseño, formado por dos ruedas paralelas, que moverán motores diferentes y una rueda de “dirección”, que puede girar libremente al rededor de un eje vertical, de modo que siga la ruta marcada por la diferente velocidad de giro de las ruedas tractoras.

Pines específicos PWM

Como es lógico, pese a las modestas prestaciones de la Tarjeta Arduino, esto no se podía pasar por alto y Arduino Uno, tiene integradas, no dos, sino seis pines PWM de fábrica, que son capaces de forma interna de generar y modular los impulsos de forma independiente de el programa, de forma que los seis pueden funcionar incluso concurréntemente. Y eso es la modesta tarjeta Arduino UNO otros modelos de Arduino tienen incluso más pines PWM.

Entre los modelos mas generalizados Arduino Uno, Mini y Nano, disponemos de 6 salidas PWM de 8bits que dan un hasta 256 niveles diferentes de salida en los pines 3, 5, 6, 9, 10 y 11., pero Arduino Mega tiene 15 salidas PWM de 8bis de los pines 2 a 13 y 44 a 46 y Arduino Due tiene 13 salidas PWM de 8bits en los pins 2 a 13 y dos salidas con resolución de 12bits (4096 niveles) .

Dejo para el siguiente post la explicación de como se manejan. Lo único que anticipo, es que como ocurre con los pines digitales, cuando le mandas que envíen energía con un cierto nivel, la mandan de forma continuada independientemente de lo que después haga el programa, porque de generar las ondas PWM como tu solicitas, se encarga automáticamente los circuitos internos de la tarjeta y lo harán en tanto, tu no des la orden de cesar, o cambiar el nivel de energía, que estas mandando, Por tanto el sketch queda liberado para hacer otras cosas.

Repaso

Como repaso final te diré lo que has aprendido hoy

  • Manejo de contadores crecientes y decrecientes
  • Control sobre el primer “loop”
  • Qué se lenguaje máquina
  • Qué es le lenguaje Ensamblador (Asembler)
  • Qué son los lenguajes de bajo nivel
  • Qué son los lenguajes de alto nivel
  • Que son “pulsos con modulación”, o en inglés “pulse-width modulation”, (PWM),
  • Mandato “delayMicroseconds”
  • Ventajas de poder controlar diversas salidad PWM
  • Pines PWM

Ejercicios para resolver

Con el fin de ejercitarse en el manejo de contadores Cambiar en el último ejemplo tanto la velocidad de subida y bajada de la luz como el número de “loops” por segundo

Félix Maocho

indice

Anuncios

10 febrero 2017 - Posted by | Curso de Arduino, Robotica | , ,

Aún no hay comentarios.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: