huborarduino.com

La web de Hubor para la simulación de Arduino con Proteus

Banner Guia Programacion Arduino Con Proteus

arduino,

  • Curso de simulacion de Arduino con Proteus

    kungfu-MinionEn este curso, vamos a introducirnos desde cero en la simulación de los equipos Arduino utilizando la herramienta Proteus. Sólo trataremos las nociones básicas que nos permitirán entender las principales técnicas de simulación y que nos servirán para arrancar. Otros cursos de esta web se basarán en los conceptos iniciales que se explican en este.

  • Ejecutar proyectos Arduino en Proteus





  • Lección 1: Conceptos generales y salidas digitales

    [Proteus versión 8.1]

    En esta lección encenderemos un led situado en una de las salidas de nuestra tarjeta Arduino y aprovecharemos para introducirnos en algunos de los conceptos generales de la programación de los equipos Arduino.

    Para llevar a cabo esta lección utilizaremos un proyecto de Proteus basado en la tarjeta de desarrollo "Arduino328" con el shield "Led Bar". En la sección simulación encontrará toda la informacion necesaria para crear el proyecto con esta tarjeta y este shield y los conceptos básicos de la simulación de Arduino utilizando Proteus.

    Por supuesto, si usted dispone de la tarjeta de desarrollo de hardware y las herramientas de programación también podrá utilizarlos para seguir esta lección. Nosotros hemos optado por utilizar el simulador en todas nuestras lecciones por la comodidad que esto supone.

    El esquema electrónico de nuestro equipo, con la placa base y el shield, se recoge en la imagen siguiente. Recuerde que los terminales del mismo nombre están eléctricamente unidos. Así, que el terminal 1 de la barra de led está eléctricamente enlazado con el pin 17 del microprocesador ATMEGA328P, porque ambos están unidos a un terminal del mismo nombre, IO11.

    Lec01 001

    Al crear el proyecto, Proteus genera automáticamente para nosotros la estructura básica de nuestro código fuente. Sólo utilizaremos un fichero llamado Main.ino donde se albergará toda la lógica de nuestro primer proyecto.

    Lec01 002

    Por defecto, el editor nos presenta de color verde los comentarios. Los comentarios son ignorados por el programa y no ocupan espacio en la memoria.

    El comentario puede ocupar una sola línea o un bloque formado por varias líneas. Para escribir un comentario de una línea utilizamos los caracteres // seguidos del texto que deseamos. Una línea de comentario puede empezar en una columna distinta de la primera, pero siempre termina en la siguiente línea de código. Por ejemplo, en nuestro código las líneas de comentario situadas en las líneas de código 9 y 13 no empiezan al principio. El compilador ejecutará el código de la línea hasta encontrar los caracteres // e ignorará todo lo que siga a partir de ahí considerándolo un comentario.

    Para escribir un bloque de comentarios que ocupe más de una línea, insertamos el texto del comentario entre el código de inicio /* y el código de fin */.  En nuestro ejempo, las líneas de código 1 a 6 son un bloque de comentarios.

    Los comentarios ayudan a escribir información que facilita la interpretación de lo que realiza cada parte de nuestro programa facilitando la depuración del mismo cuando ha transcurrido un tiempo o cuando lo lee una persona diferente a quien los escribió.

    Además de los comentarios, el código contiene dos funciones. Una llamada setup y otra loop. Una función es un bloque de código que contiene una serie de sentencias o instrucciones, que tiene un nombre que la identifica de forma inequívoca y que se ejecuta cada vez que alguien utiliza una llamada a dicha función desde el programa. El formato de una función es el siguiente:

    <valor devuelto> <nombre de la función>()

    {

    sentencias

    }

    En nuestro ejemplo, las funciones setup() y loop() no devuelven ningún dato. Por esta razón el <valor devuelto> es "void". Void es una palabra clave que indica que nuestra función no devuelve nada. Además, como dentro de las llaves, lo único que hay son comentarios, tal y como están no ejecutarían ninguna acción.

    Todos los programas escritos para Arduino deben tener al menos dos funciones, las que acabamos de ver, llamadas setup() y loop(). Como es lógico, programas más complejos pueden contener muchas más funciones.

    La función setup() contendrá la parte de código encargada de la configuración de nuestro equipo. En ella debemos poner la declaración de las variables que utilizaremos en nuestro programa (más adelante ahondaremos en el concepto de variable), la configuración de los diversos pines del microprocesador, la configuración de las comunicaciones, etc. Siempre es la primera función que se ejecuta al arrancar nuestro equipo.

    La función loop() contiene la parte de código que se ejecutará de forma cíclica durante todo el tiempo que esté nuestro equipo en funcionamiento. El nombre loop en inglés significa bucle y expresa exactamente este concepto, sentencias que se ejecutan en bucle repetidamente una y otra vez. Aquí escribiremos el código que se encargará de leer el estado de las entradas, ejecutar la lógica de funcionamiento y escribir el estado de las salidas en función de los resultados calculados.

    Vamos a empezar a escribir la primera línea de código de nuestro programa. Nuestro objetivo será encender el led 8 de la barra de leds. Para ello debemos configurar el pin llamado PD4 (el número 6) del microprocesador como salida. El led 8 y el pin PD4 están unidos por el conector llamado IO4.

    Algunos de los pines del microprocesador ATMEGA328P pueden funcionar como entradas o como salidas. Para definir el uso que daremos a un determinado pin utilizaremos la función pinMode(). La sintaxis de esta función es la siguiente:

    pinMode( <número de pin>,<modo de funcionamiento>)

    Donde <modo de funcionamiento> puede tener dos valores, INPUT cuando queremos que funcione como entrada y OUTPUT cuando queremos que realice las funciones de salida. El párametro <número de pin> puede  tomar los valores de 0 a 13. El 0 se utiliza para el pin PD0 y el 13 para el pin PB5.

    La terminología utilizada en el microprocesador para nombrar los pines puede resultar un poco confusa la primera vez que uno se acerca a un microprocesador. Agrupa los pines en conjuntos (puertos) de 8 pines. Por ejemplo, los pines 2 al 13 forman el puerto D. El primer pin del puerto D recibe el nombre de PD0 (el pin físico con el número 2) y así sucesivamente hasta llegar al PD7 (el pin número 13). Para simplificar el uso, los conectores a cada uno de los pines se han rotulado siguiendo otra convención. Los 13 pines digitales llevan los nombres IO0 a IO13. Los 6 pines analógicos llevan los nombres AD0 a AD5. Así es más fácil su identificación.

    Modifiquemos nuestro programa para indicar que el pin IO4 funcionará como salida.

    Lec01 003

    Observe que hemos terminado la sentencia con un punto y coma. Para separar cada uno de los comandos que componen nuestro programa utilizamos el indicador punto y coma.

    Ahora vamos a escribir el código para activar la salida IO4 y encender el segmento 8 de nuestra barra de leds. Para activar o desactivar una salida utilizamos la función digitalWrite(). Su formato es el siguiente:

    digitalWrite( <número de pin> , <estado del pin>)

    Donde <estado del pin> puede tener dos valores, HIGH cuando queremos activar la salida y LOW cuando la queremos desactivar.

    Ya podemos modificar nuestro programa para activar la salida IO4.

    Lec01 004

    Note que hemos terminado la sentencia con un punto y coma y que la hemos escrito dentro de la función loop().

    Para compilar nuestro programa, utilizaremos la opción "Construir proyecto" del menú "Construir" o el atajo de teclado "F7". 

    Lec01 005

    Si todo ha ido bien, recibiremos un mensaje diciendo "compilado ok" en la ventana "Salida VSM Studio". Si no sucede esto, repase con cuidado toda el código y vigile especialmente el uso de mayúsculas y minúsculas y los signos de puntuación.

    Lec01 006

    Volvamos a la pestaña "Esquema electrónico" y ejecutemos la simulación. Podremos observar que el led 8 de la barra de leds se enciende.

    Lec01 007

    Ya hemos terminado nuestro primer proyecto y hemos logrado encender un led de nuestra barra de leds.

    Practique ahora usted escribiendo el programa para encender el resto de las barras de nuestro dispositivo de leds.

    En esta primera lección no hemos entrado a fondo en el funcionamiento eléctrico de las salidas. Más adelante tendremos tiempo de profundizar en ello. En la sección de hardware podrá encontrar varios esquemas electrónicos con diferentes configuraciones de salidas del microprocesador.

     

     

  • Lección 10: Fundamentos del bus i2c

    [Proteus versión 8.1]

    Como vimos en la lección anterior, nuestro Arduino puede expandirse y aumentar significativamente sus capacidades utilizando el bus i2c. En esta lección vamos a introducirnos en los fundamentos de este bus.

    El bus I2C es un estándar diseñado por Philips, que facilita la comunicación entre diferentes microcontroladores, memorias y otros dispositivos con una cierta capacidad de contener lógica. Utiliza una tecnología de tipo bus. Es decir que sólo requiere dos hilos o pistas para intercomunicar todos los componentes entre sí, por los que se trasmiten los datos vía serie. En la imagen siguiente (sacada de un documento original de Philips) se puede observar la topografía de este sistema de comunicaciones en un ejemplo de utilización bastante complejo con muchas de las posibilidades que brinda (incluidos extensores de bus, utilización de múltiples maestros, etc.).

    Lec10 001

    Se utiliza uno de los dos conductores para manejar la sincronización mediante la generación de un tren de pulsos de reloj (SCL) y el otro para intercambiar los datos (SDA). Las dos líneas, SDA (Serial Data) y SCL (Serial Clock), se conectan a la fuente de alimentación a través de dos resistencias de pull-up. Cuando el bus está libre, ambas líneas están en nivel alto. Cuando algún dispositivo quiera utilizar el bus, lo hará poniendo en ambas líneas valores de alto o bajo según las necesidades. Como la línea está en descanso en nivel alto, la forma de actuar cada uno de los dispositivos sobre ella es ponerlo a masa y de esa manera forzar el nivel bajo en esa línea. La línea de reloj sólo la gobierna el maestro (puede haber varios maestros, pero en cada momento sólo un dispositivo actúa como tal). Mientras que la línea de datos la gobiernan tanto el dispositivo maestro como el esclavo que es interrogado en ese momento.

    El Maestro es el dispositivo que inicia (y gobierna mientra dura) la transferencia de datos por el bus. El bus está diseñado para permitir la participación de varios Maestros, ya que el protocolo incluye un sistema detector de colisiones. El maestro se encarga de generar la señal de reloj. El esclavo es el dispositivo direccionado que escucha y, si es necesario, responde al requerimiento del maestro. Cada dispositivo tiene una única dirección que lo identifica de modo unívoco dentro del bus, de tal manera que siempre está claro a qué dispositivo interroga el maestro y qué dispositivo debe responder a esa consulta. Si por error, configuramos dos dispositivos con la misma dirección se producirá una colisión en el bús y el sistema no funcionará correctamente.

    La cantidad de dispositivos que se pueden conectar al bus está limitada, unicamente, por la máxima capacidad permitida, que es de 400 pF y el número máximo de dispositivos que se pueden direccionar. Cada dispositivo puede operar como transmisor o receptor de datos, dependiendo de su función. Por ejemplo, un display es solo un receptor de datos. Y, como otro ejemplo, una memoria es un emisor y receptor de datos.

    Los bits de datos se transmiten por la línea SDA. Por cada bit de información se necesita un pulso del reloj en la línea SCL. El dato transmitido por la línea SDA sólo pueden cambiar cuando la línea SCL está a nivel bajo. Están prohibidas las transiciones de la línea SDA mientra la línea SCL esté en nivel alto. La única excepción a esta regla son las señales de arranque (start) y parada (stop).

    Lec10 009

    Sólo los maestros pueden iniciar una comunicación. La condición inicial, de bus libre, es cuando ambas líneas están en estado lógico alto. En esta situación cualquier maestro puede disponer del bus. Para hacerlo, establece la condición de inicio (start). Esta condición de inicio se produce cuando un maestro pone en estado bajo la línea de datos (SDA) y deja en nivel alto la línea de reloj (SCL).

    Lec10 002

    El primer byte que se transmite a continuación de la condición de inicio, está formado por siete bits que contienen la dirección del dispositivo con el que se desea establecer la comunicación, más un octavo bit que indica el tipo de operación que se establecerá con él (lectura o escritura).

    Lec10 003

    Si el dispositivo cuya dirección se corresponde con la indicada en los siete primeros bits (A0-A6) está presente en el bus, contestará con un bit bajo, ubicado inmediatamente a continuación del octavo bit que ha enviado el dispositivo maestro. Este bit de reconocimiento (ACK) en bajo le indica al dispositivo maestro que el esclavo reconoce la solicitud y está en condiciones de comunicarse.

    Lec10 004

    En este momento, en el que ya ha quedado claro quien es el maestro y cúal es el esclavo que debe mantener la comunicación, comienza el intercambio de información entre los dos dispositivos. Si el bit de lectura/escritura (R/W) fue puesto a nivel lógico bajo (escritura), el dispositivo maestro envía datos al dispositivo esclavo. Este proceso se mantiene mientras se reciban señales de reconocimiento por parte del esclavo. La comunicación concluye cuando se hayan transmitido todos los datos.

    Lec10 005

    Cuando el bit de lectura/escritura fue puesto a nivel alto (modo lectura), el maestro genera pulsos de reloj para que el dispositivo esclavo pueda enviar los datos. Después de cada byte recibido el maestro que está recibiendo los datos genera un pulso de reconocimiento.

     

    Lec10 006

    El maestro deja libre el bus generando una condición de parada (stop).

     

    Lec10 007

    También es posible un modo mixto, donde el maestro envía datos al esclavo y a continuación recibe datos de ese mismo esclavo. En este caso el maestro genera otra condición de inicio en lugar de una condición de parada. Esta nueva condición de inicio se denomina "inicio reiterado" y se puede emplear para direccionar un dispositivo esclavo diferente o para alterar el estado del bit de lectura/escritura.

    Lec10 008

    Lo más común en los dispositivos para el bus I2C es que utilicen direcciones de 7 bits, aunque existen dispositivos capaces de gestionar direcciones de 10 bits.  Una dirección de 7 bits implica que se pueden poner hasta 128 dispositivos sobre un bus I2C, ya que un número de 7 bits puede ir desde 0 a 127. La mayoría de los proyectos tienen más que suficiente con esta capacidad de direccionamiento y no es habitual encontrarnos con casos donde nos veamos comprometidos por esta limitación.  El hecho de colocar la dirección de 7 bits en los 7 bits más significativos del byte produce confusiones entre quienes comienzan a trabajar con este bus. Si, por ejemplo, se desea escribir en la dirección 21 (hexadecimal), en realidad se debe enviar un 42, que es un 21 desplazado un bit hacia arriba.

    La lección de hoy ha sido evidentemente teórica. Pero no debemos asustarnos. Hemos explicado todo esto porque una buena base teórica nos ayudará a entender cómo funciona nuestro bus i2c y, sobre todo, a detectar de donde provienen los problemas cuando se produzcan. Pero no hay que olvidar que las librerías de software disponibles nos facilitan toda la gestión del bus y nos permiten olvidarnos de todos estos entresijos a la hora de programar.

    En la lección anterior vimos que era muy fácil utilizar el bus i2c. En ella utilizamos un dispositivo i2c que nos proporcionaba 8 salidas digitales adicionales (el PCF8574). También vimos que podíamos utilizar el depurador de mensajes i2c incluído en Proteus. Con el depurador podíamos visualizar los mensajes que se transmitían por el bus. En concreto transmitíamos una secuencia de órdenes que iban encendiendo y apagando los leds situados en las ocho salidas de forma secuencial. En un momento dado, uno de los mensajes transmitidos era el siguiente:

    Lec10 010

    Ahora, a la luz de la teoría vista, ya podemos interpretar este mensaje:

    En primer lugar la 'S' simboliza el bit de arranque.

    Luego se trasmite el byte de valor 40 (0x40 en hexadecimal) que representa la dirección del esclavo. Si recordáis nuestro componente PCF8574 tenía la dirección 20 en hexadecimal (0x20). 0x20 es 10 0000 en binario. Si escribimos este número en binario en los siete bits superiores y le añadimos en el octavo bit el valor 0  para indicar que es una orden de lectura, tenemos el valor 100 0000 que en hexadecimal se corresponde con el valor 0x40.

    La 'A' que aparece a continuación es el pulso de reconocimiento (acknowledgement) que devuelve el esclavo para decir que está listo.

    El siguiente byte 00 (0x0 en hexadecimal) es el byte de datos que se transmite. Si visualizamos el programa de la lección anterior, veremos que este valor de byte va aumentando en cada nuevo mensaje para ordenar el encendido secuencial de los leds.

    Lec10 011

    La nueva 'A' que aparece detrás del byte del dato, es el nuevo pulso de reconocimiento que devuelve el esclavo para decir que ha recibido el dato correctamente.

    Por último, la 'P' es el bit de parada que manda el maestro para dar por concluido las comunicaciones.

    Sencillo ¿verdad? Depurar ahora nuestras comunicaciones i2c y descubrir los posibles errores es un poco más fácil.

    En posteriores lecciones aumentaremos nuestro conocimiento y utilizaremos de forma más intensiva las capacidades del bus i2c.

     

  • Lección 11: Medición de temperaturas utilizando un dispositivo i2c

    [Proteus versión 8.1]

    Ahora que ya hemos visto en la anterior lección la base teórica de la utilización del bus i2c, estamos en condiciones de ver un ejemplo práctico de utilización de un dispositivo i2c para medir temperaturas desde nuestro equipo Arduino. Esta selección tiene un evidente caracter pedagógico. Este dispositivo es configurable por software a través del bus i2c y devuelve el dato de la temperatura medida. Así podremos practicar una secuencia completa de escrituras y lecturas a través del bus i2c.

    Nuestra primera tarea consiste en seleccionar el dispositivo i2c más adecuado y comprobar sus características para aprender a utilizarlo en nuestro proyecto. En este caso, deseamos medir temperaturas. Así que vamos a utilizar el termómetro digital de la casa MAXIM (Dallas Semiconductor) referencia DS1621 que tiene un precio inferior a los 6,00€. Podemos encontrar fácilmente la información completa de este dispositivo en internet en la dirección: http://pdfserv.maximintegrated.com/en/ds/DS1621.pdf De todas formas, como internet es un 'animal vivo' donde los enlaces pueden variar con el tiempo, si no estuviera disponible en el momento de leer esta lección, podemos también realizar una búsqueda de este dispositivo con google que nos ayudará a encontrar la información de este dispositivo. Además, este componente tiene una ventaja adicional muy interesante para nosotros. Proteus incluye entre sus librerías un modelo completo de simulación para este elemento que nos permite ir fijando en tiempo real diversas temperaturas con ayuda de unos botones y de esa manera simular su funcionamiento real.

    Lec11 001

    Como se puede observar en la imagen, el modelo nos permite ver en pantalla la temperatura actual y modificarla a nuestro antojo con los botones con las flechas arriba y abajo.

    El segundo paso consiste en conectar nuestro dispositivo a nuestro Arduino a través del bus i2c. Para hacerlo, conviene recordar que el bus i2c se compone de dos hilos cada uno de ellos conectado a VCC a través de sendas resistencias de pull-up. El esquema completo de conexionado se muestra a continuación.

    Lec11 002

    Hemos conectado el bus (a la línea del reloj representada por el rótulo SCL y a la línea de datos representada por el rótulo SDA)  el dispositivo DS1621, el depurador del bus i2c y nuestro equipo Arduino. Observe que para conectar el Arduino al bus hemos unido el terminal AD5 a la línea SCL y el AD4 a la línea SDA. Además, hemos conectado un terminal virtual a los terminales TXD y RXD para poder enviar datos a la consola durante la ejecución de nuestro programa. En otro lugar de nuestro esquema el terminal TXD está unido al pin IO1 de nuestro arduino y el terminal RXD al pin IO0. Este es el mismo procedimiento que hemos utilizado en lecciones anteriores para usar el terminal virtual. De todas formas, a continuación, mostraremos el esquema completo para que no existan dudas.

    Lec11 003

    Una vez realizadas las conexiones del hardware, tenemos que direccionar correctamente nuestro termómetro digital. El DS1621 se direcciona de la siguiente manera (se puede consultar sus datos para comprobarlo). Como todo dispositivo i2c, utiliza siete bits para fijar su dirección. Los cuatro primeros bits adoptan siempre el valor fijo 1001. Los tres útlimos bits se confguran utilizando los pines A0, A1 y A2. Si el pin está conectado a VCC tomará el valor 1 y si está conectado a GND el valor 0. Por lo tanto la dirección del dispositivo será la siguiente: 1 0 0 1 A2 A1 A0. En nuestro caso el proyecto sólo utiliza un dispositivo DS1621, así que hemos conectado los tres pines a GND y la dirección resultante es: 1001000 (0x48 en hexadecimal y 72 en decimal).

    Ya podemos escribir nuestro primer programa.

    Lec11 004

    Lo hemos hecho expresamente corto, para ir siguiendo paso a paso todo el proceso y poder desgranarlo y comprenderlo perfectamente. El programa contiene una sentencia include para enlazar con la librería de Arduino que nos ayuda a gestionar el uso del bus i2c. Luego declaramos tres variables, una para almacenar el valor de la temperatura en grados centígrados, otra para guardarla en grados Farenheit y una tercera con la dirección de nuestro dispositivo i2c (acabamos de ver que su dirección es 0x48 en hexadecimal).

    En la sección de configuración, inicializamos el puerto serie donde conectamos nuestro terminal virtual para usarlo a una velocidad de 9600 baudios. Inicializamos también el bus i2c con la función wire.begin() de la librería wire.h.

    A continuación hacemos una cosa extraña, pero que la llevamos a cabo para que el lector pueda ver el uso del depurador del bus i2c paso a paso. Sólo utilizamos las funciones beginTransmission() y endTransmission(). A la primera le pasamos como parámetro la dirección de nuestro termómetro digital. Con esto logramos enviar un único telegrama a través de nuestro bus i2c. Un telegrama que básicamente es una orden de direccionamiento del maestro y que debe ser contestada por nuestro esclavo con un bit de reconocimiento (ACK).

    Ejecutemos la simulación y obtendremos lo siguiente:

    Lec11 006

    Cómo espérabamos en nuestro depurador se puede observar el único telegrama que hemos puesto en el bus: S 90 A P. Es decir, un bit de arranque (S). El byte de valor 0x90 que se corresponde con la dirección de nuestro dispositivo (0x48 = 100 1000) más un bit 0 a su derecha que indica que el telegrama es para escribir (1001 0000 = 0x90). Un bit de reconocimiento del esclavo (A). Y, por último, un bit de parada (P).

    Con esto hemos comprobado que todo funciona bien y que nuestro esclavo está direccionado según nosotros creemos.

    Cambiemos nuestro programa y sustituyamos el valor de la variable dirección por 0x49. Compilemos y arranquemos la simulación. Nuestro depurador del bus i2c mostrará el siguiente telegrama.

    Lec11 007

    Se puede ver que el bit de reconocimiento (A) se ha sustituido por un bit de no reconocimiento (N). Eso significa que hemos mandado un telegrama por el bus dirigido a un dispositivo con una dirección que nadie reconoce. De esta manera tenemos una forma sencilla de comprobar que el direccionamiento de los dispositivos que hemos puesto en nuestro proyecto se corresponde con el que nosotros esperábamos. Volvamos a dejar nuestro programa con la dirección correcta.

    Nuestro termómetro digital es susceptible, como ya dijimos, de ser configurado a través del bus i2c. Esto se logra mediante la escritura de una serie de comandos (se puede ver la lista completa de los comandos disponibles en la documentación del dispositivo). En nuestro caso nos interesa programar nuestro DS1621 para que esté realizando de forma continua lecturas de temperatura. Para hacerlo modificaremos nuestra función de configuración de la siguiente manera.

    Lec11 008

    Como es nuestra costumbre, añadimos unos comentarios para entender cada uno de los pasos que estamos dando. El comando 0xAC le indica al DS1621 que deseamos escribir en su configuración y el byte que enviamos a continuación es el valor de configuración que deseamos escribir. En este caso 0x02 siginifica que deseamos que el dispositivo esté leyendo las temperaturas de forma continuada. 

    A continuación volvemos a utilizar la función beginTransmission() para hacer un inicio reiterado y poder mandar un segundo comando. En este caso el comando es el 0xEE que ordena a nuestro dispositivo que comience con las lecturas de temperaturas. Por último finalizamos el telegrama.

    Si compilamos y ejecutamos la simulación, el resultado debe ser.

    Lec11 009

    A estas alturas ya tenemos que ser capaces de reconocer perfectamente lo que aparece en el depurador i2c.

    Todavía no estamos leyendo datos a través del bus. Sólo hemos escrito la configuración. Nuestro siguiente paso, será recibir las lecturas de temperatura y mostrarlas en el terminal virtual. Para ello modificaremos la función del bucle continuo de nuestro arduino.

    Lec11 010

    En este caso enviamos un nuevo telegrama de escritura en el esclavo para enviar el comando que fuerza la lectura de la temperatura. Para ello utilizamos la funciones beginTransmission, write y endTransmission de la librería wire.

    A continuación mandamos un nuevo telegrama para que el esclavo nos devuelva el valor de temperatura leído. Lo llevamos a cabo con la funcion requestFrom. El resultado de la lectura recogido por la funcion read, lo guardamos en la variable tempC. Para obtener el valor en grados Faranheit, utilizamos una sencilla fórmula matématica y el resultado lo guardamos en la variable tempF.

    El resto del programa es para enviar los resultados por el puerto serie hacia el terminal virtual.

    Si compilamos y arrancamos la simulación podremos ver en el terminal virtual las temperaturas léidas. Podemos cambiar la temperatura a leer con los botones de subir y bajar del dispositivo DS1621.

    Lec11 011

    También podemos observar en el depurador del bus i2c, los diferentes telegramas que se envían.

    Lec11 012

    El primero es el que se utiliza para escribir la configuración. Los otros van en parejas. Uno es de escritura (por eso la dirección es 0x90 para que el último bit sea 0) y el otro el de lectura (la dirección es 0x91 porque el último bit es 1).

    El valor leído dependerá de lo que hayamos introducido en nuestro dispositivo DS1621 con los botones. En nuestro caso se corresponde con una lectura de temperatura de 18ºC (0x12 en hexadecimal es 18 en decimal).

    Nuestro programa tiene un defecto. Si seleccionamos una lectura negativa (grados bajo cero) observar que lectura recibimos. Os dejamos como tarea para investigar, la solución de este problema.

    Además os invitamos a un nuevo reto. El DS1621 puede activar su pin 3 (TOUT) cuando la temperatura se encuentra entre un determinado rango de valores configurables. Esta función se llama termostato en contraposición a la función termómetro que hemos utilizado nosotros en este ejemplo. ¿Se anima el lector a intentar poner en marcha esta función para comprobar sus conocimientos sobre el dispositivo y la utilización del bus i2c?

    El curso se vuelve un poco más interactivo para hacerlo más interesante. Los que tengan la solución a nuestros dos problemas, las pueden compartir en nuestra página de facebook:

    https://www.facebook.com/pages/Hubor-Proteus/294446180592964?ref=hl

     

  • Lección 12: Control de un motor de corriente continua (DC)

    [Proteus versión 8.1]

    Antes de empezar con esta nueva lección, vamos a facilitar la solución a los problemas que planteamos en la anterior. El profesor German Tojeiro ha sido tan amable de hacernos llegar un vídeo donde ofrece una solución original y muy interesante. Utiliza una librería disponible de forma gratuita en la red para el control del dispositivo DS1621. La solución la podemos encontrar en su blog utilizando el siguiente enlace: http://arduinorastman.blogspot.com.es/2014/04/utilizacion-del-sensor-de-temperatura.html. Los que quieran, pueden acceder directamente al vídeo donde se muestra en vivo toda la elaboración del proyecto acudiendo directamente a youtube con el siguiente enlace: http://www.youtube.com/watch?v=wLyWQK3_CvY&feature=youtu.be.

    Ahora ya estamos en condiciones de pasar a la lección de hoy. Nos vamos a ocupar de controlar desde nuestro Arduino un motor de corriente continua (normalemente nos referimos a él utilizando la siglas DC). El motor DC es una máquina capaz de convertir la energía eléctrica en mecánica provocando un movimiento rotatorio.

    Lec12 001

    Su funcionamiento interno es sencillo y puede consultarse en diversas páginas de internet. Por ejemplo, podemos visitar la entrada disponible en wikipedia en el siguiente enlace: http://es.wikipedia.org/wiki/Motor_de_corriente_continua.

    Leccion12 002

    A todos aquellos que les guste la electrónica y hayan desmontado alguna vez algún dispositivo para ver qué hay por dentro, seguro que se han encontrado en muchas ocasiones con un motor de este tipo. Dentro de una unidad de disquetes, de un DVD, de un coche de juguete, de un ventilador manual, etc, hay un motor DC que genera el movimiento de las piezas móviles. Para ponerlo en marcha, la operativa es muy sencilla. Este tipo de motores tiene dos polos donde debemos conectar la alimentación. Si alimentamos con la tensión nominal (siempre con corriente contínua) al motor empieza a girar. Y aquí empieza una de las características más interesantes de este tipo de motores que los hace tan útiles para usarlos en nuestros diseños. Si bajamos la tensión de alimentación el motor girará más despacio y si intercambiamos la polaridad girará en sentido contrario. Como modificar la tensión y cambiar la polaridad son operaciones muy sencillas de implementar en nuestro diseño, las posibilidades que nos brinda este dispositivo son enormes y con muy poco esfuerzo.

    Proteus nos permite simular el funcionamiento de este tipo de motores, utilizando el dispositivo MOTOR.

    Lec12 003

    Vamos a ver cómo funciona para sacarle el máximo rendimiento a nuestra simulación. Colocamos un dispositivo motor en nuestro montaje de la siguiente forma.

    Lec12 004

    Y ahora sólo tenemos que arrancar la simulación, para observar el movimiento (en esta página la imagen aparece congelada en un momento dado, pero si está usted siguiendo la lección con Proteus, podrá ver el efecto del movimiento).

    Lec12 005

    Solo nos resta configurar adecuadamente nuestro modelo de motor para que la simulación sea más consistente. Para ello, nos pondremos sobre el icono del motor, pulsaremos el botón derecho del ratón y editaremos sus propiedades. En la ventana resultante podemos seleccionar la tensión nominal de nuestro motor (muy importante), el número de revoluciones con las que va a girar (ojo, estamos hablando de la simulación y poner velocidades muy altas cargará mucho la cpu de nuestro ordenador de forma totalmente innecesaria) y la resistencia interna.

    Lec12 006

    Si probamos a cambiar la alimentación intercambiando los +5V y el GND, podemos comprobar que el motor gira en el sentido contrario. Si cambiamos el potencial VDD (recuerde que son +5V) y lo fijamos a valores de +4V, +3V, +2V y +1V, podremos observar que el motor gira a menor velocidad. Como es lógico, si ponemos un 0V el motor se detendrá. Con esto, ya sabemos todo lo necesario sobre nuestro motor de corriente continua y estamos en disposición de comenzar a controlarlo con nuestro ARDUINO.

    En muchas webs y libros sobre ARDUINO encontraréis explicaciones de cómo controlar el motor DC directamente desde nuestro equipo ARDUINO utilizando una salida del tipo PWM que vimos en nuestra lección 6. Este sistema es perfectamente válido y controlaremos la velocidad de nuestro motor aumentando o disminuyendo el pulso de modulación. Pero, sin duda, el lector que siga nuestro curso será, a estas alturas, capaz de hacerlo por sí mismo siguiendo los pasos mencionados en aquella lección. Por ello, en esta ocasión vamos a utilizar un método alternativo y vamos a utilizar el driver para el control de motores L293D. Este chip (de coste muy reducido inferior a los 3€) nos permite controlar motores de DC (cuatro de forma unidireccional o dos de forma bidireccional) utlizando una técnica denominada puente en H. Utilizándolo lograremos controlar motores de mayor consumo y de tensiones diferentes a las que podríamos utilizar utilizando nuestro ARDUINO para controlar el motor de forma directa. En el siguiente enlace podemos encontrar toda la información técnica de este integrado: http://www.ti.com/lit/ds/symlink/l293d.pdf.

    Lec12 007

    Proteus incluye el modelo para poder simular el funcionamiento de nuestro driver L293D. Así que vamos a realizar el siguiente montaje para realizar nuestra práctica.

    Lec12 008

    Cómo se puede observar hemos conectado nuestro motor a la salidas OUT1 y OUT2 del L293D (porque lo vamos a utilzar de forma bidireccional). La alimentación del motor, en este caso, la estamos haciendo con el mismo potencial VDD aplicándolo al pin 8. Las entradas para gobernar la velocidad y el sentido de giro de nuestro motor (IN1 e IN1) del L293D las hemos conectados a las salidas IO11 e IO10 de nuestro Arduino. Además hemos conectado un led a la salida IO0 de nuestro Arduino por motivos pedagógicos para facilitar la interpretación al lector de la fase de nuestro programa en la que nos encontramos.

    El código de nuestro programa es el siguiente.

    Lec12 009

    El programa empieza con las declaraciones y configuraciones, según es habitual. La lógica de funcionamiento es la siguiente. Encendemos el led y ponemos el pin IO11 a cero, mientras vamos aumentando el valor del pulso de modulación de la salida PWM sobre el pin IO10 progresivamente desde 0 a 255 en saltos de 5 en 5. De esta manera el motor girará en un sentido cada vez a mayor velocidad. A continuación apagamos el led y vamos disminuyendo el pulso de modulación de forma progresiva hasta detener el motor. Más tarde repetimos la operación pero invirtiendo los pines. En este caso ponemos a cero el pin IO10 y aumentamos y disminuimos el pulso de modulación sobre el pin IO11 con lo que logramos que el motor acelere y luego disminuya su velocidad pero, esta vez, en sentido contrario de giro.

    Esperamos que esta lección os haya resultado útil. Seguro que encontráis muchas aplicaciones prácticas al uso de motores DC en vuestros diseños.

    Para los que quieran comprobar sus conocimientos, proponemos usar nuestro ARDUINO y dos drivers L293D para controlar una grua. Con uno de los motores podemos desplazar el brazo de nuestra grúa en sentido horario y antihorario. Con el segundo podemos deslizar el carretón desde el centro hasta el extremo. Y con el tercer motor podemos subir y bajar la carga.

    Por supuesto, tendremos que utilizar seis entradas digitales para implementar una botonera de mando para gobernar nuestra grúa en los dos sentidos posibles por cada uno de los tres movimientos disponibles. Además, si queremos perfeccionar nuestro trabajo, utilizaremos varias entradas digitales para controlar finales de carrera que nos avisen cuando llegamos al tope del recorrido de nuestro carretón impidiendo dañar el motor siguiendo empujando en una dirección cuando hemos alcanzado el final del recorrido posible. ¿Os animáis a mandarnos vuestras soluciones a nuestro facebook? https://www.facebook.com/pages/Hubor-Proteus/294446180592964

    Lec12 010

     

     

  • Lección 13: Control de un servomotor

    [Proteus versión 8.1]

    En la lección anterior aprendimos a gobernar un motor de corriente continua utilizando nuestro ARDUINO. Utilizando los motores de corriente continua somos capaces de lograr que nuestros proyectos gobiernen máquinas con movimiento. Por ejemplo podemos controlar un coche, una grúa o una unidad de CD. Los motores DC nos permiten variar la velocidad y el sentido de giro.

    Supongamos ahora que nuestra proyecto nos exige ser capaces de posicionar una pieza movil con una alta precisión. Por ejemplo, pensemos que utilizamos nuestro Arduino con dos motores para controlar la plumilla de un plotter capaz de realizar dibujos de un tamaño DIN A4. Utilizaríamos uno de los motores para generar el desplazamiento de la plumilla en la dirección del eje x y el otro para la dirección del eje y. Podríamos utilizar dos motores de corriente continúa como los vistos en la lección anterior para este trabajo. Pero imaginemos que queremos situar nuestra plumilla exactamente en la esquina superior izquierda de nuestra hoja de papel. La precisión se ha convertido en un asunto importante, porque desplazarnos fuera de la hoja, puede dañar nuestra plumilla. ¿Se imagina el lector, teniendo que ir dando pequeños toques en los pulsadores de control de nuestros dos motores para lograr situar la plumilla en el punto exacto? Seguro que dejamos de utilizar nuestro plotter.

    Para dar respuesta a esta problemática disponemos de los servomotores. Un servomotor es un motor con un eje de rendimiento controlado. Esto significa que podemos posicionarlo a nuestra voluntad con una gran precisión. Pero, eso sí, siempre dentro de un rango determinado. En general, los servomotores que podemos utilizar nos permiten usar rangos de 180º, aunque existen versiones de 210º e incluso los de rotación continua.

    Lec13 001

    Un servomotor es un motor de corriente continua con una serie de engranajes que aprovechan su velocidad para aumentar su torque (fuerza) y un sistema de control basado en un potenciómetro que permite saber constantemente la ubicación del eje. El sistema de control responde a las ordenes generadas con una señal que debemos proporcionarle para establecer la posición del eje deseada.

    Lec13 002

    La señal de control que introducimos al servomotor, es una señal parecida, que no igual, a la del tipo PWM. Para gestionar un servomotor vamos a generar un tren de pulsos con una frecuencia de 50Hz. Dependiendo del ancho de la zona alta del pulso que le enviemos (he ahí el parecido con las señales PWM), nuestro servo se situará en una posición o en otra. 

    Proteus nos facilita un modelo para simular un servomotor, llamado MOTOR_PWMSERVO. Construyamos un primer circuito para poder simularlo hasta entender perfectamente el funcionamiento del mismo. Utilizaremos un servomotor que alimentaremos conectándole los potenciales VDD y GND. Para la señal de control utilizaremos un generador de señal del tipo DPATTERN (Patrón de señales digitales).

    Lec13 003

    El segundo paso consiste en editar las propiedades del servomotor y de la señal de control. Como siempre, para hacerlo nos pondremos sobre cada uno de ellos y con el botón derecho del ratón abriremos el menú contextual y seleccionaremos la opción de editar sus propiedades. En el caso del servomotor tenemos que parametrizarlo teniendo en cuenta los siguientes aspectos. El rango de giro que nos permite nuestro servo. En nuestro caso vamos a elegir 0º como ángulo mínimo y 180º como el máximo ángulo posible. La velocidad máxima de giro. Vamos a seleccionar 50Hz. Y el ancho de la zona alta del pulso que vamos a utilizar para colocar nuestro servo en el ángulo mínimo y el que usaremos para indicar a nuestro servo que se coloque en el ángulo máximo. Unos valores bastante estandarizados son 0,544 mseg para el mínimo y 2,4 mseg para el máximo. Vamos a introducir estos valores utilizando su equivalente en microsegundos (544u para el mínimo y 2400u para el máximo). Al final de parametrizar nuestro componente debe tener el aspecto recogido en la siguiente imagen.

    Lec13 004 

    Para el generador de la señal de control, vamos a utilizar los siguientes valores:

    Lec13 005

    Como se puede observar estamos definiendo un tren de pulsos con forma de secuencia continua que empieza en una señal baja, con el primer flanco en el instante cero. El tiempo que permanecerá en alto el pulso será de 2.4 mseg (el que hemos definido como posicionarse en el ángulo máximo de 180º y el tiempo que permanecerá bajo será de 17,6 mseg. Para obtener esta segunda cifra hemos realizado el siguiente cálculo. Puesto que la señal de control es de 50Hz, el tiempo de cada pulso debe ser de 20 mseg (1 seg / 50 = 20 mseg). Si el pulso debe estar en alto 2,4 mseg, el resto del tiempo debe estar bajo (20 mseg - 2,4 mseg = 17,6 mseg).

    Ahora que ya hemos explicado cómo se configura la señal de control, podemos arrancar la simulación y comprobaremos que el servomotor se coloca en la posición deseada (180º).

    Lec13 006

    En general, podemos fijar el ángulo deseado de nuestro servomotor, calculando el tiempo del flanco alto y bajo de nuestra señal de tren de pulsos de control con estas dos fórmulas:

    tiempo flanco alto = 0,544 + n ( 1,856 / 180 ) donde n es el ángulo en grados.

    tiempo flanco bajo = 20 - tiempo flanco bajo.

    Probemos a colocar nuestro servo en la posición 0º (0,544 y 19,456), en la posición 45º (1,008 y 18,992) y en la posición 90º (1,472 y 18,528). Realmente es sencillo colocar nuestro servomotor en la posición que deseemos.

    Ahora que ya entendemos completamente como funciona un servomotor, vamos a realizar el siguiente circuito para controlarlo con nuestro Arduino.

    Lec13 007

    Y nos enfrentaremos ahora a la tarea de programar la señal de control. En un primer momento podría parecer que generar nuestra señal de control desde Arduino no va a resultar una tarea sencilla. Afortunadamente contamos con una librería (servo.h) que nos pondrá las cosas muy sencillas. Podemos consultar todas sus funciones en el siguiente enlace (http://arduino.cc/es/Reference/Servo). Vamos a utilizar el siguiente código de ejemplo para controlar nuestro servomotor y, como siempre, iremos explicando cada línea de nuestro programa.

    Lec13 008

    La sentencia #include  <servo.h> sirve para enlazar con la librería servo. A continuación definimos dos variables. Una llamada myservo del tipo servo (este tipo está definido en la propia librería) que utilizaremos para controlar nuestro servomotor. Si nuestro Arduino controlara más de un servomotor, tendríamos que definir una variable de este tipo por cada uno de los servomotores que deseamos controlar. Además usaremos una segunda variable entera llamad ángulo que usaremos para fijar el ángulo al que deseamos situar nuestro servomotor.

    En la función de configuración (setup), definiremos los pines que vamos a utilizar para generar nuestra salida de control (pin IO9) y el que vamos a utilizar para controlar un led que nos ayudará a visualizar en que fase del programa nos encontramos (el pin IO0).

    En el bucle principal del programa (loop) realizaremos la siguiente secuencia de control. Primero encendemos el led, luego vamos aumentando el valor del ángulo de posición de nuestro servomotor desde 0º a 180º en saltos de 10 en 10 grados, dejando un retardo de 1 segundo entre cada nuevo posicionamiento. A continuación apagamos el led y vamos disminuyendo el ángulo de posición desde 180º a 0º pero esta vez en saltos de 5 en 5 grados. La función de la librería servo.h utilizada para posicionar el servomotor en el ángulo que deseemos es write().

    Arranquemos nuestra simulación y comprobemos que todo funciona como deseamos.

    Lec13 009 

    Utilizar un servomotor desde nuestro ARDUINO es muy sencillo.

    Construyamos ahora un segundo circuito donde utilizaremos nuestro terminal virtual para poder enviar órdenes a nuestro ARDUINO y que éste nos porporcione información usándolo como consola.

    Lec13 010

    Para que el terminal virtual funcione bien, debemos configurarlo como se muestra en la imagen siguiente.

    Lec13 011

    El código de nuestro programa de control será el siguiente:

    Lec13 012

    En este caso esperamos que el usuario pulse un número entre 0 y 9 y colocamos el servomotor en el ángulo correspondiente (el número multiplicado por 10). Dejamos al usuario que estudie el programa para entenderlo bien. Es importante tener en cuenta cuando arranquemos la simulación, que para poder enviar teclas desde nuestro teclado por el terminal virtual tenemos que poner el ratón encima y pulsar sobre él para activarlo. Si no se visualizara el terminal virtual al arrancar la simulación podemos ordenar que se muestre desde el menú depuración (debug). El resultado si pulsamos la tecla 8 sería:

    Lec13 013

     

    De esta manera, en este ejemplo, no tenemos un servomotor cambiando constantemente de posición como en el primer caso. En este nuevo ejemplo, el uso del servomotor es más parecido a la realidad. El usuario cada vez indica desde el terminal la posición en la que deseamos colocar el servomotor, ARDUINO se encarga de generar la orden y el servomotor permanece en esta posición hasta que se recibe una nueva orden.

    Si no podemos gobernar nuestro ARDUINO desde la consola, podemos utilizar un potenciometro para indicar la posición a la que deseamos colocar nuestro servomotor. A continuación mostramos el circuito y el código del programa. A estas alturas del curso seguro que el lector no encuentra problemas para comprender ambos.

    Lec13 014

    Lec13 015

    En este caso, moviendo el potenciometro, recolocamos nuestro servomotor según deseemos. Si giramos el potenciometro en uno u otro sentido, el ángulo de posicionamiento del servomotor se incrementa o decremente. Seguro que el lector encontrará muchas aplicaciones para los servomotores.

     

     

  • Lección 14: Operaciones matemáticas

    [Proteus versión 8.1]

    Hasta ahora el curso se ha centrado más en la utilización de nuestro Arduino y la serie de componentes que giran a su alrededor. Pero en esta ocasión hemos querido dedicar esta lección a un concepto puramente de sotware: las operaciones matemáticas. El uso de operadores matemáticos y de funciones matemáticas es una tema fundamental en la mayoría de los proyectos desarrollados con Arduino porque son los que nos posibilitan la realización de los diferentes cálculos necesarios para llevar a buen fin nuestro proyecto.

    Los lectores que estén familiaraizados con la programación en C o C++ podrán comprobar que los operadores y las funciones matemáticas son iguales que los que se utilizan en estos lenguajes de programación. Como suele ser habitual en el tratamiento de este tema en todos los libros de texto, vamos a estudiar los operadores matemáticos agrupados por familias básicas. Para poder practicar con su uso vamos a realizar el siguiente montaje, dónde simplemente se utiliza un terminal para poder presentar por pantalla los valores resultantes de nuestras operaciones.

    Lec14 001

    Operadores suma, resta, multiplicación y división.

    Sirven para realizar las cuatro operaciones fundamentales y se utilizan los operadores universales para estas operaciones +, -,* y /. Para comprobar su funcionamiento vamos a utilizar el siguiente programa muy sencillo que nos muestra su uso.

    Lec14 002

    Hemos utilizado la función setup() en lugar de la habitual loop() para que las operaciones no se estén realizando de forma continua. El resultado de compilar y ejecutar nuestro programa es el mostrado en la imagen siguiente, donde se pueden ver el resultado de las cuatro operaciones matemáticas básicas.

    Lec14 003

    Podemos utilizar paréntesis para indicar qué operaciones se deben llevar a cabo con anterioridad a otras.

    Lec14 004

    Operadores Incrementadores y Decrementadores.

    Los operadores para incrementar o decrementar el valor de una variables son += y -=. Su uso equivale a las siguientes sumas y restas:

    resultado += 5  --> resultado = resultado + 5

    resultado -=3  --> resultado = resultado - 3.

    Su utilización se muestra en el siguiente ejemplo.

    Lec14 005

    También se pueden utilizar los operadores para incrementar y decrementar una variable un número 'n' de veces. Es decir usar los operadores *= y /= para multiplicar o dividir una variable por un número. El equivalente con los productos y divisiones sería:

    resultado *=5 --> resultado = resultado * 5

    resultado /= 3  --> resultado = resultado / 3

    Un código que utiliza estos operadores sería:

    Lec14 006

    Para terminar esta sección, veremos los operadores ++ y --. Sirven para incremetar o decrementar una unidad el valor de la variable. El equivalente con las sumas y restas serían:

    resultado++ ---> resultado = resultado + 1

    resultado-- ---> resultado = resultado - 1

    Un programa de ejemplo sería:

    Lec14 007

    Operador Resto de una división.

    El operador resto (%) nos proporciona el resto resultante de dividir dos números. El ejemplo de utilización se muestra en la siguiente imagen.

    Lec14 008

    En este caso el resto es 0.

    Lec14 009

    Y en este caso el resto es 1.

    Un sencillo ejemplo de utilización sería el siguiente código que comprueba si un número es par o impar.

    Lec14 010

    Función valor absoluto.

    Para calcular el valor absoluto de una variable podemos utilizar la función abs( <operando> ) que nos devulve una variable de tipo entero que se corresponde con el valor absoluto del operando. El ejemplo de uso se muestra a continuación.

    Lec14 011

    El resultado devuelto será 15. Es importante caer en la cuenta que en la programación de Arduino se utiliza el punto '.' como separador decimal y no la coma ','.

    Función potencia.

    Para calcular el resultado de elevar un determinado número x a una potencia y, la función que tenemos que utlizar es pow( <base> , <exponente>). Su uso se muestra en el siguiente código.

    Lec14 012

    Para calcular la raiz cuadrada de un número utilizaremos la función sqrt( <operando>).

    Lec14 013

    Números decimales y varibles tipo float.

    Hasta ahora hemos utilizado números enteros en todos nuestros ejemplos. Sin embargo, es muy habitual que los cálculos matemáticos den como resultados números decimales y no enteros. Para manejarlos, Arduino nos permite utilizar dos tipos de variables: float y double. En Arduino se mantienen los dos tipos por compatibilidad con C y C++, pero es importante tener en cuenta que no hay diferencia entre ambos. Los dos permiten utilizar números decimales entre el intervalo 3.4028235e+38 y -3.4028235e+38.

    En informática, para gestionar números decimales utilizamos la técnica denominada de punto flotante. La razón de ello es que la memoria limitada de nuestros equipos no nos permiten guardar una gran cantidad de números con la precisión necesaria para almacenarlos con todas sus cifras decimales. Por ello, se recurre a la mencionada técnica que consiste en descomponer el número en dos partes y almacenarlas juntas. Por un lado se almacena la mantisa (también llamada coeficiente o significando) que contiene los dígitos del número. Mantisas negativas representan números negativos. Por otro lado se almacena el exponente que indica dónde se coloca el punto decimal (o binario) en relación al inicio de la mantisa. Exponentes negativos representan números menores que uno. Este formato cumple las siguientes exigencias:

    • Puede representar números de órdenes de magnitud enormemente dispares (limitados a la longitud del exponente).
    • Proporciona la misma precisión relativa para todos los órdenes (limitadoa a la longitud de la mantisa).
    • Permite cálculos entre magnitudes: multiplicar un número muy grande y uno muy pequeño conserva la precisión de ambos en el resultado.

    Los números de coma flotante normalmente se expresan en notación científica con un punto explícito siempre entre el primer y el segundo dígitos. El exponente o bien se escribe explícitamente incluyendo la base, o se usa una e (indiferentemente en mayúsculas o minúsculas) para separarlo de la mantisa. Ejemplos de uso son:

    El número float escrito 10.0 equivale a 10

    El número float escrito 10.324 equivale a 10,324

    El número float escrito 2.34E5  equivale a 2.34 * 10^5  y su valor es 234000

    El número float escrito 67e-12  equivale a 67.0 * 10^-12  y su valor es 0.000000000067

    El número float escrito 3.45e-5 equivale a 3.45 * 10^-5 y su valor es 0.0000345

    Es importante tener en cuenta las siguientes limitaciones en el uso de números de coma flotante:

    • Los float tienen una precisión de 6 o 7 dígitos decimales. Esto significa el número total de dígitos, no el número a la derecha de la coma decimal. Al contrario que en otras plataformas, donde tu podrías obtener mayor precisión usando una variable tipo double (por ejemplo, por encima de 15 dígitos), en Arduino los double tienen el mismo tamaño que los float.
    • Los números en coma flotante no son exactos, y muchos proporcionan falsos resultados cuando son comparados. Por ejemplo, 6.0 / 3.0 puede no ser igual a 2.0. Debes comprobar que el valor absoluto de la diferencia entre los números pertenezca a un rango pequeño.
    • La matemática en coma flotante es mucho más lenta que la matemática de enteros para realizar operaciones, por lo que deberías evitarla si, por ejemplo, un bucle tiene que ejecutarse a la máxima velocidad para funciones con temporizaciones precisas. Los programadores normalmente suelen asignar unas longitudes para convertir las operaciones de coma flotante en cálculos con enteros, para aumentar la velocidad.

    Es importante dedicar un tiempo a estudiar y practicar el uso correcto de los números en coma flotante (float) porque tiene singularidades, como acabamos de ver, que conviene tener en cuenta.

    Además, es importante tener en cuenta que cuando utilizamos las funciones print o println para mostrar una variable de tipo float, hay que utilizar la siguiente sintaxis para no ver truncados los valores decimales, porque por defecto print y println sólo muestran dos posiciones decimales. Un ejemplo nos ayudará a entender la diferencia.

    Lec14 014

    Lec14 015

    funciones trigonométricas.

    Las funciones trigonométricas básicas se pueden calcular utilizando las funciones sin( <angulo> ), cos(<angulo> ) y tan( <angulo> ). Con ellas podemos calcular el seno, el coseno y la tangente de un ángulo dado. Es importante tener en cuenta que las tres funciones esperan que el ángulo se exprese en radianes. El radián es la unidad de ángulo plano en el Sistema Internacional de Unidades. Representa el ángulo central en una circunferencia y abarca un arco cuya longitud es igual a la del radio. Por tanto, el ángulo completo de una circunferencia de radio r, medido en radianes es 2Π radianes.

    Lec14 016

    Si preferimos pasar el ángulo expresado en grados sexagesimales, podemos convertirlos facilmente utilizando la constante DEG_TO_RAD definida por defecto cuando utilizamos el sistema de desarrollo de ARDUINO. El siguiente programa nos facilita la conversión de grados sexagesimales a radianes de todos los valores comprendidos entre 0º y 360º en saltos de 5 en 5.

    Lec14 019

    El resultado en el terminal será:

    Lec14 020

    Veamos ahora un ejemplo de utilización de las funciones vistas para calcular el seno, el coseno y la tangente de 45º. Observe que utilizamos la función println con la cláusula DEC para no truncar los números decimales.

    Lec14 017

    El resultado obtenido en el terminal virtual debe ser:

    Lec14 018

    Con lo visto en esta lección, el lector ya está en disposición de enfrentarse con aquellos proyectos que necesitan realizar cálculos matemáticos.

     

     

  • Lección 15: Uso de un teclado matricial

    [Proteus versión 8.1]

    En la lección de hoy vamos a aprender a controlar un teclado matricial desde nuestro equipo Arduino. Un teclado matricial es un dispositivo compuesto de una serie de pulsadores conectados de forma matricial (en filas y colúmnas).

    lec15 001

    La ventaja de disponerlos de esta manera es que se pueden leer la información de múltiples botones utilizando un número reducido de pines de nuestro microprocesador. Así, por ejemplo, un teclado matricial de 4×4 que se compone de 16 pulsadores, solamente necesita 4 pines para las filas y otros 4 pines para las colúmnas. De esta forma se pueden leer las 16 teclas utilizando solamente 8 pines, en lugar de los 16 que serían necesarios utilizando otra configuración.

    lec15 002

    lec15 003

    El sistema en el que se basa el funcionamiento de un teclado matricial es el multiplexado. Configuramos cuatro pines de nuestro microprocesador como entradas y otros cuatro como salidas. Conectamos las entradas a las columnas y las salidas a las filas (o viceversa porque el funcionamiento sería el mismo). Empezamos con las cuatro salidas en nivel bajo. Activamos la salida de la primera fila y leemos las cuatro entradas. Cada una de ellas nos informará del estado de los cuatro pulsadores de esa fila. La entrada 1 el pulsador de la columna 1, la entrada 2 el pulsador de la columna 2 y así sucesivamente. A continuación, desactivamos la salida 1 y activamos la 2. En este momento en las cuatro entradas podemos leer el estado de los pulsadores situados en la segunda fila. Haremos lo mismo con las salidas 3 y 4 para obtener el estado de los pulsadores situados en las filas 3 y 4.

    A continuación, vemos un esquema del momento en que hemos activado nuestra salida 2 (para leer el estado de los pulsadores de la segunda fila) y vemos como, si estuviera actuado el pulsador 6 (fila 2, columna2), tendríamos la entrada de la columna 2 activa.

    lec15 004

    Afortunadamente, no tenemos que preocuparnos de la programación de nuestro multiplexor, porque Arduino ya nos facilita librerías para ello. Así que el uso de un teclado matricial es muy sencillo. Proteus, también nos facilita varios modelos de teclados matriciales para poder llevar a cabo la simulación. Un teclado de calculadora reducido (keypad-smallcalc), un teclado de calculadora más amplio (keypad-calculator) y uno de teléfono (keypad-phone).

    lec15 005

    Cada uno de ellos difiere de los otros, no solo en el serigrafiado de los botones, sino también en el número de pulsadores y, consecuentemente, en el número de pines de entrada y salida necesarios para gobernarlos. Nosotros vamos a utilizar en nuestra práctica el teclado telefónico, aunque al lector no le costará nada aplicar luego los conocimientos adquiridos para gestionar cualquier otro tipo de teclado. El esquema que vamos a utilizar se muestra en la imagen siguiente:

    lec15 006

    La librería que viene en nuestra ayuda se llama keypad.h. Esta librería no viene de forma estándar con la distribución del compilador de Arduino. Así que tenemos que descargarla desde la web. La podemos encontrar en el siguiente enlace:

    http://playground.arduino.cc/code/Keypad

    Descargaremos el fichero comprimido zip a nuestro ordenador y lo desempaquetamos. Con esta operación obtendremos una carpeta llamada keypad. Esta carpeta la copiaremos en la carpeta 'Libraries' del directorio donde se encuentra instalado el compilador Arduino en nuestro equipo. En nuestro caso la ruta de acceso es:

    Archivos de programa /(x86)/Arduino/libraries. 

    lec16 006

    Una vez hecho esto ya podemos escribir nuestro código. Vamos a realizar una aplicación que nos permite introducir con ayuda de nuestro teclado una clave de cuatro dígitos que nos posibilitará abrir o cerrar una caja fuerte. Si la clave introducida coincide con el número almacenado, encenderemos un led verde (simulamos que abrimos la caja). En caso contrario, encenderemos un led rojo (simulamos que cerramos nuestra caja fuerte).

    lec16 007

    Lo primero que hacemos es enlazar con la librería keypad. A continuación definimos una serie de constantes que utilizamos para indicar el número de filas y columnas de nuestro teclado. Y luego dos matrices que usamos para indicar los pines a los que conectaremos cada fila y cada columna.

    lec16 008

    A continuacíón creamos una serie de variables que utilizamos para diversas operaciones de control.

    lec16 009

    La variable position la utilizaremos para ir ordenando las cifras que componen nuestra clave. La variable redPin almacena el pin de salida donde conectamos nuestro led rojo. La variable greenPin almacena el pin de salida donde conectaremo nuestro led verde. La variable secretCode contiene nuestra clave válida. Nosotros hemos puesto 2468. Luego generamos una matriz para identificar cada una de las teclas con el símbolo que tiene rotulado. Por último creamos un objeto del tipo keypad (este tipo se define en la librería keypad.h) que nos permite inicializar todos los datos del objeto que controla nuestro teclado. Como se ve, para inicializar el objeto, se llama a la función keypad() y se le pasa cinco parémetros. El primero es una llamada a la función makeKeymap() que, a su vez, recibe como parámetro la matriz con los símbolos serigrafiados. El segundo y tercer parámetro son las matrices que contienen los pines a los que conectamos las columnas y filas. Por último, el cuarto y quinto parámetro son las variables con el número de filas y columnas de nuestro teclado.

    Como se ha visto, la librería es muy fácil de adaptar a diversas configuraciones de teclados matriciales, simplemente cambiando las definiciones de las matrices keys, rowPins, colPins y de las variables rows y cols.

    El siguiente paso es inicializar nuestro equipo utilizando la función setup().

    lec16 010

    En este caso, sólo configuramos los pines a los que hemos conectado los dos leds, como pines de salida y realizamos una llamada a la función setLocked() pasando como parámetro la constante true. La función setLocked() se encuentra situada al final de nuestro código.

    lec16 011

    Su funcionamiento es muy sencillo. Cuando la llamamos pasando como parámetro true, encendemos el led rojo y apagamos el verde (no se abrió la puerta). Si pasamos como parámetro false, encendemos el led verde y apagamos el rojo (se abrió la puerta). Por eso, de mano, la hemos llamado pasando como parámetro true, para indicar que empezamos con la puerta de nuestra caja fuerte cerrada.

    Veamos ahora el último trozo de código que está contenido en la función loop().

    lec16 012

    Primero asignamos el resultado del método getKey() de nuestro objeto keypad a la variable key. De esta manera tan sencilla la librería nos indica qué botón de nuestro teclado matricial ha pulsado el usuario. A continuación evaluamos si se ha pulsado las teclas '*' o '#'. En caso afirmativo, ponemos el contador de cifras introducidas a cero y cerramos la caja llamando a la función setLocked() con el parámetro true.

    Si la tecla introducida es una diferente a estas dos (es decir, cualquiera que tenga una cifra serigrafiada), comprobamos si el valor de la tecla introducida coincide con la el valor del código secreto de esa misma posición. En caso afirmativo pasamos a la segunda cifra y así sucesivamente hasta llegar a la cuarta cifra.

    Cuando hemos acertado con las cuatro cifras de nuestra clave secreta, abrimos la caja llamando a la función setLocked() pasándole el parámetro false.

    Vamos a compilar nuestro programa para ver como funciona. Si es la primera vez que utilizamos nuestra librería keypad.h encontraremos un mensaje de error porque Proteus no sabe que esa carpeta se ha añadido a nuestro proyecto. Para solventarlo tenemos que utilizar la opción de menú Construir->reconstruir el proyecto.

    lec16 013

    Ahora ya se habrá compilado correctamente si no nos hemos equivocado al escribir el código de nuestro programa. Ya podemos arrancar la simulación y observar que nuestra caja empieza cerrada (led rojo encendido).

    lec16 014

    Ahora ya podemos probar a meter la clave secreta en nuestro teclado pulsando sobre las teclas 2, 4, 6 y 8. Si lo hacemos en el orden correcto veremos cómo se abre nuestra caja fuerte (led verde encendido).

    lec16 015

    Para volver a cerrar la caja o para poner el contador de cifras a cero, sólo tenemos que pulsar los botones '*' o '#'.

    Es evidente que no iremos muy lejos con nuestra caja fuerte si el funcionamiento real fuera como el que se muestra en esta lección. El programa sólo pretende mostrar el funcionamiento de un teclado matricial. 

    Como ejercicio, proponemos en esta ocasión utilizar cualquiera de los otros teclados y realizar algún proyecto controlándolos desde nuestro Arduino.

    Esperamos que la lección os haya resultado interesante.

     

  • Lección 16: Uso de un display LCD de texto

    [Proteus versión 8.1]

    En la lección de hoy vamos a aprender a mostrar información utilizando un display LCD de texto. Un display LCD (siglas del inglés liquid crystal display) o pantalla de cristal líquido, es una pantalla delgada y plana formada por un número de píxeles, en color o monocromos, colocados delante de una fuente de luz o reflectora. Su principal ventaja es que utiliza cantidades muy pequeñas de energía eléctrica.

    Lec16 001

    Existen pantallas muy variadas en el mercado. Las hay de colores diferentes (monocromo verde, monocromo ámbar o color). Unas tienen capacidad para mostrar sólo caracteres de  texto y otras admiten gráficos. Las hay que se controlan utilizando varios pines de nuestro Arduino (normalmente dos para el control y otros 4 para los datos) o que se comunican con él mediante comunicaciones vía serie. Y, además, las podemos encontrar de  tamaños muy distintos.

    Lec16 002

    En esta lección, nos vamos a centrar en aquellas que son monocromo y sólo pueden mostrar caracteres de texto. Proteus pone a nuestra disposición una amplia variedad de pantallas de este tipo para que nuestra simulación se adapte lo más precisamente posible a las necesidades de nuestros proyectos.

    Lec16 003

    En este proyecto vamos a utilizar el modelo LM016L que nos permite simular una pantalla de dos filas de 16 caracteres cada una. Para controlarla utilizaremos 6 pines de nuestro Arduino (dos para el control y cuatro para los datos). El esquema de conexionado se muestra en la siguiente figura.

    Lec16 004

    Al estar utilizando un dispositivo virtual no tenemos que preocuparnos de gestionar la luminosidad de nuestra pantalla (normalmente se puede graduar para que luzca con más o menos intensidad mediante la utilización de un potenciómetro).

    Además, para simplificar aún más las cosas, en Arduino disponemos de una librería llamada LiquidCrystal.h que nos permite gestionar de forma muy sencilla nuestra pantalla. De esta manera nos podemos olvidar de las complejidades derivades de todas las escrituras a bajo nivel con la correspondiente gestión de activación y desactivacióin de los pines. 

    Vamos a escribir un sencillo programa que muestra en la pantalla un contador de segundos. Con la ayuda de la librería el código es así de sencillo:

    Lec16 005

    Al principio se realiza una llamada a la librería LiquidCrystal.h. A continuación utilizamos dos variables que nos ayudan a definir las filas y columnas de nuestra pantalla. Configuramos los pines de Arduino que vamos a utilizar para controlar nuestra pantalla con la función lcd() de la librería.

    En la función setup() aprovechamos para inicializar nuestra pantalla con 2 filas y 16 caractéres y escribir en la primera fila de nuestra pantalla un mensaje que muestre el rótulo "segundos:"

    En la función loop() situamos el cursor en el primer carácter de la segunda fila (tener en cuenta que siempre empezamos a contar desde la posición 0, por eso el primer carácter ocupa la posición 0 y la segunda línea la posición 1). A continuación escribimos en dicha posición la lectura del contador interno del Arduino de los milisegundos que han transcurrido desde que lo encendemos (utilizando la función millis() ) y para obtener la cuenta en segundos dividimos por 1000 ese valor.

    Si compilamos y generamos la simulación, tendremos un contador de segundos en nuestra pantalla de cristal líquido.

    Lec16 006

    El control de una pantalla no puede ser más sencillo. Simplemente hay que configurarlo al principio y luego, cada vez que deseamos refrescar la información presentada al usuario, tenemos que colocar el cursor en el lugar deseado y escribir la cadena de texto correspondiente.

    Para completar la lección, vamos a realizar un segundo proyecto. Vamos a añadir tres pulsadores a nuestro anterior montaje. Así que ahora tenemos el siguiente diseño electrónico:

    Lec16 007

    El programa que vamos a escribir ahora, nos permitirá tener un reloj en nuestra pantalla y los tres pulsadores nos servirán para configurar la hora, minuto y segundo que deseemos. El código es el siguiente:

    Lec16 008

    Lec16 009

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    El esquema es el mismo: configuramos nuestra pantalla y escribimos el mensaje de la primera línea que permanece siempre fijo. Como el bucle se ejecuta cada 1 segundos, cada iteración vamos sumando un nuevo segundo. Cuando llegamos a 60 segundos, incrementamos en una unidad los minutos. Cuando llegamos a 60 minutos, incrementamos en una unidad las horas. Por último, controlamos la acción del usuario sobre los pulsadores para incrementar los segundos, minutos u horas.

    El resultado de compilar y ejecutar la simulación se muestra a continuación.

    Lec16 010

    Observe que el programa no reacciona de forma inmediata al uso de los pulsadores. La razón es que tenemos la mayor parte del tiempo a nuestro programa parado en el delay situado al final del código. Por eso hay que tener pulsado el botón y ver que tarda un segundo en reaccionar. Más adelante en otra lección veremos cómo podemos solventar este problema de forma satisfactoria.

    Esperamos que la utilización de pantallas LCD de texto ya sea un asunto sin secretos para el usuario.

     

  • Lección 17: Funciones de tiempo

    [Proteus versión 8.1]

    En la lección anterior (Lección 16) descubrimos los problemas que resultaban de utilizar la función delay(). Cuando tenemos un código de varias líneas que contiene una instrucción delay(1000) para esperar a que pase un segundo, nos encontramos que nuestro Arduino está inoperativo la mayor parte del tiempo.  Ejecutar el resto del código le puede llevar apenas unos milisegundos y tiene que estar esperando el resto del tiempo sin capacidad de responder a ninguno de los eventos que suceden a su alrededor. Por esa razón, nuestros pulsadores para cambiar la hora, minuto y segundo no respondian de forma satisfactoria.

    Además, en la lección 2 tuvimos ocasión de ver cómo se usaba también una instrucción delay() para lograr intermitencias de leds. Seguro que el lector ha visto ejemplos similiares en muchos otros cursos de Arduino. Sin embargo esta técnica tiene un segundo problema añadido al mencionado. Supongamos que queremos generar intermitencias en varios leds con frecuencias diferentes (uno parpadea cada un segundo y otro cada dos y medio, por ejemplo). Evidentemente utilizando la técnica de la función delay() es imposible.

    Lec17 001

    Por eso, en esta lección vamos a abordar la construcción de funciones de tiempo con una filosofía diferente que solucione los dos problemas anteriores. Empezaremos, como es habitual en este curso, con un mínimo de teoría. El primer concepto importante para entender las funciones de tiempo es el de "timer". En general, todos los microprocesadores tienen disponibles varios temporizadores (timer). En concreto el Arduino Uno tiene tres: timer0, timer1, timer2. Un timer no es más que un contador cuya entrada está conectada al reloj del sistema y que nos permite "contar" los tics (ciclos de reloj) que han transcurrido. 

    Nota aclaratoria: Por defecto la señal que van a contabilizar los timers corresponde a la frecuencia del oscilador dividida por cuatro. Por lo tanto, en realidad cuentan ciclos máquina, no ciclos de reloj. Con un reloj de 20 Mhz tendríamos una frecuencia de ciclos máquina de 20/4 = 5 MHz, por lo que un ciclo máquina corresponde a 0.2 usec. En principio, el contador del timer de un micro que funcione a 20Mhz se incrementará cada 0.2 microsegundos o 5 veces en 1 usec. De todas formas, el lector podrá comprobar que con el método propuesto en esta lección puede olvidarse de estas consideraciones.

    Lec17 002

    El segundo concepto que necesitamos conocer para construir nuestras funciones de tiempo, es el de interrupción de timer. Básicamente, todos los microprocesadores nos permiten generar interrupciones asociadas a los timer. Eso significa que podemos configurar nuestro Arduino para que se genere una "interrupción" cada vez que un timer ha contado un número concreto de tics. Como sabemos la frecuencia con la que se produce cada tic, evidentemente, podemos decir que se genere una interrupción cada vez que hayan transcurrido una serie de "unidades de tiempo" determinadas. Estas "unidades de tiempo" pueden ser microsegundos, milisegundos, centésimas, décimas o segundos. La gran ventaja de utilizar interrupciones, es que todo el código que se esté ejecutando se detiene cada vez que se produce una interrupción y se ejecuta el código que hayamos escrito para "atender" a la interrupción. Por ello la precisión obtenida es muy grande.

    Lec17 003

    Por lo tanto, con los timer y las interrupciones podemos lograr construir un reloj (con la precisión que necesitemos acorde a nuestras necesidades) independiente del código de nuestro programa y no sujeto a que el micro esté con más o menos carga de trabajo. La técnica propuesta consiste en mantener un "reloj maestro" que sepa exactamente el tiempo transcurrido en cada momento y utilizar tantos "relojes secundarios" como sea necesario para cada una de las tareas temporizadas que queramos llevar a cabo de forma independiente.

    Lec17 004

    Por defecto, Arduino utiliza el timer0 para las funciones de tiempo incorporadas por el software báse: delay(), millis() y micros(). El timer1 lo utiliza la librería servo. Y el timer2 lo utiliza la función tone(). Por lo tanto, el usuario que no profundice más puede llegar a pensar que su utilización para los fines que pretendemos está comprometida a menos que sacrifiquemos alguna de las funcionalidades mencionadas. Para evitarlo, la solución será aprovechar la función millis() para implementar nuestro reloj maestro en el que se basan las funciones de tiempo.

    Básicamente, la función millis() nos devuelve el tiempo en milisegundos transcurridos desde que se arranco la placa Arduino con el programa actual. Por lo tanto Arduino ya nos facilita el reloj maestro. La función millis() nos devuelve un unsigned long, es decir un número de 32 bits. Así que todos nuestros relojes secundarios deben utilizar variables de este mismo tipo. Puesto que la función millis() cuenta milisegundos, la precisión de nuestros relojes secundarios será, precisamente de un milisegundo (más que suficiente para la mayoría de los proyectos habituales).

    Lec17 005

    La primera función que vamos a escribir se llamará iniTemp() y se encarga de arrancar un cronómetro (reloj secundario) indicando el tiempo que deseamos que se cuente. La segunda función será chkTemp() y nos dirá los milisegundos que  restan para que el cronómeto alcance el tiempo deseado.

    El código de ambas funciones es el siguiente:

    Lec17 006

    Antes de utilizar estas dos funciones y ver con detalle cómo están escritas, vamos a construir una tercera, a la que llamaremos parpadeo() y que utiliza las dos anteriores para generar una intermitencia de una frecuencia determinada. Su código es el siguiente:

    Lec17 007

    Ahora veamos un ejemplo de su uso. En primer lugar, el esquema electrónico que vamos a utilizar en nuestra lección es muy sencillo. Nuestro Arduino y dos leds para hacer las pruebas.

    Lec17 008

    Y el código de nuestro programa:

    Lec17 009

    Lec17 010

    Cada vez que utilicemos la función intermitencia() necesitamos pasarle cuatro parámetros. El primero es una señal que indica cuando debe ejecutarse la función (in) y llevar a cabo la intermitencia. En nuestro ejemplo, como deseamos que la intermitencia se esté generando sobre el led todo el tiempo, escribimos directamente un 1. Si quisiéramos que la intermitencia se produjera sólo cuando se cumpliera una determinada condición, utilizaríamos este primer parámetro para llevar a cabo esta tarea sólo cuando se cumpla la condición deseada.

    El segundo parámetro es el tiempo en milisegundos que permanecerá alto (y, por lo tanto, también bajo) nuestra cadena de pulsos intermitentes. En nuestro caso para el led situado en el pin IO2 hemos fijado como tiempo 1000 mseg y para el led situado en el pin IO3 hemos fijado como tiempo 2500mseg.

    El tercer parámetro es una variable auxiliar de trabajo que utilizaremos para almacenar nuestro cronómetro. Debe ser del tipo unsigned long como explicamos antes. Hemos creado las variables temporizador1 y temporizador2 para este fin. La primera la utilizamos para un led y la segunda para el otro.

    El cuarto y último parámetro es una variable auxiliar que utilizamos para indicar el estado de nuestra salida de la intermitencia. Es la variable que usamos para indicar si el led se iluminará o apagará, según el momento de la intermitencia en que nos encontremos. Hemos utilizado las variables intermitencia1 e intermitencia2 para este fin.  La primera la utilizamos para un led y la segunda para el otro.

    El código de nuestro bucle principal, loop(), no puede ser más sencillo. Dos llamadas a la nueva función intermitencia() y dos sentencias para escribir el resultado que nos devuelven para activar o desactivar las salidas donde se conectan nuestros leds. Ejecutamos la compilación y simulamos nuestro programa. Podemos comprobar que hay dos intermitencias a frecuencias de 1 y 2,5 segundos que no se interfieren una con la otra.

    Lec17 011

    Conviene aquí que dediquemos un momento a estudiar el código de la función parpadeo() y algunas técnicas de programación utlizadas en él. 

    Lec17 012

    En primer lugar, utilizamos AND y NOT en lugar de && y !. La razón es que en la zona superior del código hemos utilizado tres sentencias #define para fijar las definiciones de AND, NOT y OR y utilizar estos nombres más intuitivos que &&, || o !.

    Lec17 018

    En segundo lugar las variables auxiliares crono y out las hemos utilizado precedidas de un asterisco. Esto signfica que estamos utilizando punteros a las variables en lugar de las propias variables. El puntero indica la dirección que ocupa una variable. Así, el código de la misma función puede ser utilizada diversas veces con temporizadores diferentes sin que entren en conflicto unos con otros. Por eso cuando utilizamos la función parpadeo la llamamos utilizando como parámetros las variables precedidas del símbolo &.

    Lec17 013

    El código de la función realiza lo siguiente. Si el parámetro IN es verdad (lanzador de la función) y el cronométro ya ha cumplido su tiempo (la primera vez que se utiliza la función es siempre es así por el reloj maestro siempre es mayor que 0 que es el valor del cronómetro en ese momento) arrancamos nuestro cronómetro con el tiempo establecido en el parámetro TIEMPO y ponemos alto el parámetro utilizado para indicar el estado de la intermitencia -OUT-. Si no es verdad ponemos bajo el parámetro OUT para indicar el estado de la intermitencia.

    Para arrancar y comprobar el estado de nuestro cronómetro hemos utilizado las otras dos funciones auxiliares que hemos creado initTemp() y chkTemp(). La primera asigna a la variable que utilizamos como cronómetro el valor actual devuelto por la función millis() -nuestro reloj maestro- más el tiempo que deseamos controlar y que le pasamos a la función como parámetro.

    Lec17 014

    La segunda comprueba si el valor almacenado en nuestro cronómetro es mayor que el reloj maestro -la función millis()- y devuelve un cero si ya ha transcurrido el tiempo o el valor en milisegundos que falta, en caso contrario.

    Lec17 015

    El código de una nueva función retraso() que sustituya a la estándar delay() pero que no paraliza a nuestro Arduino mientras se ejecuta, se muestra a continuación como otro ejemplo de las funciones temporales que podemos escribir. También se apoya en el uso de las funciones iniTemp y chkTemp, que siempre son la base de todas las demás.

    Lec17 016

    Dejamos como problema para el usuario, corregir el programa de la lección anterior para que los botones de nuestro reloj ya estén activos todo el tiempo. Esperamos que vuestras soluciones las compartáis en nuestro facebook (https://www.facebook.com/pages/Hubor-Proteus/294446180592964?ref=hl).

    A continuación, vamos a mostrar, como ejemplo final, el código de una nueva función llamada pulso() que permite generar un pulso de una duración determinada siempre que se cumpla una determinada condición. De esta manera tenemos otro ejemplo de funciones de tiempo que podemos construir con ayuda de nuestra técnica de cronómetro maestro y cronómetros derivados. De la misma forma no nos resultará difícil construir otras que se adapten a nuestras necesidades concretas de cada proyecto.

    Lec17 017

    No queremos terminar sin hacer dos consideraciones importantes. La función millis() utiliza, como ya mencionamos, números de 32 bits. Como cada unidad es un milisegundo, eso significa que nuestro cronómetro puede contar hasta 4.294.967.296 mseg = 4.294.967 seg = 71.582 min = 1.193 horas = 49 días.

    Es decir que cada 49 días nuestro reloj maestro se reiniciará a cero. En la práctica, suele ser suficiente y no tendremos problemas. Pero es posible que en ciertos proyectos tengamos que tener esta circunstancia en cuenta porque un retraso, un pulso o un parpadeo que se produzca justo en el momento en que se reinicia el reloj maestro podría no funcionar correctamente. Si en nuestro proyecto se diera este caso, podemos solucionar el problema utilizando dos variables unsigned long combinadas.

    La segunda consideración es que la precisión de nuestro cronómetro es de un milisegundo. Si necesitáramos precisiones mayores (de hasta un microsegundo) podemos utilizar la función micros() que devuelve microsengundos transcurridos en lugar de milisegundos como hace la función millis(). En este caso la limitación máxima temoral es de unos 70 minutos. 

    Esperamos que esta lección le haya resultado útil e interesante.

     

  • Lección 18: Implementación de un reloj en tiempo real

    [Proteus versión 8.1]

    En la lección 16 montamos como práctica un reloj digital. En aquel momento nuestro objetivo era aprender a utilizar un display LCD de texto monocromo y, por esa razón, no nos preocupó realizar una serie de simplificaciones que, en la práctica, hacían que nuestro reloj fuera inservible. De hecho, nuestro reloj era un prodigio de inexactitud y de ineficacia. Inexacto, porque contábamos los segundos mediante la utilización de lapsos de tiempo de inactividad (lo que significa que cada segundo se produce un desfase igual al tiempo que tarda el micro en ejecutar el resto del código de programa). Ineficaz porque teníamos a nuestro microprocesador ocupado la mayor parte del tiempo en un bucle de espera inoperativo.

    Lec18 001

    En la lección 17 vimos como podíamos utilizar una serie de funciones de tiempo que nos permitían evitar el uso de la función delay() con los problemas que nos acarreaba. En esta lección vamos a construir un reloj un poco más profesional. Utilizaremos un display LCD de dos filas y 40 caracteres. Aprovecharemos para aprender a utilizar un integrado RTC (reloj de tiempo real) que proporcionará a nuestro reloj una adecuada precisión. Incluiremos el uso de las funciones temporales para que los pulsadores de mando estén activos en todo momento. Y veremos la utilización de un función que nos permita detectar el flanco de subida de una señal. Además utilizaremos el bus i2c que vimos en las lecciones 910 y 11. Por supuesto todo ello simulado desde nuestro entorno Proteus.

    Nuestro montaje se muestra en las imagénes siguientes. Como es un poco mayor de lo habitual, presentamos una versión global y varios detalles:

    Lec18 002

     El detalle del integrado RTC se muestra a continuación:

    Lec18 003

    El conexionado del display LCD es el siguiente:

    Lec18 004

    Y, por último, el conjunto de pulsadores de mando, se muestra a continuación:

    Lec18 005

    El DS3232 es un integrado de la firma Maxim (http://www.maximintegrated.com/en/products/digital/real-time-clocks/DS3232.html) que nos proporciona un reloj de gran precisión con corrección de las derivas que se producen por los cambios de temperatura. Se conecta con nuestro ARDUINO utilizando el puerto i2c. Proteus incluye un modelo que nos permite llevar a cabo su simulación. Para ello sólo tenemos que unirlo a nuestro ARDUINO utilizando dos pines (los del puerto i2c). Además, aprovecharemos una ventaja que nos proporciona Proteus. Si modificamos las propiedades del DS3232 podemos indicarle que tome de forma automática la hora de nuestro PC al arrancar la simulación. Así tendremos un reloj en hora sin esfuerzo.

    Lec18 006

    Una vez que tenemos listo nuestro hardware, podemos pasar a ocuparnos del programa que se ejecutará en nuestro Arduino. Esta vez es un poco más largo de los que habitualmente utilizamos en el curso. Pero es que, a medida que el curso avanza, los programas se hacen más interesantes, más sofisticados y, por lo tanto, más largos. Por ese motivo, lo vamos a analizar por trozos.

    En la primera parte (líneas 1 a 25) vamos a enlazar con las dos librerías que vamos a utilizar. Wire.h nos posibilita la utilización del bus i2c y LiquidCrystal.h el uso del display LCD (ya vimos el uso de las dos en lecciones anteriores). Luego definimos unas constantes que utilizaremos a lo largo del programa (la dirección de nuestro integrado DS3232 y los operadores lógicos AND, OR y NOT). A continuación, dos constantes para indicar el tamaño de nuestro display LCD (dos filas y cuarenta columnas). Por último una serie de variables de trabajo, de las cuales sólo destaca la variable del objeto que usamos para controlar nuestro display LCD donde definimos los pines con los que se conecta a nuestro ARDUINO y que vimos con detalle en lecciones anteriores.

    Lec18 007

    El segundo trozo de nuestro código contiene la función setup() que sirve para inicializar nuestro equipo Arduino. La utilizamos para escribir la primera línea de nuestro display LCD, definir como entradas los pines a los que conectamos los pulsadores de mando e inicializar el puerto serie que utilizamos para el bus i2c. Observe que hemos dejado comentadas una serie de líneas al final en las que se incluye un pequeño trozo de código que sirve para escribir la hora actual en el dispositivo DS3232. Como explicamos antes, Proteus nos facilita esta tarea e inicializa por defecto el dispositivo DS3232 con la hora actual del reloj de nuestro PC por lo que en simulación no es necesaria utilizarla.

    Lec18 008

    La tercera parte del programa contiene la función loop() que es el código que se ejecuta de forma cíclica en nuestro equipo Arduino.

    En la primera parte utilizamos las funciones chkTemp e iniTemp para ejecutar una serie de instrucciones sólamente una vez cada 100ms. De esa forma no tenemos que utilizar el poco práctico delay() y descargamos a nuestro Arduino de la tarea de estar refrescando de forma infructuosa cada ciclo de programa el display. Por otro lado 100ms es tiempo suficiente para que si se usan los pulsadores de mando, la información se refresque en el display cuando el usuario los utilice.

    La tarea que se ejecuta cada 100ms es la encargada de refrescar la hora actual en el display LCD utilizando la función auxiliar llamada MostrarHoraEnDisplay() cuyo código veremos más adelante.

    El resto de la función es una serie de trozos de código iguales, cada uno de los cuales controla un pulsador y responde aumentando la parte del reloj correspondiente (segundo, minuto, hora, etc) una unidad y controlando los desbordes. Destacamos el uso de la nueva función FlancoSub() cuyo código veremos más adelante. Con ella posibilitamos que cuando el usuario pulse el botón, sólo se incremente una unidad con el flanco de subida. Si no lo hiciéramos así, cada vez que el usuario actúa sobre el pulsador se incrementarían muchas unidades de un golpe. La función flanco de subida puede resultar muy útil en muchos otros proyectos en los que necesitemos utilizar pulsadores.

    Lec18 009

    Lec18 010

    El siguiente trozo de código contiene la función MostrarHoraEnDisplay() que ya dijimos que se ocupa de escribir en el display LCD la hora actual. Recibe como parámetros las unidades que componen nuestro reloj: segundo, minuto, hora, día de la semana, día, mes y año. Formatea de forma adecuada el mensaje para presentarlo en nuestro display.

    Lec18 011

    Lec18 012

    La función LeerHoraDS3232() se encarga de recibir de la información de la hora actual desde el dispositivo DS3232. Puesto que es un dispositivo con el que nos conectamos a través del bus i2c, el flujo de control es muy similar al que ya vimos en lecciones anteriores para leer información procedente de un dispositivo que cumple el estandar de comunicación i2c. Primero hacemos una llamada a su dirección dentro de la red, luego escribimos el comando adecuado para indicarle que queremos que nos devuelva los datos de la hora actual y, por último, leemos los diferentes bytes que nos trasmite. Guardamos cada trozo de informacióin en la variable destinada a tal fin y utilizamos la función bcdToDec(), que veremos con detalle más adelante, para transformarla al formato decimal.

    Lec18 013

    FijarHoraDS3232() es una función que utilizamos para cambiar la hora guardada en este momento en el dispositivo DS3232. Su funcionamiento es muy similar a la que acabamos de ver, pero en este caso escribimos en lugar de leer los datos.

    Lec18 014

    Las funciones iniTemp() y chkTemp() ya las vimos en lecciones anteriores. Sirven para arrancar un temporizador de un determinado tiempo de duración y para comprobar si ese tiempo ya se ha cumplido o cuanto queda todavía para hacerlo. Es una técnica muy útil para poder manejar varios temporizadores diferentes en nuestro Arduino basándose en un único reloj maestro (en este caso el que usa la función millis().

    Lec18 015

    decToBcd() es una función que convierte un número en formato decimal a un número en formato BCD. La función bcdToDec() hace la función inversa. El código BCD ( Binary Coded Decimal) es un código estándar de 6 bits usado por algunos ordenadores para almacenar caracteres alfanuméricos y signos de puntuación. Cada carácter está compuesto por 6 bits (2 carácteres en formato octal). Con estos 6 bits se pueden definir un total de 64 caracteres (2^6). Como el dispositivo DS3232 almacena su información utilizando este formato, estas funciones son útiles para hacer las transformaciones adecuadas al formato decimal que utilizamos en nuestro Arduino.

    Lec18 016

    En una señal digital, se denomina flanco a la transición del nivel bajo al alto (flanco de subida) o del nivel alto al bajo (flanco de bajada). En este caso, incorporamos la función FlancoSub() que detecta el flanco de subida. De esta forma, nos resulta sencillo determinar el momento en que se actúa sobre el pulsador ignorando el resto del tiempo que dicho botón permanece actuado. Así, permitimos que se incremente una unidad cada vez que se produce una actuación sobre el pulsador. 

    Esta función es muy utilizada en dispositivos de mando de máquinas móviles para reducir el riesgo de que una botonera caída en el suelo (con tal mala suerte de que el pulsador de marcha quede pulsado de forma constante) o humedades producidas por condensación en el interior de la botonera, puedan provocar de forma extemporánea arranques no intencionados con riesgo para las personas.

    Lec18 017

    Una vez desentrañado todo el código, ya sólo resta arrancar la simulación y disfrutar de nuestro reloj.

    Lec18 018

    Esperamos que les haya resultado interesante la lección de hoy y como siempre terminamos proponiendo una práctica. ¿Se anima el lector a completar el proyecto logrando que nuestro reloj se convierta en un despertador? Para ello es necesario poder fijar una hora con ayuda de los pulsadores y hacer sonar una alarma cuando se cumpla dicha hora.

     

     

  • Lección 19: Control de un depósito de agua mediante una bomba.

    [Proteus versión 8.1]

    En esta lección vamos a recopilar muchos de los conocimientos adquiridos durante el curso hasta este momento para realizar un proyecto que se encarge de la gestión de un depósito de agua alimentado por una bomba. El siguiente esquema muestra un esquema de los equipos y nos servirá para fijar nuestros objetivos.

    Lec19 001

    Nuestro depósito de agua se rellena con ayuda de una bomba y se vacía a medida que se va utilizando el agua de su interior.

    Para conocer con exactitud el nivel del agua contamos con un sensor analógico de nivel del agua. Dicho sensor nos devuelve una salida comprendida entre 0 y 5V correspondiéndose el valor de 0 con el depósito vacío y el valor de 5 con un nivel de agua de 5mts (la altura de nuestro depósito). Por supuesto, las lecturas intermedias son proporcionales a la altura del agua dentro de nuestro depósito.

    Además disponemos de cuatro boyas digitales que nos indican cuando el agua alcanza cuatro niveles concretos dentro del depósito y que utilizaremos para el mando de la bomba. Dos marcas en la zona inferior que denominamos nivel extra-bajo y nivel bajo. Y dos marcas en la zona superior que llamamos nivel alto y nivel extra-alto. En control industrial es una buena práctica utilizar siempre para el mando sensores digitales y para la medida sensores analógicos, puesto que los segundos están mucho más expuestos a las interferencias y pueden generar arranques o paradas intempestivas. El hecho de que utilicemos dos sensores para la zona superior y dos para la zona inferior es únicamente para implementar un sistema redundante y aumentar la seguridad de que el depósito no se quede vacío (evitando que no arranque la bomba porque el detector de nivel bajo se estropee) ni desborde por la parte superior (descartando que la bomba no se detenga porque el sensor del nivel alto no funcione correctamente).

    Para el gobierno y protección eléctrico de nuestra bomba de carga del depósito, hemos diseñado un circuito formado por un interruptor magnetotérmico de mando manual y un contactor gobernado eléctricamente. El primero se encarga de la vigilancia eléctrica del motor de la bomba protegiéndolo de posibles sobrecargas y cortocircuitos. El segundo nos permite su gobierno mediante la alimentación o corte de la corriente. El contactor se gobierna de forma eléctrica para permitir su control por parte de nuestro ARDUINO. El interruptor sólo se gobierna de forma manual porque es conveniente que si se produce un fallo por cortocircuito o sobrecarga, el operador realice una inspección de la instalación para encontrar el motivo del fallo.

    Las órdenes de marcha y parada de la bomba se realizan de dos formas:

    Automáticamente, según el estado de los niveles digitales. Cuando se deje de recibir la señal de nivel bajo se procederá a arrancar la bomba para llenar el depósito. Ya hemos explicado que, por si acaso fallará el sensor, se ha dispuesto un segundo denominado nivel extra-bajo con la misma función. Cuando se reciba la señal de nivel alto se detendrá la bomba. Igual que en el caso de los niveles inferiores, se ha dispuesto un segundo nivel extra-alto con la misma función como sistema redundante de seguridad.

    De forma manual, cuando se utiliza el pulsador de marcha del panel de mando (y siempre que no estén activos los niveles alto o extra-alto porque de otra forma, el agua desbordaría por la zona superior del depósito) se arrancará la bomba. Si la seta de emergencia de parada se pulsa, la bomba se detendrá en todo momento y la bomba no se arrancará aunque se pierda la señal de los niveles bajos.

    Antes de proceder a arrancar la bomba, se genera un aviso (acústico o luminoso) de pre-arranque con objeto de prevenir de la maniobra a cualquier persona que pudiera estar trabajando en el depósito. Realmente en este caso es un poco exagerado generar un aviso de pre-arranque, puesto que la ley sólo obliga a hacerlo en los casos de máquinas móviles, pero nos resultará interesante por motivos pedagógicos.

    En el panel de señalización se mostrará con ayuda de pilotos luminosos la siguiente información: fallo por cortocircuito en el interruptor, fallo por sobrecarga en el interruptor, estado del interruptor (cerrado o abierto) y estado del contactor (cerrado, abierto o intermitencia cuando se produjo un fallo de confirmación). Cuando el interruptor y el contactor estén cerrados, la bomba estará en marcha.

    Por último, utilizaremos el terminal para enviar cada cinco segundos la información completa del estado de nuestros equipos. De esta forma podemos disponer a distancia de todos los datos para conocer en todo momento cómo se encuentra el sistema.

    Para gestionar todo este conjunto con nuestro Arduino, necesitamos utilizar una entrada analógica (el medidor analógico de nivel de agua en el depósito), seis salidas digitales (una para el contactor, otra para el aviso de pre-arranque y cuatro para los indicadores luminosos del panel de señalización) y diez entradas digitales (sobrecarga, cortocircuito, estado interruptor, estado contactor, marcha, parada, nivel extra-alto, nivel alto, nivel bajo y nivel extra-bajo). Si alguien las ha contado, observará que nuestro Arduino no tiene suficientes pines digitales, pero encontraremos la forma de obviar esta dificultad.

    En la imagen siguiente mostramos todo el conjunto.

    Lec19 002

    Los pines IO0 e IO1 los utilizamos para el terminal de datos que vamos a utilizar para enviar la información de monitorización del conjunto al exterior.

    Lec19 007

    Los pines IO2 a IO7 los utilizamos como entradas digitales. Los tres primeros para recibir la información del interruptor (fallo por sobrecarga, fallo por cortocircuito y estado). El cuarto para la información del estado del contactor. El quinto y sexto para el panel de mando (pulsador de marcha y seta de parada). 

    Lec19 003

    Los pines IO12 e IO13 como salidas digitales. Les conectaremos a dos relés con los que gobernaremos el contactor del motor de la bomba y la señal de pre-aviso. Para ayudar visualmente durante la simulación, hemos colocado una lámpara conectada al contactor normalmente abierto del relé que nos muestra la orden de cierre del contactor y del dispositivo de pre-aviso.

    Lec19 004

    Los pines IO8 a IO11 harán las funciones de salidas digitales para los pilotos luminosos del panel de señalización. Así podemos indicar cuándo se produce un fallo por cortocircuito, un fallo por sobrecarga y el estado del interruptor y del contactor. Este último lo utilizamos para mostrar tres estados: cuando el contactor está cerrado el piloto está encendido, cuando está abierto el piloto está apagado y cuando se ha producido un fallo de confirmación el piloto parpadeará.

    Lec19 005 

    Este es un buen momento para explicar que entendemos por fallo de confirmación del contactor y porque razón lo utilizamos.

    Los contactores suelen venir provistos de un contacto auxiliar que sirve para monitorizar su estado. Este contacto es el que utilizamos para indicar si el contactor está cerrado o abierto. Seguimos aquí un procedimiento diferente al que siguen otros ingenieros de procesos que utilizan la salida de mando del Arduino como indicador del estado del contactor. Pero no siempre la activación de nuestra orden de mando significa que el contactor haya cerrado realmente. Un fallo en el contactor o en el cableado puede significar que estemos generando la orden y no se produzca el efecto deseado. Otro fallo puede provocar que el contactor siga cerrado (porque se han pegado sus contactos, por ejemplo) aunque la orden no se esté generando. Por esta razón es una buena práctica que la información de los estados de los dispositivos del proyecto se facilite siempre con datos procedentes de sensores y no suponer que una salida de nuestro Arduino suscita automáticamente el cambio del dispositivo gobernado.

    Ahora ya se entiende mejor porque surge la necesdidad de utilizar un fallo de confirmación. Se produce este tipo de fallo cuando generamos la orden de cierre y no se recibe la señal de que el contactor está cerrado durante un segundo.

    Por último, los cinco primeros pines analógicos (AD0-AD4) los utilizaremos para conocer el nivel de llenado del depósito. Los cuatro primeros para señalizar el estado de las boyas de nivel y el quinto para el indicador analógico de nivel.

    Lec19 006 

    En condiciones normales, deberíamos utilizar entradas digitales para las boyas de nivel, puesto que son elementos digitales. Pero como nuestro ARDUINO no dispone de pines digitales suficientes, utilizaremos para este fin los pines analógicos, aunque sólo indicaremos dos estados (cuando tengamos una medida de menos 2,5V entenderemos que la boya indica que no se alcanzó el nivel en ese punto y una medida superior a 2,5V que sí).

    Ya podemos analizar el software que se encargará de controlar todo nuestro sistema. Como en ocasiones anteriores vamos a verlo por trozos para facilitar su comprensión. Algunas de las funciones que utilizamos ya se vieron en lecciones anteriores por lo que obviaremos su explicación.

    Lec19 008

    Lo primero que hacemos es llevar a cabo una serie de definiciones. Los tres operadores lógicos y cuatro valores que nos ayudarán a parametrizar nuestra aplicación de forma sencilla sin tener que navegar por el código. El primero es el tiempo de parpadeo que utilizaremos para gobernar un piloto informativo (encendido o apagado cada medio segundo). El segundo el tiempo que pasará entre cada refresco de la información que mandaremos al terminal (5 segundos). El tercero el tiempo del aviso de pre-arranque (3 segundos). Y el cuarto y último, el factor de conversion de la medida analógica.

    Como nuestro Arduino tiene una entrada analógica que puede leer valores entre 0 y 1023 y el sensor de medida del nivel de agua en el depósito nos devuelve 0V cuando el nivel es 0 metros y 5V cuando el nivel es 5 metros, el factor de conversión se obtiene con una sencilla regla de tres.

    Si 1023 unidades son 5 metros, cada unidad serán 5 / 1023, es decir que el factor de conversión es 0.004887.

    El lector podrá adecuar estos valores a las necesidades reales de su proyecto de forma sencilla y cómoda.

    Lec19 009

    Luego definimos una estructura a la que le daremos el nombre de MotorDatosControl. Las estructuras en el lenguaje C se utilizan para agrupar variables que se usan para una misma finalidad y que se pueden pasar entre las funciones con un sólo paso. En este caso la estructura se utilizará para el control del motor. Más adelante veremos su uso.

    Lec19 010

    Luego definimos las variables que vamos a utilizar en el control de nuestro proceso. Llamamos especialmente la atención sobre la variable m1 que utilizamos para controlar el motor de la bomba y que es del tipo de la estructura que definimos con anterioridad.

    Lec19 011

    A continuación figura la función setup() con la configuración. En este caso definimos qué pines se utilizarán como entradas y cuáles de salida. Además fijamos la velocidad del terminal en 9600 baudios.

    Lec19 012

    La función loop() con el código del bucle de control repetitivo lo hemos dividido en cuatro partes. La primera se encarga de leer todas las entradas y almacenar en variables su estado. En primer lugar, las seis variables digitales. Luego las cuatro variables analógicas que utilizaremos como lecturas digitales para mostrar el estado de las boyas de nivel y, por último, la lectura del sensor de nivel del depósito. Destacar el uso de las funciones if() para recibir una señal digital a través de una entrada analógica.

    Lec19 013

    La segunda parte contiene la lógica de gobierno del motor de la bomba.

    La línea 83 contiene las condiciones de arranque de la bomba (bien una orden manual o que no existe señales en las boyas Nivel bajo o Nivel extra-bajo).

    La línea 84 las condiciones que generarán la parada de la bomba (seta de parada, fallos eléctricos, interruptor abierto o niveles alto y extra-alto).

    Las líneas 85 y 86 generan el aviso de prearranque con un pulso de duración 5 segundos (recordar las definiciones) desde el momento que se genera la orden de arranque hasta que el motor se pone en marcha o se produce una orden de parada.

    La línea 87 genera el parpadeo cuando se produce un fallo de confirmación para su utilización con el piloto de señalización del panel de estados.

    Las líneas 88 a 91 se utilizan para llamar a la función ctrlMotor (más adelante la veremos cuando tratemos de las funciones auxiliares) con ayuda de la estructura m1 del tipo MotorDatosControl. La función ctrlMotor() se encarga de todo y nos facilita nuestro trabajo. El uso de la estructura nos permite pasar toda la información junta y no tener que ir especificando todos los datos cada vez. Además, si tuvieramos que controlar dos motores, aislarían totalmente la información de uno y otro motor, permitiéndonos utilizar el mismo código en la función auxiliar.

    Lec19 014

    La tercera parte se usa para activar las salidas digitales cuando las condiciones así lo determinan. Las líneas 93 a 95 gobiernan los pilotos que indican los fallos y el estado del interruptor. Las línes 96 y 97 los relés de mando. Y la línea 98 el piloto que indica el estado del contactor que será fijo o parpadeante según el estado del contactor o la variable que utilizamos para indicar un fallo de confirmación.

    Lec19 015

    La cuarta parte envía la información por el puerto serie al terminal. Lo hace cada 5 segundos (definición de tiempoRefrescoMonitor) y, como vimos en lecciones anteriores, no utiliza la función delay() para no interrumpir el control de los dispositivos gobernados durante la espera.

    Las funciones auxiliares que se utilizan en el programa son las siguientes:

    Lec19 016

    Las funciones iniTemp() y chkTemp() ya las vimos en lecciones anteriores y nos ayudan a implementar múltiples funciones temporales utilizando un único reloj maestro (timer) del microprocesador.

    Lec19 017

    La función báscula() implementa una báscula de tipo flip-flop con reset dominante que genera una salida desde que se produce la señal set hasta que se se produce la señal reset. En caso de que se reciban las dos señales al mismo tiempo, se impone el reset. Con ella se lograr implementar la utilización de 'memorias' (retención de un dato durante un determinado espacio de tiempo) en nuestro código. En nuestro caso la utilizamos para memorizar la pulsación sobre la marcha hasta que el aviso de prearranque concluye o se produce una condición de parada.

    Lec19 018

    La función pulso() genera una señal de una duración determinada mientras se produce una condición. Con ella conseguimos generar el aviso de pre-arranque de la duración deseada.

    Lec19 019

    La función parpadeo() nos ayuda a generar la intermitencia en los indicadores de salida. Genera un parpadeo de frecuencia constante mientras esté activa la señal de lanzamiento. Así, podemos generar una intermitencia en el piloto que muestra el estado del contactor cuando se ha producido un fallo de confirmación.

    Lec19 020

    Lec19 021

    La función ctrlMotor() realiza todas las tareas de gobierno de un motor. Transforma la orden de arranque en un pulso, limpia el fallo de confirmación con cada nueva orden de arranque o parada, genera el fallo de confirmación cuando no llega la confirmación del contactor en el tiempo prefijado y genera la salida de control del motor.

    Ya está todo listo para simular nuestro proyecto y comprobar que todo funciona. No es una simulación sencilla y daremos unas pautas para llevarla a cabo.

    Lec19 022

    Lo primero es asegurar todas las condiciones de inicio. Nos aseguraremos que no hay fallo de sobrecarga, ni de cortocircuito. Que el interruptor está cerrado y el contactor abierto. Que la seta de parada está cerrada (los dispositivos de parada siempre son normalmente cerrados por razones de seguridad). Las boyas de nivel estarán cerradas para los niveles EB y B y abiertas los niveles A y EA. El nivel del depósito lo situaremos a la mitad con ayuda del potenciómetro. Las dos lámparas que simulan el contactor y la señal de preaviso tienen que estar apagadas y el único piloto encendido será el que muestra el estado del interruptor (cerrado).

    Ya podemos arrancar la bomba con el pulsador de marcha. Si lo hacemo se generará un aviso de prearranque durante 3 segundos y luego se generará la orden de cierre del contactor. Al segundo el contactor se abrirá y el piloto del estado del contactor parpadeará (se ha producido un fallo de confirmación).

    Volvamos a generar la orden de marcha. Cuando observemos que se genera la salida de cierre del contactor, cerraremos el interruptor digital CONTACTOR. Ahora la bomba funciona normalemente.

    Cuando la queramos parar, actuaremos sobre la PARADA abriendo el contacto. El relé de gobierno del contactor se abrirá (tenemos que simular manualmente que el contactor se abre, actuando sobre la señal del contactor).

    También podemos probar a arrancar de forma automática abriendo las señales de las boyas de nivel B o EB. Y a parar de forma automática con las boyas de nivel A y EA. No se olvide de cerrar y abrir la señal del CONTACTOR cada vez.

    También podemos simular el comportamiento del sistema cuando se produce un fallo o se abre el interruptor.

    El sensor analógico nos indica (recordar que sólo indica y no gobierna) el nivel de agua del depósito. El potenciómetro nos ayuda a simularlo.

    El monitor nos muestra toda la información del sistema. Si se modifica su altura se puede lograr que la información de cada refresco se muestre en una sóla pantalla.

    En la siguiente pantalla se muestra la simulación en marcha con la bomba arrancada.

    Lec19 023

    Cuando todo funcione a nuestro gusto, ya podemos implementar la utilización de nuestro ARDUINO para el control del llenado de un depósito de agua por medio de una bomba. Esperamos que os haya gustado la lección de hoy.

     

  • Lección 2: Profundizando en las salidas digitales

    [Proteus versión 8.1]

    En la primera lección de este curso vimos como activar una salida digital con nuestro equipo Arduino. Como es lógico, en la primera lección sacrificamos algunas cosas importantes para no confundir demasiado al lector en su primer acercamiento a la programación en Arduino. Es evidente que utilizar un microprocesador para mantener un led encendido todo el tiempo no es una acción de la que podamos sentirnos muy orgullosos. Pero Roma no se hizo en un día y aprender de forma paulatina es muy importante para no agobiarnos demasiado.

    Vamos a continuar con nuestro curso modificando el estado de nuestra salida al tiempo que conocemos nuevas funciones disponibles en Arduino y progresamos en el aprendizaje del uso de Proteus. En este caso vamos a empezar utilizando únicamente el módulo principal de nuestro equipo Arduino. Nuestro esquema sería el siguiente:

    Lec02 001

     

    Observemos  que en la zona inferior existe un led conectado, en serie con una resistencia, al conector rotulado IO13. Este conector está también conectado al pin 19 (PB6) de nuestro microprocesador. Como vimos en la primera lección para encender dicho led, tendríamos que escribir el siguiente programa, donde inicializamos el pin 13 y lo activamos.

     Lec02 002

    Si ejecutamos la simulación tendríamos como resultado el led encendido.

    Lec02 003

    Hagamos ahora que nuestro led parpadee con una frecuencia de un encendido y apagado cada segundo. Para hacerlo tendremos que utilizar una función nueva llamada delay() que detiene la ejecución del funcionamiento del microprocesador durante un determinado tiempo. Su formato es el siguiente:

    delay( <numero de milisegundos>)

    Donde el numero de milisegundos indica el tiempo que estará detenido el microprocesador esperando.

    Modifiquemos nuestro programa utilizando esta función para activar y desactivar la salida donde se conecta el led de forma cíclica.

    Lec02 004

    En la línea 15 indicamos que la salida se active. En la línea 16 detenemos la ejecución del programa durante un segundo. En la línea 17 desactivamos la salida. Y en la línea 18 volvemos a detener el programa durante otro segundo. Como la función loop() se ejecuta de forma cíclica, volveremos a ejecutar de nuevo las cuatro sentencias produciéndose el parpadeo deseado. Si ejecutamos la simulación, podremos compobar que todo funciona como deseamos.

    Lec02 005

        Lec02 006

     

     

     

     

    Observe además una característica importante que nos ofrece Proteus para ayudarnos en la simulación. al lado de los componentes aparecen unos cuadros rojos y azules. Con ellos, Proteus nos indica el esado actual del circuito en ese punto. Si está rojo indica que hay tensión en ese punto y si está azúl indica que está al nivel del potencial de tierra. Por eso el cuadro situado a la izquierda del led siempre está azúl (ese punto está conectado a masa) y en cambio los cuadros situados a ambos lados de la resistencia y a la derecha de led adquieren el color en función de su estado en ese momento. Esta ayuda que nos brinda Proteus puede ser muy útil.

    Modifiquemos ahora nuestro esquema para colocar cinco nuevos leds en los pines IO0 a IO4. Lo primero que tenemos que hacer es colocar un conector con el nombre IO0. Para ello utilizaremos en la barra de herramientas lateral, el botón 'Modo terminales'.

    Lec02 007

    En la ventana de terminales, seleccionaremos uno del tipo default y lo situaremos debajo de nuestra placa.

    Lec02 008

    Situaremos el cursor sobre él y pulsaremos el botón derecho del ratón para que aparezca el menú contextual, donde seleccionaremos la opción "editar las propiedades".

    Lec02 009

    En la ventana de diálogo que aparece, asignamos como nombre del terminal el valor IO0

    Lec02 010

    De esta forma tan sencilla ya tenemos un terminal enlazado eléctricamente con el pin PD0 (pin físico 2) del microprocesador.

    Lec02 011

    Ahora conectaremos a él un led y una resistencia en serie.Siempre conviene conectar los leds a nuestro microprocesador en serie con una resistencia. De esta forma nos aseguramos que la intensidad de la corriente que pasa a través del led no supera nunca el máximo que puede soportar y nos aseguramos que no se estropeará. Cuanto mayor sea el valor de la resistencia, menor será la intensidad de corriente y, por lo tanto, el led lucirá con menos intensidad. Puesto que, normalmente, un led funciona en el rango aproximado de 5 a 25mA de corriente y la tensión de salida en los pines de nuestro microprocesador son 5V, podemos utilizar una resistencia desde 200Ω a 1kΩ dependiendo de la intensidad con la que deseemos que luzca y el consumo que queremos.

    Para colocar la resistencia seleccionaremos el botón "modo componente" de la barra de herramientas de la izquierda.

    Lec02 012

    Puesto que ya existe una resistencia en nuestro circuito, en la ventana "DEVICES" ya podemos seleccionar el dispositivo RES (resistencia). Pero de todas formas, vamos a seleccionar una resistencia de 220Ω para aprender como se seleccionar dispositivos. Pulsaremos sobre la letra "P" situada en la zona superior de la ventana "DEVICES" y se abrirá la ventana de diálogo para selecionar componentes. En el cuadro de diálogo "Categoría" seleccionaremos la opción "resistors". En el cuadro "Sub-Categoría" seleccionaremos "0.6W Metal Film". Y en el cuadro de resultados seleccionaremos "MINRES220" que se corresponde con una resistencia de 220Ω y 0,6Watios.

     Lec02 013

    Pulsamos sobre el botón "Aceptar" y ya encontramos nuestro componente en la ventana "DEVICES". Como acabamos de comprobar, "comprar" componentes en Proteus para utilizarlos en nuestros prototipos es muy sencillo y barato. A continuación, seleccionamos nuestro componente desde la ventana "DEVICES" y colocamos el ratón donde queremos posicionar nuestra resistencia.

    Lec02 014

    Cuando nos guste el emplazamiento, volvemos a pulsar el botón izquierdo del ratón y la resistencia quedará colocada en ese lugar.

    Lec02 015

    Para tender un cable entre el terminal y la resistencia seguiremos el siguiente proceso. Colocaremos el cursor del ratón sobre el extremo del terminal, pulsaremos el botón izquierdo, desplazaremos el cursor hasta el extermo de la resistencia y pulsaremos de nuevo el botón izquierdo del ratón.

    Lec02 016                                             

        Lec02 017 

     Lec02 018 

    Ahora podemos conectar nuestro led en el otro extermo de la resistencia. Ya existe en la ventana "DEVICES" un dispositivo llamado "LED-YELLOW" porque ya está utilizado en nuestra placa del microprocesador. Pero para practicar le invitamos a que seleccione un led del tipo LED-GREEN, LED-RED o LED-BLUE siguiendo los mismos pasos que hicimos para seleccionar nuestra resistencia.

    Lec02 019

    Seleccionemos el dispositivo desde la ventana "DEVICES" y coloquemos el cursor donde queremos situarlo. Observe que esta vez el led por defecto se ha puesto en posición vertical y nosotros lo queremos horizontal. Utilizando las teclas "+" y "-" del teclado numérico podemos girarlo antes de colocarlo en su emplazamiento definitivo pulsando con el botón izquierdo del ratón.

    Lec02 020

      Lec02 021

      Lec02 022

    Debemos conectar el led y la resistencia de la misma forma que antes conectamos el terminal con la resistencia.

    A continuación tenemos que poner el terminal libre del led a tierra para cerrar el circuito. Para hacerlo, tenemos que seleccionar la herramienta "modo terminal" de la barra lateral, elegir un terminal de tipo "GROUND" en la ventana "TERMINALS", colocar el cursor del ratón donde deseamos colocarlos y pulsar el botón derecho para emplazarlo definitivamente. Además tenemos que conectar el extermo libre del led con el nuevo terminal de tierra que acabamos de poner.

    Lec02 023

    Podemos repetir el proceso y colocar los otro cuatro leds.

    Lec02 024b

    A partir de este momento, en el resto de lecciones, daremos por sentado que el lector es capaz de colocar y conectar los componentes que utilicemos. Si tiene dudas de como utilizar Proteus para hacerlo puede consultar el tutorial "esquema electrónico" que se encuentra en la sección "Primeros pasos" de la pestaña "inicio" de proteus.

    Modificaremos el programa para activar las nuevas salidas conectadas a los leds que acabamos de colocar.

    Lec02 025

    Y ejecutaremos la simulacion para comprobar que todos los leds parpadean correctamente.

    Lec02 026

    Para terminar la clase, podemos probar el siguiente programa que provoca el encendido de todos los leds en secuencia tipo árbol de navidad. Ahora ya empieza a tener sentido utilizar un microprocesador en nuestros diseños, sobre todo si utilizamos una herramienta económica como Arduino. Le invitamos a probar variaciones del programa para que practique con los conocimientos adquiridos.

    Lec02 027

     

  • Lección 20: Manejo de números muy grandes.

    [Proteus versión 8.1]

    Como hemos hecho otras veces durante este curso vamos a intercalar una lección más teórica en nuestra trayectoria. Este es un buen momento para aprovechar y hacer un repaso de los tipos de datos númericos que podemos utilizar en la programación de nuestro ARDUINO y aprender cuál es el que mejor se adapta a cada necesidad.

    En lenguaje C es obligatorio declarar todas las variables y constantes que vamos a utilizar en nuestro código antes de escribir el código que se ejecutará. Por eso, en todos los ejemplos que hemos ido viendo a lo largo del curso, hemos empezado todos los programas declarando las variables con sentencias como:

    Lec20 001

    Para definir una variable la sintaxis que tenemos que utilizar es:

    <tipo> <identificador>;

    Los identificadores son los nombres que les asignamos a las variables para referirnos a ellas en el resto del código. Un identificador puede tener entre uno y treinta y dos caracteres. El primer carácter debe ser una letra o el signo de subrayado (el guión bajo '_'). El lenguaje C, a diferencia de otros lenguajes, distingue entre las mayúsculas y las minúsculas, por lo que los dos identificadores 'm1_fallo' y 'M1_fallo' son distintos y harán referencia a variables diferentes. Por último, no se pueden utilizar como identificadores ciertas palabras reservadas del lenguaje C, como, por ejemplo, 'for', 'function', 'while', 'boolean', etc.

    Los tipos indican al compilador qué tipo de dato vamos a almacenar en esa variable. Hay cuatro tipos básicos: carácter, numérico entero, numérico de coma flotante y sin definir. Además cada uno de estos tipos básicos admiten los denominadores modificadores de tipo que nos permiten definir el tamaño del almacenamiento y si guardará números sólo positivos (sin signo) o servirá para manejar números positivos y negativos.

    En este punto hay que hacer una salvedad importante. El lenguaje C define el tipo entero en función del microprocesador que estemos utilizando. Por lo tanto, si trabajamos con el ARDUINO UNO el tamaño de un entero es de 16 bits (dos bytes) pero si trabajamos con el ARDUINO DUE el tamaño será de 32 bits (cuatro bytes). Por eso a partir de ahora nos vamos a referir siempre a los tipos para el ARDUINO UNO y el lector deberá buscar las equivalencias en el caso de usar otro equipo ARDUINO:

    Empezaremos por ver los tipos que podemos utilizar para contener números enteros sin signo. Según su tamaño tenemos los siguientes.

    • boolean puede almacenar un bit. Es decir que sólo podemos almacenar 0 ó 1.
    • unsigned char puede almacenar números de un byte. Números comprendidos entre 0 y 255
    • unsigned int puede almacenar números de dos bytes. Números comprendidos entre 0 y 65.535.
    • unsigned long puede almacenar números de cuatro bytes. Números comprendidos entre 0 y 4.294.967.295.
    • unsigned long long puede almacenar números de ocho bytes. Números comprendidos entre 0 y 18.446.744.073.709.551.615.

    Realmente es difícil pensar que podamos necesitar números más grandes para el 99,999999% de los proyectos que vayamos a realizar.

    Cuando no utilizamos el modificador 'unsigned' y podemos almacenar números positivos o negativos, los rangos de los números que se pueden almacenar serían:

    • char puede almacenar números de un byte. Números comprendidos entre -128 y 127
    • int puede almacenar números de dos bytes. Números comprendidos entre -32.768 y 32.767
    • long puede almacenar números de cuatro bytes. Números comprendidos entre -2.147.483.648 y 2.147.483.648
    • long long puede almacenar números de ocho bytes. Números comprendidos entre -9.223.372.036.854.775.807 y 9.223.372.036.854.775.807.

    Sin embargo, la gestión de todos los tipos disponibles no es igual de sencilla. En concreto los números grandes (long long) son más complicados de utilizar en nuestro código cuando trabajamos con ellos. Veamos un ejemplo de código donde definimos una variable númerica entera de cada tamaño y luego tratamos de mandar los valores por el terminal virtual.

    Lec20 002

    Si compilamos, recibiremos el siguiente error:

    Lec20 003

    Es decir, que no tenemos una función print() que nos permita enviar este tipo de datos al terminal. Más adelante, veremos la solución a este problema. Por ahora, nos sirve con saber que podemos definir variables de todos estos tipos.

    Es muy importante definir bien el tamaño para nuestras variables. Si utilizamos un tipo 'unsigned int' para una variable que puede contener números mayores de 65.535 nuestro programa no funcionará bien y dará errores. Y si abusamos de los tipos grandes podemos llegar a tener problemas de ocupación de memoria y haremos trabajar a nuestro procesador de forma innecesaria puesto que los cálculos donde se utilizan números de tipos más grandes (long o long long) necesitan más ciclos de ejecución para llevarlos a cabo que los que utilizan tipos menores (char o int).

    Este es el momento adecuado para hablar de las uniones. Una unión es una posición de memoria que se usa por varias variables similares que pueden ser de tipos diferentes. Pongamos un ejemplo que nos ayude a entender su uso.

    Lec20 004

    Imaginemos que tenemos una variable de tipo 'int' que ocupa dos bytes y que queremos poder hacer referencia a cada uno de ellos por separado. El programa de ejemplo nos ofrece la solución. En las líneas 6 a 10, definimos una unión (a la que le daremos el nombre de num). Dentro de la unión definimos dos variables que ocuparán, como hemos visto, la misma posición de memoria. La primera, de nombre num16bits, es de tipo entero. La segunda, es una matriz de dos posiciones de nombre num8bits de tamaño char cada una de ellas. Luego, en la línea 12, declaramos una variable de tipo num (la nueva unión que acabamos de definir) con el nombre de prueba. En la línea 16 asignamos a la parte entera de la unión el valor en hexadecimal 0xFA5C. Observe que la forma de referirnos a cada uno de los elementos que forma una unión es mediante la sintaxis <nombre union>.<elemento union>.

    El resultado de todo ello es el siguiente. 

    Lec20 005

    El compilador almacenará en la posición de memoria correspodiente el valor FA5C. La variable num16bits devuelve el numero entero de dos bytes. La variable Num8bits[0] devuelve el valor del byte de menor peso (5C) y la variable Num8bits[1] el byte de mayor peso (FA).

    Si simulamos el programa, comprobaremos que el resultado es el explicado aquí.

    Lec20 006

    Veamos ahora, otro ejemplo que nos servirá para explicar cómo podemos utilizar la función print() para visualizar números 'long long' y para profundizar en el entendimiento de las uniones.

    Lec20 007

    En este caso utilizamos la unión para almacenar en una misma posición de memoria un número de ocho bytes, una matriz de dos posiciones de cuatro bytes cada una, una matriz de cuatro posiciones de dos bytes cada una y una matriz de ocho posiciones de un byte cada una.

    Declaramos dos variables numPrueba y numPrueba2 de tipo igual a la union que acabamos de definir. Con la primera mostramos un ejemplo en hexadecimal para mostrar el número en formato de 64 bits, en dos partes de 32 bits, en cuatro partes de 16 bits y en ocho partes de 8 bits. Como vimos antes no podemos utilizar la función print() para imprimir el número completo de 64 bits.

    Con la segunda tenemos un ejemplo de uso en decimal de un número de 64bits y la forma de mostrarlo utilizando la función print().

    El resultado de la simulación es el siguiente:

    Lec20 008

    Es importante observar que los para ordenar bien los bytes tenemos que mostrar primero los de mayor peso.

    El siguiente código de ejemplo nos permite comprobar como nuestro ARDUINO es capaz de trabajar con números muy grandes. Observe que para asignar el número de gran tamaño a la variable de 64 bits, hemos tenido que utilizar el sufijo LL para indicarle al compilador que queremos usar un número 'long long'.

    Lec20 009

    El resultado de la compilación es una cuenta incremental manejando números muy muy grandes.

    Lec20 010

    Y de esta manera, con la lección de hoy, ya hemos conseguido que nuestro ARDUINO maneje números realmente grandes.

     

  • Lección 21: Control de displays de 7 segmentos

    [Proteus versión 8.1]

    En la lección de hoy vamos a abordar la utilización de un display de 7 segmentos desde nuestro ARDUINO .

    El display de 7 segmentos es un dispositivo de salida que se utiliza para la representacion de valores numéricos. Su nombre proviene de su construcción basada en la utilización de siete leds con unas determinadas formas y conexiones internas, estratégicamente ubicados de tal forma que forman el número '8' y, opcionalmente, con un octavo led para el punto decimal.

    Lec21 001

    Cada uno de los segmentos que forman la pantalla están marcados con las ocho primeras letras del alfabeto ( de la 'a' a la 'g'), y se montan de forma que permiten activar cada segmento por separado, consiguiendo formar cualquier dígito numérico. En el caso de que incluyan el punto decimal, éste diodo se nombra como DP.

    Lec21 002

    Estos dispositivos de pueden encontar en dos configuraciones posibles, según los pines de los leds que tengan unidos: ánodo común o cátodo común. 

    Para comprender el funcionamiento de un display de siete segmentos de cátodo común, vamos a utilizar el siguiente montaje:

    Lec21 003

    Puesto que se trata de un dispositivo con el cátodo común, hemos puesto a tierra el pin común. A cada uno de los otros pines les hemos conectado una resistencia de 220 ohms en serie con un interruptor y conectado al potencial de +5V. Actuando sobre cada uno de los pulsadores podemos ver cómo se enciende el segmento correspondiente. En la imagen siguiente vemos como se ilumina el segmento 'e' al cerrar el interruptor correspondiente.

    Lec21 004

    Cómo es lógico, si cerramos los interruptores correspondientes a los segmentos a,b,c,d y g tendremos representado el número 3.

    Lec21 005

    Y de la misma forma, se pueden representar el resto de números.

    Cuando utilicemos un dispositivo de ánodo común, el funcionamiento será similar, con la única excepción de que tendremos que alimentar el pin común con los +5V y realizar el montaje de resistencias e interruptores unidos a tierra.

    Lec21 006

    El principal inconveniente de la utilización de los displays de 7 segmentos es que necesitan un gran número de líneas para su gobierno. Un control directo de un display de siete segmentos desde nuestro ARDUINO supondría utilizar siete pines (ocho si contamos el punto decimal) para gobernar un solo dispositivo.

    Con una sencilla operación de multiplicación podemos comprobar que es imposible utilizar cuatro dísplays de 7 segmentos desde nuestro ARDUINO porque necesitaríamos 28 pines.

    Por eso, la solución habitual es utilizar integrados que nos ayudan a reducir el número de pines utilizados de nuestro ARDUINO para gobernar los displays de 7 segmentos. En el siguiente montaje podemos ver la utilización de un integrado 4511 para gobernar un display del tipo cátodo común utilizando sólo cuatro pines.

    Lec21 007

    El 4511 es un decodificador BCD a 7-Segmentos de bajo coste (existen versiones que cuestan unos pocos céntimos de euro). Recibe la información del dígito a mostrar en codificación octal (4 bits) a través de los cuatros pines denominados A,B,C y D. Gobierna los siete segmentos del display por medio de los siete pines de salida marcados como QA a QG.

    El pin rotulado LT (lamp test) sirve para hacer una prueba de lámpara, es decir, que si conectamos este pin a tierra se iluminan los 7 segmentos y podemos comprobar que todos funcionan correctamente. Para un funcionamiento normal lo debemos conectar a +5V.

    El pin rotulado BL (blank) sirve para inhabilitar el display y que se queden todos los segmentos apagados. Para inhabilitar el display debemos conectarlo a tierra y para que funcione normalmente conectarlo a +5V.

    Por último, el pin rotulado LE (latch enable) permite congelar el display mostrando un determinado valor escrito en los pines de entrada del integrado. Su uso se realiza con la siguiente secuencia de trabajo. Ponemos el pin LE a tierra, utilizamos los pines A, B, C y D para indicar el número a mostrar en código octal y ponemos el pin LE a +5V. Mientras el pin LE esté conectado a +5V se ignorarán todos los cambios que se produzcan en los pines A, B, C y D manteniendo el mismo valor mostrado en el display. 

    Más tarde veremos un ejemplo de uso de este pin para gobernar más de un display desde nuestro ARDUINO utilizando el menor número de pines posible. 

    Por ahora, vamos a conectar nuestro integrado 4511 como se mostraba en la última figura.

    El código que ejecutaremos en nuestro ARDUINO para mostrar un contador incremental de 0 a 9 con un nuevo incremento cada segundo se muestra a continuación.

    Lec21 008

    Lec21 009

    El programa utiliza las funciones iniTemp() y chkTmp() que ya vimos en lecciones anteriores y que sirven para controlar la ejecución de las líneas de código convenientes cada segundo.

    Utilizamos una matriz llamada digitos, de 10 x 4 posiciones, para codificar las diez cifras en octal y facilitar la activación y desactivación de las cuatro salidas que utilizamos para controlar el integrado 4511. Usamos la variable Contador para almacenar el número a mostrar en cada nuevo ciclo.

    Al combinar el uso de la matriz digitos con la variable contador, resulta sencillo escribir el código necesario para mostrar la cifra indicada en cada bucle activando las salidas correspondientes para cada nuevo número a mostrar con unas sencillas instrucciones if..else por cada una de los cuatro pines de entrada del integrado 4511.

    El resultado es un contador incremental en el display de siete segmentos.

    Lec21 015

     

    Lec21 016

    Veamos ahora como podemos controlar cuatro displays con sólo ocho pines de nuestro ARDUINO utilizando el pin LE de cada integrado 4511. El esquema de conexionado de los cuatro displays se muestra en la siguiente imagen.

    Lec21 010

    El código de nuestro nuevo programa se muestra a continuación.

    Lec21 011

    Lec21 012

    Lec21 013

    Lec21 014

    El funcionamiento es muy similar al código que ya vimos para controlar un único display.

    La diferencia principal radica en que utilizamos cuatro pines para controlar el pin LE de cada uno de los integrados 4511 y que nuestra variable contador ahora se ha convertido en una matriz de cuatro posiciones para contener ahora los cuatro dígitos de nuestro contador.

    Con la utilizanción de los pines LE podemos compartir los mismos cuatro pines de salida de nuestro ARDUINO para enviar la información del dígito que queremos mostrar en cada uno de los cuatro displays.

    Como se puede observar la técnica consiste en activar el pin LE del integrado 4511 que gobierna el display que nos interesa excitar, escribir el código octal de la cifra a mostrar en ese display y volver a desactivar el pin LE. Y repetir esta operación para cada uno de los otros tres conjuntos de integrado y display que utilizamos para cada uno de los tres dígitos restantes.

    De esta manera cada integrado 4511 se encarga de mantener el display que controla con el digíto que deseamos mostrar mientras utilizamos los mismos pines de salida para ir enviando la información al resto de los integrados.

    Gracias a esta técnica, con sólo ocho pines podemos gobernar cuatro displays de siete segmentos. Además este código nos otorga una ventaja adicional. El tiempo de carga de trabajo de nuestro ARDUINO es muy limitado, puesto que con ayuda del uso de las funciones temporales, sólamente tenemos ocupado al microcontralador unos pocos milisegundos cada ciclo de escritura. De esta manera podemos utilizar displays de siete segmentos para mostrar información del proceso controlado por nuestro ARDUINO dejando a éste libre para realizar otras tareas de control.

    El programa es un contador incremental con una frecuencia de un segundo empezando en el número 987 (el número desde el que comienza la cuenta se configura en las líneas del código 42 a 45).

    Lec21 017

    Como siempre, esperamos que esta lección os haya resultado interesante y que encontréis aplicaciones para el uso de displays de 7 segmentos dentro de vuestros proyectos.

     

  • Lección 22: Creación de librerías

    [Proteus versión 8.2]

    En varias de las lecciones de este curso hemos utilizado una serie de funciones auxiliares que nos facilitaron la utilización de temporizadores, básculas set-reset, control de un motor, etc. Aunque estas funciones que hemos utilizado repetidamente, por ejemplo las funciones iniTemp() y chkTemp(), siempre contienen el mismo código, nos hemos visto obligados a escribirlas una y otra vez en cada nuevo programa que construíamos.

    Por supuesto, que podemos utilizar las facilidades que nos brindan los procesadores de textos actuales para copiar y pegar el código de unos proyectos a otros. Pero, tendríamos que acordarnos en qué proyecto las usamos para ir a su código y realizar el proceso de copia. Y, a medida que va pasando el tiempo, esta tarea puede terminar siendo una labor de búsqueda pesada y difícil.

    En general, todos los programadores acaban utilizando una serie de funciones de forma bastante repetitiva en todos sus proyectos. Por eso surge el concepto de librería (en algunos países también se le conoce con el nombre de biblioteca). Una librería es un fichero (o una serie de ellos) que contiene una serie de funciones que pueden ser enlazadas y utilizadas por nuestro proyecto.

    Nuestro entorno de desarrollo ya viene con una gran cantidad de librerías disponibles para utilizar en nuestros proyectos. Algunas ya las hemos ido utilizando a lo largo de este curso. Sin embargo en esta lección queremos dar un paso más allá que la mera utilización de librerías de terceras personas. Nuestro objetivo es aprender a construir nuestra propia librería con una serie de funciones básicas y a utilizarlas en uno de nuestros proyectos. Por supuesto que cada usuario podrá incluir más funciones o eliminar aquellas que él no considere útiles para adaptar la librería a sus necesidades particulares.

    En primer lugar, tenemos que tener en cuenta una cosa importante para facilitar la utilización de nuestras librerías en nuevos proyectos. El lugar donde coloquemos nuestra librería nos facilitará su uso desde nuestro entorno de desarrollo. En concreto, el sistema de desarrollo de ARDUINO busca, por defecto, todas las librerías que se encuentran en el directorio 'libraries' de la carpeta donde tengamos instalado el entorno ARDUINO. En nuestro caso (windows 7 64bits) la carpeta se encuentra en la siguiente ruta:

    Lec22 001

    Y contiene las siguientes librerías:

    Lec22 002

    Como es lógico, en cada ordenador encontraremos una lista diferente según el número de librerías de terceros que hayamos instalado.

    Nosotros, vamos a crear una librería llamada Hubor. Si el usuario quiere cambiar el nombre de su librería por uno que se adecúe más a sus gustos, sólo tiene que seguir todos los pasos de esta lección cambiando 'Hubor' por el nombre elegido en todos ellos. Lo importante es que mantengamos la coherencia a lo largo de todo el proceso para que su utilización sea intuitiva y sencilla.

    La primera tarea consiste en crear un directorio de nombre 'Hubor' en la mencionada carpeta de librerías. Dentro de esta carpeta que acabamos de crear tenemos que escribir dos ficheros de texto. Uno de ellos lo llamaremos 'Hubor.cpp' y el otro 'Hubor.h'. Por ahora están vacíos, pero más tarde iremos añadiendo los códigos necesarios. El primero contendrá el código en lenguaje 'c' de nuestras funciones. El segundo es un fichero de cabecera con información para el compilador (normalmente, una declaración directa de clases, subrutinas, variables, u otros identificadores) para el uso de la librería.

    El código completo del archivo Hubor.cpp se puede descargar desde este enlace: Fichero Hubor.cpp. Este fichero contiene el código de las siguientes funciones: iniTemp(), chkTemp(), flancoSub(), flancoBaj(), bascula_r(), bascula_s(), pulso(), pulso_mem(), parpadeo(), retraso() y ctrlMotor(). En el código, delante de cada función, figura una pequeña explicación del funcionamiento de cada una de ellas y de la forma de utilizarla dentro de nuestro código. A efectos de esta lección, vamos a concentrarnos en el comienzo del archivo con el código de las dos primeras funciones, de sobra conocidas para quien siga este curso puesto que las hemos utilizado en varias lecciones. El resto de las funciones siguen un procedimiento de construcción similar.

    Lec22 003

    Es importante observar que nuestro fichero, después de los comentarios con la información de la librería, contiene dos sentencias #include. Cuando utilizamos una sentencia include, le estamos indicando al compilador que incluya en ese punto el archivo correspondiente y lo compile con el resto de los ficheros fuente que forman nuestro programa. Puesto que sólo le indicamos el nombre del archivo, el compilador entiende que lo tiene que buscar en el mismo directorio donde se encuentra el archivo que lo está llamando (en nuestro caso la carpeta Hubor que acabamos de crear y que es dónde está situado el archivo Hubor.ccp) o en alguno de los directorios definidos por el compilador como lugares de búsqueda por defecto.

    Por lo tanto, con estas dos sentencias #include estamos diciendo al compilador que incluya en este punto los ficheros 'Hubor.h' y 'Arduino.h'. el primero es el fichero de cabecera que acabamos de crear para nuestra librería y el segundo contiene una serie de definiciones utilizadas de forma estándar por el entorno de programación ARDUINO. En realidad, estas definiciones incluidas en el fichero 'Arduino.h' las hemos estado utilizando en todos nuestros proyectos hasta ahora. Cuando creamos un programa en ARDUINO, el compilador enlaza a este fichero por defecto de forma automática para nosotros. Pero cuando construímos una librería no lo hace y, por ese motivo, se lo tenemos que indicar expresamente.

    El resto del fichero es el código de todas las funciones, tal y como lo hubierámos utilizado en cualquiera de nuestros proyectos.

    A continuación vamos a escribir el código del fichero de cabecera 'Hubor.h'. Lo podemos descargar desde el siguiente enlace: Fichero Hubor.h. Como ya dijimos antes, contiene las definiciones para ayudar al compilador a utilizar la librería.

    Lec22 004

    En la línea 15 utilizamos una sentencia #include como la que acabamos de ver en el fichero Hubor.cpp para incluir el fichero con las definiciones estándar utilizadas por el entorno ARDUINO.

    En las líneas 17 a 19 definimos los operadores lógicos para su utilización en nuestro código.

    En la linea 21 declaramos la estructura MotorDatosControl que ya vimos en la Lección 19 y que se utiliza en la función ctrlMotor() para intercambiar datos.

    Y, por último, de las líneas 35 a la 45 se incluyen los prototipos de todas las funciones incluídas en la librería. Un prototipo es una declaración de una función que se lleva a cabo con dos propósitos principales: evitar conversiones erróneas de valores (al definir el tipo de dato que utilizamos para cada parámetro pasado a la función y el tipo de dato que devuelve la propia función) y evitar errores en el número de argumentos pasados a la función.

    Y ya tenemos todo listo para utilizar nuestra librería 'Hubor.h' en cualquier nuevo proyecto. Vamos a crear el siguiente montaje:

    Lec22 005

    Donde utilizaremos nuestro Arduino con cuatro entradas (tres pulsadores y un interruptor) y siete salidas para gobernar otros tantos leds.

    El código de nuestro programa será el siguiente:

    Lec22 006

    Lec22 007

    Observe que la manera de poder utilizar todas las funciones almacenadas en nuestra librería no puede ser más sencilla. Sólo con utilizar en la línea 10 la sentencia #include correspondiente ya las tenemos todas disponibles.

    El resto de código ya no tiene que tener secretos a esta altura del curso. Primero definimos las variables de trabajo que vamos a utilizar. Luego la configuracion de los piines que vamos a usar como entradas y salidas en la función setup(). Por último el código de trabajo que se repetirá de forma cíclica en la función loop().

    En la primera parte leemos los valores de las entradas. En la segunda, utilizamos las diferentes funciones de la librería. En la tercera, escribimos las salidas. Y en la cuarta y última parte, usamos las funciones de tiempo chkTemp() e iniTemp() para enviar información cada segundo al exterior. Como en este caso no utilizamos las comunicaciones, hemos utilizado este espacio para cambiar de valor una variable llamada flag1 que utilizamos para encender y apagar cada segundo el led incluído en la placa base de ARDUINO y conectado al pin IO13.

    Si ejecutamos nuestro proyecto podemos ver como funciona cada una de las funciones de la librería. En este punto hay que hacer una salvedad importante. Para que Proteus vuelva a leer todas las librerías disponibles en el directorio 'libreries' de la carpeta 'Arduino' en lugar de compilar nuestro proyecto como siempre con la opción 'construir proyecto', necesitaremos utilizar la opción 'reconstruir el proyecto'. Una vez hecho esto la primera vez, ya podemos utilizar la opción 'construir proyecto' como siempre.

    Lec22 008

    Veamos con detalle el uso de las funciones:

    Lec22 009

    flancoSub() detecta el flanco de subida al pulsar el botón arranque1. Ese pulso de subida lo utilizamos para alimentar el set de la función bascula_r() y por lo tanto se activa la salida1. Cuando actuemos sobre el interruptor parada1 que utilizamos para alimentar el reset de la función bascula_r se desactiva la salida1.

    flancoBaj() detecta el flanco de bajada al pulsar el botón arranque2. Ese pulso al cesar la señal lo utilizamos para alimentar el set de la función bascula_s() y por lo tanto se activa la salida2. Cuando actuemos sobre el interruptor parada1 que utilizamos para alimentar el reset de la función bascula_s se desactiva la salidas.

    La diferencia entre bascula_r() y bascula_s() es la dominancia del set o el reset en caso de que ambas señales lleguen al mismo tiempo. Podemos comprobar su funcionamiento diferente ejercitando con este ejemplo.

    pulso() genera un pulso de duración igual a 5000mseg cuando se recibe la señal del pulsador arranque2 y lo muestra en la salida3.

    pulso_mem() genera un pulso de duración igual a 5000mseg cuando se recibe la señal del pulsador arranque2 y lo muestra en la salida4. 

    La diferencia entre pulso() y pulso_mem() es que la primera interrumpe la salida si cesa la señal de entrada mientras que la segunda memoriza la orden de entrada y la sigue generando después de cesar el disparador. Para interrumpir el pulso antes de alcanzar el tiempo podemos utilizar el parámetro reset de la función (en este caso parada1).

    parpadeo() genera una intermitencia sobre la salida5 mientras esté activa la entrada arranque3.

    Por último, retraso() activa la salida6 1500mseg después de que se produzca la entrada3. Si la entrada cesa antes de que pasen los 1500mseg no se llegará a generar la salida6.

    Por supuesto, que el lector ya está en condiciones de construir la librería con las funciones que él desee. Además podrá ir incorporando con el tiempo nuevas funciones acumulando trabajo para el futuro.

    Esperamos que esta leccion haya resultado interesante y que la utilización de librerías sea una práctica habitual en los próximos proyectos.

     

     

  • Lección 23: Control de un display LCD gráfico

    [Proteus versión 8.2]

    En la lección 16 estudiamos la forma de utilizar un display LCD de texto en nuestros proyectos ARDUINO. En aquella ocasión ya definimos un display LCD (siglas del inglés liquid crystal display) o pantalla de cristal líquido, como una pantalla delgada y plana formada por un número de píxeles, en color o monocromos, colocados delante de una fuente de luz o reflectora. Su principal ventaja es que utiliza cantidades muy pequeñas de energía eléctrica.

    También vimos que existen pantallas muy variadas en el mercado. Las hay de colores diferentes (monocromo verde, monocromo ámbar o color). Unas tienen capacidad para mostrar sólo caracteres de  texto y otras admiten gráficos. La lección 16 se dedicó a la familia de pantallas que sólo eran capaces de mostrar caracteres de texto. En esta ocasión queremos introducirnos en el uso de las que tienen capacidades gráficas.

    Lec23 001

    Antes de empezar con la lección hay que hacer dos salvedades importantes. En primer lugar, hay que saber que existen muchos tipos diferentes de pantallas gráficas y que su gobierno puede diferir mucho de unas a otras. Cuando vayamos a elegir la pantalla para nuestro proyecto hay que tener en cuenta dos cuestiones: la pantalla propiamente dicha (su tamaño, color o monocromo, resolución, etc) y el chip con la que se controla. Hay diferentes tipos de controlador y su programación es diferente. Nosotros vamos a ver en esta lección el control de una pantalla monocromo de 128 x 64 pixels y basada en el controlador SAMSUNG KS0108.

    Lec23 002

    En segundo lugar, es importante tener en cuenta una cosa. La programación de una pantalla gráfica es considerablemente más compleja que el de una pantalla de texto. En esta lección vamos a explicar los conceptos más básicos y realizaremos sencillos dibujos de líneas, polígonos y caracteres. Llegar a realizar una pantalla gráfica completa y profesional puede ser una tarea bastante laboriosa y que nos lleve mucho tiempo.

    Para controlar nuestra pantalla gráfica vamos a utilizar una librería para ARDUINO disponible en la página oficial en el siguiente enlace: KS0108 Graphics LCD library. Como ya hemos visto en anteriores lecciones cuando utilizamos librerías de terceros tenemos que colocarla en el directorio 'libraries' de la carpeta donde hayamos instalado nuestro entorno de desarrollo ARDUINO. En esta ocasión, la librería se creará en la carpeta llamada KS0108.

    Lec23 004

    Para simular nuestra pantalla gráfica, Proteus nos facilita varios modelos. Nosotros vamos a utilizar el llamado LGM12641BS1R. En la siguiente pantalla se muestra su representación gráfica y la forma en que lo tenemos que conectar a nuestro equipo ARDUINO.

    Lec23 003

    Es importante tener en cuenta que hemos puesto la señal de reset (RST) conectada directamente al potencial de +5V. De esta manera la pantalla está encendida en todo momento. El resto de los pines se han conectado a los pines de nuestro ARDUINO según las especificaciones de la librería que estamos utilizando. El esquema eléctrico completo es el siguiente:

    Lec23 005

    Veamos ahora el software. Vamos a empezar con un programa muy sencillo para poder ir introduciéndonos poco a poco en el control de una pantalla gráfica. Siguiendo con la costrumbre que vamos adquiriendo en lecciones anteriores partimos de un armazón básico que nos sirve de estructura para todos los programas.

    Lec23 006

    Lec23 007

    Como vamos a utilizar la librería HUBOR que ya vimos en la lección 22 y que podemos descargar desde nuestra web, hemos incluído en la línea 14 un enlace a dicha librería. En la línea 15 llamamos a la librería ks0108 que es la encargada de controlar la pantalla gráfica basada en el controlador SAMSUNG.En la línea 16 incluimos en nuestro código el fichero "SystemFont5x7.h" que contiene la definición de una familia de caracteres de 5x7 pixels. Esta fichero viene incluído en la librería ks0108 y por eso hemos optado por él.

    En la función setup() hemos escrito tres sentencias de programa. En la línea 31 inicializamos la pantalla con la función GLCD.Init() de la librería ks0108. Esta función sólo recibe un parámetro que puede tomar dos valores (NON_INVERTED y INVERTED) según deseemos que la pantalla tenga fondo claro y dibujemos en negro o tenga fondo oscuro y dibujemos en blando.

    En la línea 32 seleccionamos la familia de caracteres que vamos a utilizar para escribr textos en nuestra pantalla.Como se puede observar, pasamos como parámetro precisamente la familia de caracteres que se define en el fichero "SystemFont5x7.h".

    Por último, en la línea 33 utilizamos el primer comando para dibujar en nuestra pantalla gráfica. En este caso la función GLCD.DrawRect() que se encarga de dibujar un recuadro. Los parámetros que recibe esta función son cinco: coordenadas x e y del origen del recuadro, coordenadas x e y del fin del recuadro y color.

    Compilemos y ejecutemos nuestro programa. El resultado en pantalla, aunque no es muy espectacular, si es muy satisfactorio para el tiempo empleado en escribir el código.

    Lec23 008

    Veamos ahora un segundo ejemplo. Con este código vamos a ver un ejemplo de utilización de la mayoría de las funciones que nos facilita la librería ks0108 para dibujar en nuestra pantalla gráfica. El código completo es el siguiente:

    Lec23 013

    Lec23 014

    Lec23 015 

    La lógica de funcionamiento es como sigue.

    Hasta la línea 25 se encuentran las sentencias necesarias para llamar a las librerías utilizadas y definir las constantes y variables que vamos a utilizar a lo largo del programa.

    En las líneas 26 a 38 se encuentra la función setup() para inicializar el equipo. En ella se realiza las llamadas necesarias para inicializar la pantalla y poner a cero las variables auxiliares.

    A partir de la línea 40 se encuentra la función loop() que, como siempre, contiene las sentencias que se ejecutan de forma cíclica. Hemos divido esta función en cuatro secciones (siguiendo el esquema que ya hemos establecido como estándar en nuestras lecciones).

    En el primer bloque leemos las entradas (en este caso no hay nada que leer).

    En el segundo bloque, escribimos la lógica de control. En nuestro caso es muy sencilla. Con la ayuda de las funciones temporales iniTmp() y chkTmp() de la librería HUBOR, ejecutamos cada segundo unas sentencias que sirven para incrementar el contador y poner a uno el disparador que utilizaremos para saber cuando tenemos que escribir en nuestra pantalla.

    En el tercer bloque que utilizamos para escribir las salidas tampoco hay nada, porque en este proyecto no utilizamos salidas a parte de las utilizadas para contral la pantalla gráfica.

    En el cuarto bloque que utilizamos para controlar periféricos, se encuentran las líneas que más nos importan en esta lección y que se encargan de escribir en la pantalla. Para facilitar la visualización y comprensión de lo que escribimos en la pantalla hemos hecho lo siguiente. Cada vez que el disparador vale 1, realizamos una escritura en la pantalla y lo volvemos a poner a cero (línea 66). A continuación borramos la pantalla (línea 67).

    En las líneas 68 a 115 utilizamos la estructura de control SWITCH-CASE. Como es la primera vez que la utilizamos en este curso, vamos a empezar por explicar cómo funciona. La sentencia SWITCH es una estructura de control de bifurcación múltiple estándar en el lenguaje de programación C. En un bloque SWITCH-CASE, el controlador comprueba el valor de una variable (en nuestro caso Contador1) de forma sucesiva frente a una lista de valores (cada una de las sentencias CASE). Cuando encuentra una coincidencia, se ejecuta el bloque de sentencias asociados con el CASE hasta que encuentra un BREAK. Si no se encuntra ninguna coincidencia se ejecuta el bloque DEFAULT.

    Una vez que ya sabemos como funciona un bloque SWITCH-CASE vayamos ahora a ver cómo controlamos nuestra pantalla en cada incremento de nuestro contador. Cuando el contador vale 1 se ejecuta la función GCLD,DrawBitmap() de la librería KS0108. Esta función se encarga de dibujar en nuestra pantalla una imagen en formato de mapa de bits. En nuestro caso, la información de esta imagen se encuentra almacenada en el fichero ArduinoIcon.h que se suministra con la librería y que incluimos en la línea 16 de nuestro código.

    Lec23 016

    El resultado que se visualiza en nuestra pantalla es:

    Lec23 017

    Cuando el contador vale 2 se ejecuta un bucle repetitivo con ayuda de la estructura de control FOR. En cada iteración del bucle ejecutamos una función GLCD.DrawCircle que se encarga de dibujar un círculo en la pantalla. Los parámetros que le tenemos que pasar a esta función son las coordenadas x e y del centro, el radio del cículo y el color con el que vamos a dibujarlo. En nuestro caso, dibujamos una serie de cículos concentrícos con centro en el punto 64,32 y de radios comprendidos entre 5 y 20 en saltos de 5 en 5.

    Lec23 018

    El resultado será:

    Lec23 019

    Cuando el contador vale 3 ejecutamos la función GLCD.DrawLine() que se encarga de dibujar una línea recta. Los parámetros que debemos pasar a esta función son las coordenadas x e y del origen de la línea, las coordenadas x e y del fin de la línea y el color.

    Lec23 020

    En la pantalla se visualiza:

    Lec23 021

    Cuando el contador vale 4 ejecutamos la función GLCD.DrawVertLine() que se encarga de dibujar una línea recta vertical. Los parámetros que debemos pasar a esta función son las coordenadas x e y del origen de la línea, la longitud de la misma y el color. En este caso volvermos a utilizar un bucle FOR para dibujar una serie de líneas desde la coordenada x = 20 hasta la coordenada x=100 en saltos de 5 en 5.

    Lec23 022

    En la pantalla se visualiza:

    Lec23 023

    Cuando el contador vale 5 ejecutamos la función GLCD.DrawHoriLine() que se encarga de dibujar una línea recta horizontal. Los parámetros que debemos pasar a esta función son las coordenadas x e y del origen de la línea, la longitud de la misma y el color.

    Lec23 024

    En la pantalla se visualiza:

    Lec23 025

    Cuando el contador vale 6 ejecutamos la función GLCD.DrawRect() que se encarga de dibujar un rectángulo. Los parámetros que debemos pasar a esta función son las coordenadas x e y del origen del recuadro, las coordenadas x e y del final y el color.

    Lec23 026

    En la pantalla se visualiza:

    Lec23 027

    Cuando el contador vale 7 ejecutamos la función GLCD.DrawRoundRect() que se encarga de dibujar un rectángulo de esquinas redondeadas. Los parámetros que debemos pasar a esta función son las coordenadas x e y del origen del rectángulo, las coordenadas x e y del final, el radio con el que se redondeará las esquinas y el color.

    Lec23 028

    En la pantalla se visualiza:

    Lec23 029

    Cuando el contador vale 8 ejecutamos la función GLCD.FillRect() que se encarga de dibujar un rectángulo con relleno. Los parámetros que debemos pasar a esta función son las coordenadas x e y del origen del rectángulo, las coordenadas x e y del final y el color.

    Lec23 030

    En la pantalla se visualiza:

    Lec23 031

    Cuando el contador vale 10 ejecutamos la función GLCD.DrawLine() que ya vimos con anterioridad y que se encarga de dibujar una línea recta. Con ayuda de un bucle FOR somos capaces de dibujar una serie de líneas con origen en un punto común (0,0) y con final en un punto de coordenada x constante (127) y coordenada y variable desde 0 a 65 en intervalos de 2 pixel de separación.

    Lec23 032

    En la pantalla se visualiza:

    Lec23 033

    Cuando el contador vale más de 9 no existe ninguna claúsula CASE que se cumpla, así que se ejecutará el bloque DEFAULT. En este bloque se utilizan varias funciones. La función GLCD.CursorTo() se encarga de situar el cursor en las coordenadas x e y correspondientes. La función GLCD.Puts() escribe una cadena de texto en el lugar donde se encuentre el cursor en este momento. La función GLCD.PrintNumber() escribe un número en la pantalla. También nos ayudamos de la función estándar de C strcpy() para escribir en la variable 'Cadena' el valor de una constante de texto.

    Lec23 034

    En la pantalla se visualiza en cada nuevo incremento del contador, un mensaje con el valor del contador.

    Lec23 035

    Lec23 036

    Y con esto, terminamos nuestra lección de hoy. Ya hemos aprendido a utilizar un display gráfico. Todavía exigirá por parte del lector una buena dosis de trabajo para mostrar resultados satisfactorios en sus proyectos. Pero las bases están sólidamente fundamentadas.

    El código completo de la lección se puede descargar desde este enlace: Código lección 23.

     

  • Lección 24: Uso de un MAX7219 para controlar una fila de displays de 7 segmentos

    [Proteus versión 8.3]

    En la lección 21 vimos en profundidad la utilización de displays de 7 segmentos. También aprendimos cómo utilizar los integrados 4511 para controlar varios displays de 7 segmentos. Sin embargo, si queremos construir un cartel luminoso formado por múltiples displays de 7 segmentos, es más cómodo utilizar el integrado MAX7219.

    Este integrado nos permite controlar filas de 8 unidades de displays de 7 segementos y puede conectarse con nuestro Arduino utilizando un sistema de comunicación vía serie que sólo ocupa tres pines de nuestro controlador. Además, igual que el ejemplo que vimos con los integrados 4511, podemos utilizar varios integrados MAX7219 enlazando unos con otros, para aumentar el número de displays controlados. Por esta razón, en la práctica utilizar carteles luminosos formados por múltiples conjuntos de 8 unidades de displays de 7 segmentos practicamente exige que la mejor solución sea el uso de los integrados MAX7219. En la página www.openhacks.com podemos encontrar esta imágen con un ejemplo dónde se utiliza tres conjuntos de displays desde un Arduino.

    Lec24 001a

    Una muestra de la utilización práctica de este tipo de montajes, podría ser la creación de un panel de avisos de varias filas como el que se muestra en la imagen siguiente:

    Lec24 002

    El elemento básico de esta lección es el integrado MAX7219 de la casa  Maxim. La información técnica sobre el mismo se puede encontrar en el siguiente enlace: https://www.sparkfun.com/datasheets/Components/General/COM-09622-MAX7219-MAX7221.pdf. Básicamente se trata de un integrado que nos permite controlar ocho displays de siete segmentos (también se puede utilizar para matrices de leds de 8x8, pero eso no es objeto de esta lección). Se comunica con el microcontrolador que lo gobierna mediante el uso de un protocolo de comunicaciones serie SPI, QSPI o MICROWIRE. Incluye un decodificador BCD, lo que nos permite no tener que dibujar una matriz por cada caracter que queramos usar, resultando muy fácil escribir mayúsculas, minúsculas, dígitos y otros signos. Almacena en memoria RAM el valor de cada dígito, lo que posibilita que podamos escribir sólo el dígito que varía y que no sea necesario enviar en cada escritura todos los valores de los ocho dígitos. Y, por último, incluye en su diseño sistemas para la reducción de las emisiones electromagnéticas. En la siguiente imagen se muestra el conexionado estandar de este integrado.

    Lec24 003

    Nosotros vamos a utilizar dos integrados para controlar 16 displays. De esta manera el lector podrá conocer cómo se puede utilizar más de un integrado en sus diseños y no será complicado crear proyectos a partir de este ejemplo dónde se utilicen tres o más. El montaje completo tiene este aspecto.

    Lec24 004

    Veamos los detalles por separado para comprenderlos bien. Hemos utilizado cuatro bloques de cuatro dígitos de displays de siete segmentos cada uno de ellos con cátodo común (el modelo en proteus denominado 7SEG-MPX4-CC). Hemos unido dos bloques a un integrado MAX7219 y los otros dos bloques al otro. El conexionado de cada integrado con sus dos bloques de displays se muestra a continuación.

    Lec24 005

    Se unen los pines de cada segmento (A, B, C, D, E, F, G, DP) de cada bloque en paralelo con los pines de igual nombre del integrado. Y se unen los cuatro pines de cada bloque de displays (los ocho en total) con los pines numerados DIG0 - DIG7 del integrado.

    A continuación vemos el detalle de cómo se une cada uno de los integrados con nuestro Arduino.

    Lec24 006

    El pin CLK se une con el pin IO13 de nuestro Arduino. El pin LOAD con el pin IO10 y el pin DIN con el pin IO11. Si sólo usáramos un integrado, aquí hubiera acabado nuestro trabajo. Pero como queremos utilizar dos, el segundo integrado debe conectarse de la siguiente manera.

    Lec24 007

    Los pines CLK y LOAD se conectan en paralelo con el primer integrado, uniéndolos a los mismos pines del Arduino. Pero en el segundo integrado el pin DIN debe conectarse con el pin DOUT del primer integrado. De esta forma generamos la cadena de integrados unos detrás de otro. Si quisierámos unir un tercer integrado MAX7219, conectaríamos el pin DOUT del segundo integrado con el pin DIN del tercero. Y así sucesivamente. Nosotros hemos utilizado un terminal con el mismo nombre (LINK_MAX) para enlazar los dos integrados y simplificar el cableado. Recordemos que Proteus enlaza automáticamente los terminales del mismo nombre.

    Lec24 008

    Nuestro diseño no tiene más complicación. Realmente es sencilla la implementación de rótulos de filas de displays de siete segmentos con ayuda de los integrados MAX7219.

    No queremos terminar esta sección de conexionado sin mencionar que en el mundo real (ya sabemos que la simulación en Proteus siempre tiende a simplificarnos la tarea), hay que utilizar una resistencia y un condensador en cada integrado. Consultar la hoja de datos del integrado para ver un esquema completo de ejemplo:

    Lec24 009

    Es el momento ahora de escribir el código que deseamos ejecutar en nuestro Arduino. Como es habitual en el mundo Arduino, existe una librería que nos hará la vida muy sencilla en el uso de este tipo de integrados. En este caso, Eberhard Fahle ha escrito la librería que tiene el nombre de HCMAX7219 y puede ser descargada desde este enlace: https://github.com/HobbyComponents/HCMAX7219. La información sobre el uso de esta librería la podemos encontrar en este enlace: http://forum.hobbycomponents.com/viewtopic.php?f=58&t=1794.

    Nuestro código es tan sencillo gracias a la utilización de la librería que no consideramos necesario añadir muchas explicaciones. Simplemente, señalar que utilizamos tres funciones. HCMAX7219.print7Seg() para enviar la cadena de caracteres a los integrados. HCMAX7219.Refresh() para refrescar los caracteres que se visualizan en los displays. Y HCMAX7219.Clear() para limpiar el buffer de datos de los integrados al acabar.

    Lec24 010b

    Nos falta un punto muy importante para que nuestro programa funcione bien. La librería está por defecto escrita para gestionar un sólo integrado MAX7219 con ocho displays, pero preparada para cambiar el número de integrados a utilizar con una parametrización muy sencilla. Para indicarle que queremos utilizar más de un integrado, tenemos que ir a la carpeta donde se encuentran las librerías de Arduino (en la carpeta libraries del directorio dónde se aloja el compilador). Abrimos la carpeta de nuestra librería (HCMAX7219) y modificaremos el fichero llamado HCMAX7219.h. Buscamos la sección USER CONFIGURATION.

    Lec24 013

    Modificaremos la definición NUMBEROFDRIVERS para indicar el número de integrados que estamos usando (en nuestro caso 2).

    Y ya podemos compilar nuestro proyecto y arrancar la simulación.

    El resultado es nuestro mensaje desplazándose por los diferentes displays generando la sensación de un rótulo en movimiento.

    Lec24 011

    Lec24 012

    Esperamos, como en las ocasiones anteriores, que esta lección os permita implementar en vuestros diseños filas de displays de 7 segmentos utilizando los integrados MAX7219.

     

     

  • Lección 3: Entradas digitales, variables, una instrucción de control y generación de sonidos

    [Proteus versión 8.1]

    Dado el éxito cosechado con nuestro trabajo de las dos primeras lecciones, en esta tercera nos hemos crecido y vamos a introducir cuatro nuevos conceptos. Vamos a ver cómo podemos tener una entrada digital, a conocer la utilización de las variables, a utilizar una primera instrucción de control (el bloque if..else) y a generar notas musicales con nuestro Arduino.

    En lecciones anteriores hemos visto cómo podíamos actuar sobre otros dispositivos utilizando las salidas digitales. Activando una salida podíamos encender un led y desactivándola podíamos apagarlo. Aunque sea de forma muy rudimentaria todavía, lo cierto es que ya sabemos cómo nuestro equipo Arduino puede utilizarse para gobernar dispositivos.

    El siguiente paso importante consiste en lograr que nuestro Arduino sepa cómo es el mundo de su alrededor. Para ello uno de los caminos disponibles es la utilización de las entradas digitales. Con una entrada digital somos capaces de saber si un determinado dispositivo está activado o desactivado. Es muy importante que nos fijemos en el matiz. Con una entrada digital sólo podemos saber si un dispositivo está en uno de los dos estados posibles: activado o desactivado. No hay término medio. El caso más simple es la utilización de un interruptor. Un interruptor puede estar abierto o cerrado. Con una entrada digital nuestro Arduino es capaz de saber si el interruptor está abierto o cerrado.

    Proteus nos posibilita simular un interuptor utilizando el dispositivo "SWITCH". 

    Lec03 001

    Como podemos observar, junto al interruptor hay dos pequeños botones con una flecha apuntando para arriba y otra para abajo. Si pulsamos sobre la primera simulamos el cierre del interruptor y si pulsamos sobre la segunda simulamos su apertura.

    Lec03 002

    El estado inicial del interruptor (abierto o cerrado) se puede establecer editando sus propiedades utilizando en el menú contextual la opción "Editar las propiedades". Para abrir el menú contextual en Proteus debemos pulsar con el botón derecho del ratón sobre el dispositivo en cuestión. En la ventana de diálogo que se abre, debemos seleccionar la casilla "Editar todas las propiedades como texto" y entonces podremos ver la variable STATE. Si le asignamos el valor 1 el dispositivo se inicia cerrado y si le asignamos el valor 0 lo hará abierto.

    Lec03 003

    Para conocer el estado de nuestro interruptor con una de las entradas digitales de nuestro Arduino tenemos que dar los siguientes pasos. Primero definir que vamos a utilizar ese pin como una entrada. Segundo conectarlo eléctricamente a nuestro pin. Y, tercero y último, leer el estado del pin desde nuestro programa. Vamos a dar los tres pasos.

    Ya vimos que para definir si un pin va a funcionar como entrada o salida, utilizábamos la instrucción pinMode() dentro de la función setup(). En nuestro caso deseamos conectar nuestro interruptor al pin 5, así que nuestro código deberá ser:

    Lec03 004

    Para conectar eléctricamente nuestro interruptor con el pin 5 de nuestro Arduino podríamos tener la tentación de utilizar un esquema como el siguiente.

    Lec03 005

    Sin embargo esta forma de conectar una entrada es una mala práctica que debemos erradicar desde el principio de nuestros diseños. Aunque a primera vista puede parecer que todo es correcto, existe un problema serio en utilizar las entradas de esta forma. Cuando el interruptor está cerrado el pin 5 está en un estado conocido, +5V, y la entrada estará activa. Pero, ¿qué pasa cuando el interruptor está abierto? La respuesta rápida podría ser que la entrada está a 0V y, por lo tanto, desactivada. Pero esta respuesta no es correcta. En realidad cuando el interruptor está abierto la entrada se encuentra en un estado indeterminado puesto que no está conectada a ningún potencial conocido. Para entendernos, que un cable no esté conectado al positivo de la fuente no significa que esté conectado al negativo. Los denominados "ruidos" o interferencias en el circuito pueden generar potenciales indeterminados en nuestro pin de entrada. Para evitar esta indefinición el circuito que debemos utilizar es el siguiente que fija claramente el estado del pin a +5V o a 0V.

    Lec03 006

    Para conocer desde nuestro programa si el pin está activo o desactivado, utilizamos la función digitalRead(). La sintaxis de esta función es la siguiente:

    digitalRead( <número de pin> )

    La función devolverá un valor 1 cuando el pin esté activo y un valor 0 cuando el pin esté desactivado.

    Ahora ya estamos en condiciones de utilizar en el código del programa nuestra primera entrada digital para conocer el estado del interruptor y decidir si debemos encender el led o apagarlo. Aunque sería más preciso decir casi estamos en condiciones. Porque la frase incluye la palabra "decidir" que hasta ahora no habíamos utilizado. Tomar decisiones es una de las tareas más importantes de los ordenadores y precisamente su potencia viene de esta capacidad de decisión. Para tomar decisiones, utilizamos las instrucciones de control.

    La instrucción de control más básica son los bloques de código if...else. El objeto de un bloque de código if...else es evaluar una determinada condición y en función de que sea verdadera o falsa ejecutar sentencias diferentes. La sintaxis de un bloque if...else es la siguiente.

    if( <condición> )

    {

       instrucciones que se ejecutan si la condición se cumple (vale 1)

    }

    else

    {

       instrucciones que se ejecutan si la condición no se cumple (vale 0)

    }

    Un ejemplo nos ayudará a entenderlo mejor. Nuestro objetivo es encender el led situado en el pin 0 cuando el interruptor situado en el pin 5 esté activo y apagarlo cuando esté desactivado. El código de nuestro programa sería el siguiente.

    Lec03 007

    Y el funcionamiento lo podemos probar con el siguiente circuito. Con el pulsador S1 gobernamos el estado del led D2.

    Lec03 008

    Hasta ahora nos hemos limitado a que nuestro Arduino sea capaz de encender o apagar leds. Es decir que somos capaces de hacer señales luminosas con nuestro Arduino. Pero también podemos generar señales acústicas si en lugar de conectar un led a un pin de nuestro Arduino, le conectamos un altavoz piezoléctrico. El altavoz piezoeléctrico es un tipo de altavoz basado en la propiedades de los cristales piezoeléctricos (poliéster o cerámica), que se deforman cuando se les aplica una tensión entre sus caras y que actuando como transductor electroacústico es utilizado para la reproducción de sonido. También se le suele conocer con el nombre de "chicharra".

    Lec03 009

    En Proteus podemos simular un altavoz piezoeléctrico con el dispositivo SPEAKER.

    Lec03 010

    Por defecto, el dispositivo SPEAKER viene configurado para funcionar a 1V. Para adaptarlo a nuestra tensión de 5V debemos editar sus propiedades y cambiar este valor en la variable "Nominal Input".

    Lec03 011

    El esquema que utilizaremos en nuestro diseño es el siguiente:

    Lec03 012

    Y el código de nuestro programa:

    Lec03 013

    Podemos realizar la simulaicón. Si actuamos sobre el interruptor S1 se encenderá el led D2 y se escuchará un sonido en el altavoz. Si actuamos sobre el interrumptor S2 cuando el S1 está activado, provocaremos que la nota que se escucha en el altavos sea una octava diferente. Si desactivamos el interruptor S1 el led se apaga y el altavoz deja de sonar.

    Una vez que hemos entendido el funcionamiento de nuestro programa, podemos analizarlo en detalle. La primera parte la dedicamos a declarar variables. Una variable está formada por un espacio en el sistema de almacenaje (memoria principal de un ordenador) y un nombre simbólico (un identificador) que está asociado a dicho espacio. Ese espacio contiene una cantidad o información conocida o desconocida, es decir un valor. El nombre de la variable es la forma usual de referirse al valor almacenado: esta separación entre nombre y contenido permite que el nombre sea usado independientemente de la información exacta que representa. En nuestro caso, hemos definido ocho variables que las usamos para identificar los pines 0 a 7 de nuestro Arduino. Las variables las hemos utilizado para inicializar los pines indicando si los utilizaremos como entrada o salida, y dentro de las funciones de escritura y lectura de su estado. Como las variables contienen valores numéricos al definirlas hemos puesto delante la palabra clave INT. Con ello indicamos que esa variable almacenará un valor numérico de tipo entero.

    En este ejemplo todavía no hemos sacado mucho partido a la utilización de las variables, porque en realidad las estamos utilizando como si fueran constantes (les asignamos un valor al principio que no cambia a lo largo de toda la vida del programa). Pero en lecciones posteriores iremos utilizando y aprovechando muchos más el uso de las variables.

    El núcleo del programa se basa en el uso de un primer bloque if...else. Con él evaluamos el estado del pin5 y ejecutamos un código u otro en función del resultado. En caso de que el pin5 esté activo, encendemos el led conectado al pin0 y volvemos a evaluar un segundo bloque if...else. De esta manera acabamos de descubrir una importante características de los bloques de control: que pueden anidarse. Anidar varios bloques de control significa que podemos utilizar unos dentro de otros que se irán ejecutando o no en función de las condiciones.

    Para generar las dos notas diferentes que suenan en el altavoz hemos hecho lo siguiente. Cuando el interruptor conectado al pin6 está activo, activamos y desactivamos el altavoz con un espacio de 2272 microsegundos entre cada operación. La función delayMicrosegundos() tiene una sintaxis muy similar a la función delay() ya estudiada en lecciones anteriores, sólo que, en este caso, el intervalo de tiempo que mantenemos dormido al procesador viene expresado en microsegundos en lugar de segundos.

    Para generar la nota una octava más alta cuando el interruptor S2 no está pulsado, lo que hacemos es activar y desactivar el altavoz con un espacio de separación de 1136 microsegundos.¿Por qué se produce este cambio con estos valores? Vamos a estudiarlo con detalle para entenderlo.

    Un sonido tiene un determinado tono que depende de la frecuencia a la cual vibra el aire. Las notas musicales son vibraciones de frecuencias determinadas. Una vibración a una frecuencia concreta, produce un sonido puro que nosotros percibimos como un pitido de un determinado tono. En el sistema musical occidental, se ha acordado utilizar sólo unas frecuencias concretas, a las cuales llamamos notas.Dividimos las posibles frecuencias en porciones que llamamos "octavas", y cada octava en 12 porciones que llamamos notas. Cada nota de una octava tiene exactamente la mitad de frecuencia que la misma nota en la octava superior. El oido humano capta sólamente frecuencias que estén por encima de los 20Hz y por debajo de los 20.000 (muy aproximadamente). Así pues, y con mucha suerte, sólo podemos oir unas 10 octavas como mucho, con doce notas cada una. La nota "La" sirve como referencia para todas las demás. A menudo se denomina "nota de afinar". Se produce un "La" de afinar cuando el aire vibra 440 veces por segundo, es decir a 440 hertzios. Por convención, a la octava que contiene esta nota "La" se le suele considerar la cuarta.Hay otra nota "La", de una "octava" superior (la quinta octava) cuando el aire vibra a 880 hertzios. Del mismo modo, hay un "La" que se produce cuando el aire vibra a 220Hz (tercera octava).

    Por lo tanto, para conseguir una determinada nota, sólo tenermos que hacer vibrar nuestro altavoz a una determinada frecuencia. La tabla que identifica cada nota con su frecuencia y toda la teoría relativa a este tema la podemos encontrar muy bien explicada en el siguiente enlace: http://latecladeescape.com/t/Frecuencia+de+las+notas+musicales.

    Una vez que sabemos que queremos emitir un La4 y un La5 tenemos que hacer vibrar nuestro altavoz a unas frecuencia de 440Hz y 220Hz respectivamente, ya podemos hacer los cálculos necesarios para escribir nuestro código. Para obtener una frecuencia de 440Hz necesitamos activar y desactivar nuestro altavoz con una separación de tiempo entre ambas órdenes (es decir, con un periodo) de 2.272 µsegundos:

    frecuencia = 1 / periodo.

    440 = 1 / x   ---> x = 1 /440 = 0,002272 segundos = 2.272µsegundos.

    Como la mitad de ese tiempo tiene que estar el altavoz encendido y la otra mitad apagado, la separación entre ambas órdenes debe ser:

    2.272 / 2 = 1.136

    Ese es el valor que hemos escrito cuando nuestro pulsador está desactivado. Para obtener un La5 (una octava superior) utilizamos el valor 2.272 porque:

    220 = 1 / x  ---> x = 1 / 220 = 0,004545µsegundos. La mitad de ese valor 4.545 / 2 = 2.272.

    Acabamos de enseñarle los rudimentos de la música a nuestro Arduino, pero todavía nos queda un largo trecho hasta que consigamos que Arduino funcione como un instrumento musical y podamos interpretar una melodía con él. Pero es que hay que dejar algo para las próximas lecciones.

     

  • Lección 4: Utilizar las comunicaciones serie para enviar información del Arduino a nuestro ordenador

    [Proteus versión 8.1]

    Hemos visto en las lecciones anteriores como nuestro Arduino puede interactuar con el mundo exterior recibiendo información por sus entradas digitales y generando órdenes con sus salidas digitales. A estas alturas del curso todavía las "capacidades sociales" de nuestro Arduino son muy limitadas, pero en esta lección vamos a intentar aumentar su círculo social. Las comunicaciones serie son una forma sencilla y flexible de comunicar nuestro Arduino con otros equipos. Por ejemplo con otro arduino, con nuestro ordenador personal o con cualuqier otro dispositivo que sea capaz de comunicarse por un puerto serie.

    Habilitar las comunicaciones serie entre dos equipos involucra tareas referntes al hardware y al software. El hardwre posibilita el intercambio de señales eléctricas entre los dos equipos con unos valores normalizados que entienden ambos interlocutores. El software se apoya en el hardware para intercambiar información entre los equipos. Las librerías de comunicaciones serie suministradas con Arduino nos liberan de prácticamente todo el trabajo y sólo tenemos que preocuparnos de dos cosas: conectar los dos equipos en los pines correctos y utilizar las funciones disponibles para mandar las cadenas de caracteres deseadas. Sin embargo hay unas nociones básicas que nos resultarán útiles para poder solucionar problemas cuando nuestras comunicaciones fallen. Si el lector está familiarizado con las comunicaciones serie entre microprocesadores puede saltar esta introducción.

    [Introducción a las comunicaciones serie]

    La comunicación entre dos dispositivos se establece intercambiando cadenas de caracteres. Por ejemplo podemos querer enviar la cadena de caracteres "Saludos". Cada caracter de nuestra cadena se codifica utilizando un byte según un código que se conoce con el nombre de ASCII. Cada byte se compone de un conjunto de ocho bits distintos. El bit es la unidad mínima de información y sólo toma valores digitales (verdadero o falso). Es decir que para enviar nuestra cadena de ejemplo entre los dos microprocesadores, lo que tenemos que enviar es una "serie" de bits concreta.

    Por ejemplo, para enviar nuestra cadena "Saludos" tendríamos que enviar una serie de 56 bits (7 bytes).

    Las comunicaciones serie a nivel de hardware se establecen enviando cadenas de pulsos eléctricos unos a continuación de otros (en serie), cada uno de los cuales representa un bit de información. Si el nivel eléctrico del pulso es bajo, es decir su tensión es de 0V, significa que el valor de ese bit es 0. Si el nivel del pulso es alto, es decir su tensión es 5V, significa que el valor de ese bit es 1. Por lo tanto nuestra cadena de 56 bits se tiene que convertir en un tren de pulsos de niveles alto o bajo.

    Lec04 001

    Muchas de las tarjetas Arduino tiene incluido un chip que se encarga de traducir los valores de tensión TTL a los niveles estándar utilizados por las comunicaciones serie que cumplen el estándar USB. De la misma forma podríamos utilizar un chip para convertir nuestro puerto serie en otro de un estandar diferente (por ejemplo RS232, CANBUS, etc). En cualquier caso el funcionamiento del software sería el mismo.

    Por último, con objeto de realizar labores de sincronizacióin y de verificación de errores entre el emisor y el receptor, la cadena de bits se enmarca entre códigos de control estandarizados por el protocolo de comunicaciones (bits de arranque, bits de parada y bits de control).

    Lec04 002

    [Fin de la introducción de las comunicaciones serie]

    Como todo este curso lo estamos realizando utilizando Proteus como simulador, necesitamos simular de alguna manera nuestro ordenador PC realizando funciones de terminal para comunicarse con nuestro equipo Arduino. En la barra de herramientas de la izquierda seleccionamos el botón instrumentos virtuales y en la lista que aparece escogemos "virtual terminal".

     Lec04 003

    Colocamos el terminal virtual en nuestro esquema y lo unimos a los terminales TXD y RXD.

    Lec04 004

    El terminal TXD está unido por una resistencia al pin IO1 y el terminal RXD al pin IO2 del Arduino (los pines del puerto serie). Observar que hemos unido el terminal TXD del Arduino con el RXD del terminal y el RXD del Arduino con el TXD del terminal. De esta manera cruzada el emisor de uno se une al receptor del otro. De esta forma tan sencilla ya tenemos enlazado nuestro equipo Arduino con nuestro ordenador PC.

    A continuación tenemos que escribir nuestro programa que se encargará de enviar una cadena de caracteres hacia el terminal virtual utilizando el protocolo serie.

    Lec04 005

    Nuestro programa es muy sencillo. En la zona de configuración inicializamos el puerto serie para que se comunique a una velocidad de 9600 baudios. El baudio es la unidad de medida de la velocidad de las comunicaciones serie. Un baudio equivale a un bit por segundo.  Y en la zona del lazo de ejecución enviamos la cadena Saludos por el puerto serie, esperamos un 1 segundo y volvemos a iniciar el ciclo. El resultado, si ejecutamos nuestro programa, tiene que ser ver en el terminal virtual una cadena de saludo cada segundo.

    Lec04 006

    Si no puede ver el terminal virtual, compruebe que lo tiene activo en el menú Depuración en la opción 5. Virtual terminal.

    El siguiente programa combina varios de los conceptos vistos en lecciones anteriores y utiliza variables, salidas digitales, comunicaciones serie para enviar un mensaje cada segundo, cambiar el estado de un led cada nuevo mensaje. Utilizamos un variable llamada número para hacer las funciones de contador y distinguir cada mensaje enviado.

    Lec04 007

    Lec04 008

    Observe que hemos utilizado dos funciones diferentes para enviar cadenas por nuestro puerto serie. La función Serial.print(), ya vista antes, envía una cadena de caracteres o un número por el puerto serie. Sin embargo, la función Serial.println(), después de enviar la cadena de caracteres o el número por el puerto serie, envía también un código de control que genera un salto de línea. De esta forma, en este nuevo programa resulta más sencilla la lectura en el terminal virtual de la información recibida porque cada nuevo mensaje, y el contador que los numera, se muestra en una línea diferente.

    La función Serial.println() nos permite enviar números con formatos diferentes (decimal, octal, hexadecimal, binario, etc). El siguiente programa muestra un ejemplo de utilización de estas posibilidades.

    Lec04 009

    Lec04 010

     

  • Lección 5: Entradas analógicas

    [Proteus versión 8.1]

    Hasta este momento hemos visto que nuestro Arduino era capaz de saber lo que pasaba a su alrededor utilizando las entradas digitales. De esta manera podía conocer el estado de determinados dispositivos que sólo eran capaces de discernir entre dos estados: activo-desactivo (abierto-cerrado, conectado-desconectado, verdadero-falso, etc). El ejemplo más sencillo consiste en determinar si un interruptor estaba abierto o cerrado, pero existen muchos otros dispositivos disponibles que reciben el nombre de transductores (finales de carrera, células fotoeléctricas, detectores de proximidad, detectores de presión, etc). 

    Hay muchos otros casos en los que necesitamos saber el valor de una determinada magnitud. La distancia a un determinado lugar, la temperatura de un horno, el nivel de agua de un depósito, el voltaje de una fuente de alimentación, son algunos ejemplos. Para que nuestro Arduino pueda conocer estos valores utilizamos las entradas analógicas.

    El esquema de funcionamiento de una entrada analógica es el siguiente. El transductor convierte una determinada magnitud física (distancia, calor, caudal, etc) en una magnitud eléctrica (tensión o corriente). El transductor se conecta a uno de los pines de entrada analógica de nuestro Arduino. Como las entradas analógica del Arduino sólo son capaces de leer valores de tensión comprendidos entre 0 y 5V, si el transductor suministrara otros valores diferentes necesitariamos poner entra la salida del transductor y la entrada analógica un conversor adecuado. Por último, el microprocesador incluido en nuestro Arduino dispone de una unidad de conversión analógico-digital que convierte los valores de tensión de la entrada en valores numéricos comprendidos entre 0 y 1023. El número 1023 no es una casualidad. Los conversores incluidos en el micro tienen una precisión de 10bits. Los valores posibles que se pueden obtener con 10 bits van de 0 a 1023. En la imagen siguiente se muestra un esquema de una solución completa compleja.

    Lec05 001

    El termómetro (transductor) situado en el horno nos devuelve valores de corriente comprendidos entre 4 y 20mA. Cuando la temperatura es de -20°C devuleve 4mA y cuando al temperatura es de 200°C devuleve 20mA. Por eso necesitamos utilizar un conversor que pase de 4-20mA a 0-5V. Por último el conversor analógico-digital del Arduino nos devolverá valores comprendidos entre 0 (cuando la temperatura sea de 0°C) y 1023 (cuando la temperatura sea de 200°C). Conocer a qué temperatura corresponde un determinado valor que nos proporciona Arduino es sólo una cuestión de utilizar una regla de tres.

    Arduino dispone de seis entradas analógicas conectadas a los pines 23 a 28 y que reciben los nombres ADC0 - ADC5.

    Para poder probar una entrada analógica, la forma más sencilla es utilizar un potenciómetro haciendo las veces de un divisor de tensión. De esta manera, si conectamos un extremo a los 5V y el otro a GND obtendremos los diferentes valores posibles moviendo el potenciómetro. Veamos el esquema electrónico del montaje para esta lección donde utilizaremos el terminal virtual que vimos en la lección 4 y el montaje del potenciómetro para simular valores de tensión en la entrada analógica (en la zona inferior derecha del montaje).

    Lec05 002

    La función que nos permite recuperar el valor leído en un pin de entrada analógico se llama analogRead(). Su formato es:

    <valor devuelto entre 0 y 1023> = analogRead( <numEntradaAnalogica>)

    Donde numEntradaAnalogica es un valor comprendido entre 0 y 5.

    El código de nuestro programa es el siguiente:

    Lec05 003

    El funcionamiento es muy simple. Leemos el valor de la entrada analógica 0 (la que se conecta en el pin AD0) y enviamos el valor al terminal virtual. Esperamos un segundo y volvermos a repetir el proceso. La espera de un segundo la hacemos con objeto de no estar enviado constantemente valores al terminal lo que dificulta mucho la lectura. Si ejecutamos la simulación y movemos el potenciómetro iremos obteniendo los valores calculados entre 0 y 1023 en nuestro terminal virtual.

    Lec05 004

    Utilizar entradas analógicas es muy sencillo con Arduino. 

    Veamos otro ejemplo de utilización de entradas analógicas donde Arduino toma decisiones en funicón de los valores leídos en una entrada digital. Supongamos el ejemplo de configuración de nuestro horno que vimos antes. Imaginemos que deseamos que nuestro Arduino mantenga el horno encendido hasta alcanzar una determinada temperatura. Al hacerlo pararemos el horno. Cuando la temperatura vuelva a bajar, nuestro Arduino volverá a encender el horno. De esta forma podemos mantener nuestro horno a una temperatura constante.

    El código de nuestro programa sería el siguiente:

    Lec05 005

    Como se puede ver el programa es muy sencillo. Cuando la lectura analógica es menor que 300 se activa la salida digital y en caso contrario se apaga. Para comprobar el funcionamiento, simularemos la salida que acitva el horno con un led y la entrada del termómetro con un potenciómetro. La simulación se muestra a continuación. Moviendo el potenciómetro simulamos la temperatura y cuando se alcanza el valor adecuado se activa o desactiva la salida y se enciende o apaga el led.

    Lec05 006

    Lec05 007

    Ya somos capaces de utilizar entradas digitales en nuestro equipo Arduino.

     

  • Lección 6: Introducción a las salidas analógicas

    [Proteus versión 8.1]

    En lecciones anteriores vimos que nuestro equipo Arduino era capaz de controlar diferentes dispositivos en función de la lógica de su programa. Para hacerlo utilizábamos las salidas digitales. Cuando queríamos encender un led, activábamos una salida digital y cuando lo queríamos apagar, la desactivábamos.

    Al igual que sucedía con las entradas, el mundo de las salidas no acaba en lo meramente digital. Cuando necesitamos gobernar un determinado dispositivo no solo controlando que esté activado o desactivado, sino que deseamos manejar el grado mayor o menor de una determinada magnitud, utilizamos las salidas analógicas. Algunos ejemplos de utilización de las salidas analógicas son variar la intensidad luminosa para que un foco dé más o menos luz, controlar el caudal de sálida de un líquido variando el nivel de apertura de una electroválvula o modificar la velocidad de un motor. 

    Cuando vimos en la lección anteror las entradas analógicas, vimos que el microprocesador leía el valor de tensión que se generaba en uno de sus pines (dentro de un rango que oscilaba entre 0 y 5V) y lo traducía a valores entre 0 y 1023. En muchos microprocesadores, el procedimiento para generar salidas digitales es exactamente el contrario. Escribimos en un determinado registro un valor entre 0 y 1023 y el microprocesador se encarga de poner uno de sus pines a un valor de tensión correspondiente entre el rango 0 y 5V.

    Sin embargo nuestro querido Arduino no dispone de pines de salida analogíca. Así que tiene que utilizar otra técnica diferente denominada modulación de ancho de pulso (PWM pulse width modulation). Con esta técnica se puede utilizar un pin de salida digital haciéndole funcionar como una salida analógica. La técnica consiste en generar en el pin de salida un tren de pulsos de frecuencia constante (490Hz) y de tensión igual a cinco voltios en el ciclo alto y 0 voltios en el ciclo bajo (los únicos posibles en un pin digital). 

    Lec05 001

    Para obtener los diferentes valores analógicos se modifica el ciclo de trabajo de la señal periódica. Si hacemos que el 0% del tiempo la señal esté en el nivel bajo y el 100% en el nivel alto, obtendremos una señal de 5V. Si hacemos que el 25% del tiempo esté en el nivel bajo y el 75% en el nivel alto, obtendremos una señal de 3,75V. Con la señal al 50% de nivel alto y bajo, tendremos una señal de 2,5V. Con la señal al 75% de nivel bajo y 25% de nivel alto, tendremos una señal de 1,25V. Y si la señal está el 100% del tiempo a nivel alto, la señal de salida será de 0V.

    Lec06 013

    Ejemplo de señal con el 75% del ciclo en nivel alto que genera una salida de 3,75V.

    Lec06 002

    Ejemplo de señal con el 25% del ciclo en nivel alto que genera una salida de 1,25V.

    Lec06 003

    Utilizando la técnica PWM podemos tener salidas analógicas en nuestro Arduino utilizando algunos de sus pines digitales. Por ejemplo, el ATmega8 habilita la función PWM para los pines 9,10 y 11. El ATmega168 y el ATmega328P nos permite usar PWM con los pines 3,5,6,9,10 y11.

    Para controlar el ciclo de trabajo de nuestra señal PWM podemos utilizar la función analogWrite(). El formato de esta función es:

    analogWrite( <número pin> , <valor> )

    Donde <número pin> se utilizar para seleccionar el pin en el que vamos a generar nuestra señal pwm y <valor> es un número comprendido entre 0 y 255.

    Como es habitual en nuestro curso, vamos a realizar ejemplos prácticos que nos permitan aprender el funcionamiento de las salidas analógicas. Utilizaremos el siguiente circuito:

    Lec06 004 

    Hemos utilizado en esta ocasión el instrumento virtual osciloscopio que nos proporciona Proteus. Con este instrumento virtual podemos usar en nuestra simulación un osciloscopio como lo  haríamos en la vida real. Hemos conectado el pin IO3 al primero de los cuatro canales disponibles en nuestro osciloscopio.

    Nuestro primer programa va a ser muy sencillo. Simplemente crearemos una variable entera, le asignaremos el valor 100 y utilizaremos la función analogWrite() que acabamos de ver. El código completo es el siguiente:

    Lec06 005

    Y el resultado de ejecutar nuestra simulación se muestra en la siguiente imagen.

    Lec06 007

     

    Practiquemos cambiando el valor de la variable valor_salida en el rango 0 a 254 y compruebe el efecto obtenido en la salida de nuestro arduino con ayuda de nuestro osciloscopio virtual. Para un valor 200, la señal leída por nuestro osciloscopio virtual es la siguiente.

    Lec06 008

    Como modificar nuestro programa cada vez, para variar la salida pvm es un poco tedioso, vamos a cambiar un poco nuestro programa para variar nuestra salida analógica en función de la lectura de una entrada analógica que iremos manipulando con ayuda de nuestro potenciómetro. El nuevo código sería:

    Lec06 009

    Observe como asignamos el valor de la entrada analógica a la variable valor_salida. Como los valores de lectura analógica abarcan un rango de 0 a 1023 y las salidas utilizan el rango de 0 a 254, dividimos el valor de entrada entre 4 para obtener valores proporcionales en la salida.

    El resultado de la simulación con tres valors posibles de nuestro potenciómetro se muestran en las siguientes imágenes:

    Lec06 010

    Lec06 011

    Lec06 012

    En próximas lecciones ahondaremos en el uso de las salidas digitales.

     

     

  • Lección 7: Salidas digitales de potencia

    [Proteus versión 8.1]

    En las primeras lecciones de este curso abordamos el tema de las salidas digitales. En la lección de hoy queremos profundizar y ampliar nuestros conocimientos un poco más allá. Cuando tratamos el asunto de las salidas digitales, lo hicimos siempre, por motivos pedagógicos puesto que estábamos empezando, como si en el mundo todo funcionara con tensiones de corriente contínua de 5V. Esta simplificación fue muy conveniente, pero dista mucho de ser la realidad del día a día.

    La mayoría de los equipos útiles que podemos gobernar con nuestro Arduino funcionarán con alimentaciones diferentes.Si queremos controlar el motor que se encarga de la apertura y cierre de la puerta del garaje, la bomba que alimenta de agua un depósito desde el que regamos nuestra huerta, el encendido y apagado autómatico de las luces de nuestro porche, sólo por citar algunos ejemplos, la posibilidad de que estos elementos funciones con una corriente contínua de 5V es prácticamente nula. Así pues, nos tenemos que enfrentar a la problemática en la que nuestro arduino se ve obligado a gestionar elementos externos que funcionan con otras tensiones y/o que tienen un consumo de corriente más elevado que el que se puede gestionar directamente con una salida digital.

    Afortunadamente tenemos a nuestra disposicion numerosas técnicas que nos permiten afrontar esta problemática eligiendo la mejor opción en cada caso para nuestro problema. El aspecto más importante que tenemos que tener en cuenta es mantener la independencia entre uno y otro circuito. Por un lado, se encuentra el circuito de mando (gestionado por nuestro Arduino y que, por fuerza, debe funcionar con las especificaciones de tensión y corriente de las placas Arduino) y por otro lado, debemos diseñar el circuito de potencia (que alimentará o dejará sin alimentación el equipo gobernado). Nuestro objetivo es interelacionar los dos circuitos, pero manteniendo aislados el uno del otro. Precisamente, el nivel de seguridad que deseemos obtener respecto al aislamiento de los dos circuito, nos determinará la solución que debemos seleccionar en cada caso.

    Primera solución: Utilizar un transistor (o componente similar) para relacionar un circuito con otro.

    Este es, posiblemente el caso más sencillo. Supongamos que queremos controlar con nuestro arduino, con ayuda de un interruptor, una lámpara de incandescencia que funciona a 220VAC y que consume 60W. Antes de empezar con nuestro equipo Arduino, queremos hacer aquí un inciso referente a la simulación con Proteus. La forma más gráfica de simular un circuito que alimente una bombilla de 220VAC se muestra en la imagen siguiente:

    Lec007 001

    Hemos utilizado un componente del tipo LAMP (al que configuramos para que funcione a 220VAC y 50Hz) para simulara la bombilla y un componente tipo ALTERNARTOR (al que también configuramos para que funcione a 220VAC y 50Hz) para simular la alimentación de la red. En este caso hemos puesto un interruptor para encender y apagar, así el lector puede comprobar que todo funciona correctamente. Por último queremos destacar el hecho de que hemos cerrado el circuito alternador y lámpara uniendo los dos extremos a un terminal de tierra. Esto puede parecer chocante y muchos hubieran optado por cerrar directamente el alternador con la lámpara sin usar el terminal de tierra. Sin embargo, cuando se utilizan simuladores siempre es muy conveniente fijar todos los potenciales para facilitar la tarea de resolución de los circuitos al motor del simulador. Por eso es una buena práctica colocar un terminal del generador a GND. Como en Proteus todos los terminales del mismo potencial están unidos internamente, nos ha bastado con poner el extremo libre de la lámpara también unido a un terminal de tierra para que todo el circuito esté cerrado y funcione.

    Si activamos la simulación, la luz debe aparecer luciendo al cerrar el interruptor.

    Lec007 002

     

    Ahora ya podemos empezar a ver nuestro primer circuito utilizando Arduino y un transistor a la salida. Sustituyamos el interruptor por un transistor y cebemoslo con una salida digital de nuestro equipo. El montaje, muy simple para centrarnos en lo fundamental, lo hemos diseñado como sigue:

     Lec007 003

    El código fuente de nuestro programa, que también es muy sencillo, lo hemos escrito como sigue:

    Lec007 004

    Si ponemos en marcha la simulación y actuamos sobre la entrada digital, nuestro Arduino activará su salida y encenderemos la lámpara a 220VAC.

    Lec007 005

    Acabamos de aprender la manera de gobernar un elemento que funciona con alimentaciones diferentes a la de nuestro equipo Arduino. Pero, ojo, la solución tiene limitaciones importantes. Sugongamos que el transistor se cortocircuita. En este caso, pondríamos uno de los pines de nuestro Arduino a una tensión de 220VAC y, muy probablemente, lo dejemos inservible. Por eso es conveniente, utilizar otras opciones que aislen los dos circuitos.

    Segunda solución: Utilizar un relé.

    La segunda solucion consiste en la utilizacióin de un relé para separar ambos circuitos. Nuestro montaje en este caso sería el siguiente:

    Lec007 006

    Hemos utilizado un elemento de tipo RELAY al que hemos configurado para funcionar a 5V. Vemos que en este caso el aislamiento entre ambos circuitos está garantizado, puesto que el circuito de mando circula a través de la bobina del relé, mientras que el circuito de potencia lo hace a través de uno de los contactos libres de potencial.

    Tercera solución: utilización de un optoacoplador.

    En determinadas ocasiones podemos encontrar problemas para la utilización de los relés. Por ejemplo, porque necesitemos velocidades de conmutación por encima de las posibilidades de un dispositivo electromecánico como el relé. En esos casos podemos encontrar una solución en la utilizacion de un optoacoplador con un transistor de potencia a su salida. Dependiendo de los casos (voltaje y corrientes demandadas a la salida) podríamos prescindir del transistor, pero corregir nuestro circuito prescindiendo del transistor de salida no nos supondría mayor dificultad.

    Veamos un esquema de este tipo de conexionado.

    Lec007 007

    Hemos utilizado como optoacoplador, el componente CNY70 incluido en Proteus como un modelo activo puesto que en los objetivos de este curso habíamos fijado la intención de poder simular todos los circuitos que utilizamos utilizando Proteus. De todas formas, al lector no le resultará complicado trasladar este esquema para adaptarlo y utilizar cualquier otro optoacoplador disponible en el mercado.

    Alimentamos la entrada del optoacoplador desde la salida del Arduino y cerramos el circuito con un terminal de tierra. Utilizamos la salida del optoacoplador para la alimentación del circuito de potencia con un esquema similar al que vimos en la primera de las opciones estudiadas. La diferencia entre aquella y esta, radica que el acoplamiento entre los dos circuitos se realiza en esta segunda ocasión mediante la utilización de un dispositivo óptico (el optoacoplador) que nos garantiza un alto nivel de aislamiento (del orden de los 2,5kV) entre ambos circuitos.

    Con esto terminamos el repaso a las diferentes soluciones disponibles para implementar salidas de potencia en nuestros equipos arduino.

     

  • Lección 8: Entradas digitales con vigilancia de cable.

    [Proteus versión 8.1]

    En lecciones anteriores hemos visto el uso de las entradas digitales y de las entradas analógicas. Vamos en esta lección a mezclar ambos conceptos para lograr utilizar en nuestro equipo Arduino entradas digitales con vigilancia de cable. Para entender que queremos decir, vamos a poner un ejemplo en el que sea necesario vigilar la correción del cableado en una entrada digital.

    Supongamos que nuestro equipo Arduino lo utilizamos para controlar algún tipo de máquina móvil, por ejemplo la apertura y cierre de la puerta de un garaje. Cualquier máquina móvil encierra un cierto riesgo potencial para las personas que la utilizan. Siguiendo con nuestro caso, la puerta del garaje puede cerrarse cuando alguien está en el medio y pillarle. Para prevenir esta posibilidad, dotamos a nuestro sistema de control de un pulsador de parada, que detiene el movimiento de la puerta en cuanto se pulsa. El pulsador de parada lo hemos diseñado utilizando uno de sus contactos auxiliares como entrada digital de nuestro equipo Arduino. Por lo tanto tendríamos este esquema de montaje.

    Lec08 001

    Así, cuando nuestro amigo ve que la puerta que se está cerrando puede pillarnos, pulsará sobre el botón de parada, activará una de las entradas digitales de nuestro equipo Arduino y éste se encargará de detenerla evitando el accidente y todos estaríamos muy satisfechos y orgullos de nuestro trabajo.

    Lec08 002

    Pero, ¿qué ocurre si el cable que une el pulsador de parada con nuestro equipo Arduino se corta. Pues que cuando nuestro amigo vea el peligro y pulse sobre el pulsador de parada el equipo Arduino no recibirá la señal (porque el cable está roto) y no detendrá la puerta y nosotros acabaremos en el Hospital.

    Lec08 003

    Para evitar este riesgo, nuestra primera solucion consiste en utilizar un contacto normalmente cerrado. De esta forma nuestro equipo Arduino necesita recibir una señal activa (un uno) en la entrada digital donde conectamos el pulsador de parada para permitir el funcionamiento de la máquina. Al actuar sobre el pulsador de parada interrumpimos el circuito, impedimos la llegada de la señal y el equipo ordenará detenerse a la puerta. Si el cable se corta, la señal del pulsador de parada tampoco llega y, por lo tanto, en caso de ruptura del cable, la máquina se para.

    Lec08 004

    Sin embargo los riesgos aún contínuan. Los problemas con los cables no siempre están motivados por su ruptura. En ocasiones porque los cables están pillados o machacados por alguna máquina, o porque existen humedades y condensaciones en ciertas cajas de conexión, lo que se produce es el cortocircuito entre los dos hilos.

    Lec08 005

    En estos casos, al actuar sobre nuestro pulsador de parada, el circuito no se interrumpe y el equipo Arduino no es capaz de ver la orden y, lógicamente, no ordena la parada de la puerta.

    Así pues, es necesario buscar un sistema que nos asegure que en todos los casos conocemos la situación real de un pulsador y que diferencie entre pulsador abierto, cerrado, cable roto y cable cortocircuitado. Para ello, y si recordáis nuestras primeras lecciones, las entradas digitales no nos pueden ayudar. Una entrada digital sólo distingue entre dos estados posibles y aquí tenemos que distinguir entre cuatro. Por eso, las entradas analógicas deben venir en nuestra ayuda.

    Para implementar nuestra solución nos vamos a basar en el esquema siguiente:

    Lec08 008

    Básicamente, hemos utilizado dos entradas digitales IO2 e IO3 para manejar dos leds. De esta manera uno de ellos (D1) lo utilizaremos para mostrar la salida con la que controlamos nuestra puerta (luce cuando ordenamos detenerse a la puerta) y el otro (D2) lo utilizaremos para indicar que tenemos un problema en el cable (luce cuando el cable está mal y se apaga cuando el cable está en condiciones correctas).

    También hemos conectado un terminal virtual para visualizar el valor de la entrada analógica.

    Por último hemos hecho el siguiente esquema para controlar nuestro pulsador de parada. Mostramos esta parte ampliada para que se vea con más detalles y la comentamos.

    Lec08 009

    Desde nuestro equipo Arduino salen los +5V hacia el campo. El cable se prolonga hasta el contacto situado en la parada de emergencia. Allí hemos hecho el siguiente montaje. En serie con el contacto colocamos una resistencia de 120 Ohms y en paralelo con el mismo otra de 220 Ohms. El cable retorna y lo conectamos a nuestra entrada analógica AD0. Además a la entrada (dentro de nuestro equipo) conectamos en ese mismo punto una resistencia de 220 Ohms a tierra. Es decir, que lo que estamos haciendo es un simple divisor de tensión que varía según que el pulsador esté actuado o no.

    Lec08 010

    Lec08 011

    Además hemos añadido, con caracter pedagógico, cuatro contactos para ayudarnos a simular los posibles fallos que se pueden producir en nuestro cable. Una derivación a tierra del hilo conectado a +5V, una derivación a tierra del hilo conectado a la entrada analógica, un cortocircuito entre ambos hilos y la ruptura de uno de los hilos. De esta forma es sencillo comprobar todos los casos posibles y ver como reacciona nuestro circuito.

    El programa que corre en nuestro Arduino es muy sencillo.

    Lec08 012

     

    Primero definimos una variable que almacena la lectura analógica. Luego definimos los pines 2 y 3 como salidas y la velocidad de nuestro puerto serie a 9600 baudios. El bucle principal del programa lee el valor de la entrada analógica, lo envía por el puerto serie (hemos puesto un retardo de 0,1 segundos para no volvernos locos con tantas medidas) y luego evaluamos el valor leído. Si es menor de 350 o mayor de 700 generamos un error y activamos la salida del led D2. Si está comprendida entre 350 y 500 el pulsador está sin cerrar. Si, por último está comprendido entre 500 y 700 el pulsador está cerrado y activamos nuestra salida del led D1 (la orden de parada de la puerta).

    La ejecución de este ejemplo y el estudio detallado de los valores obtenidos en cada posible situación se ven sencillamente utilizando el terminal virtual. El circuito con el interruptor cerrado se muestra a continuación. El valor leído es de 663:

    Lec08 013

    El circuito con el cable cortocircuitado genera una lectura de 1023:

    Lec08 014

    Por último queremos destacar el caso de que el hilo que lleva los +5V se derive a tierra. Observe que Proteus nos permite seguir la simulación. En realidad, en la vida real estaríamos cortocircuitando la fuente de +5V que alimenta nuestro Arduino y el equipo se apagaría. Por eso es necesario siempre proteger y separar la alimentación de las entradas que salen al campo, pero eso lo dejamos para otro momento. Por ahora, a los efectos de lo que nos ocupa en esta leccion nos sirve este esquema.

    Lec08 015

    De esta manera terminamos con la utilización de las entradas analógicas para implementar entradas digitales con vigilancia de cable.

     

  • Lección 9: Ampliando Arduino hasta el infinito y más allá

    [Proteus versión 8.1]

    Hasta ahora hemos visto en estas lecciones las capacidades de nuestro equipo Arduino para conectarse con el mundo exterior. Entradas digitales, entradas analógicas, salidas digitales, salidas analógicas (con la matización de que las implementamos utilizando la técnica de PWM) y comunicaciones vía serie. Dependiendo de la placa de Arduino que estemos utilizando para llevar a cabo nuestro proyecto, el microprocesador ATMEGA que incorpore tendrá más o menos pines utilizados para una u otra de estas funciones. Podemos consultar el esquema de todos los pines de nuestro microprocesador ATMEGA en el enlace de la sección documentos de nuestra web siguiente: http://huborarduino.com/index.php/herramientas/documentos/23-diagramas-de-pines-de-los-arduino.

    En muchas ocasiones las necesidades de nuestro proyecto se podrán satisfacer con las posibilidades que nos brinda nuestro microprocesador. Pero no es raro (desgraciadamente con más frecuencia de lo que cabría esperar en un principio) que nos encontremos en situaciones donde el microprocesador se nos quede corto. En estos casos podemos optar por intentar utilizar otro microprocesador con más pines (aunque, ojo, nunca hay un microprocesador suficientemente grande que sirva para todo) u optar por la solución del tipo que vamos a analizar en esta y las siguientes lecciones de nuestro curso: expandir las capacidades de nuestro microprocesador utilizando periféricos con una función específica.

    Esta opción que presentamos es muy habitual en todos los sistemas de automatización (los conocidos con el acrónimo de PLC) presentes en el mercado: Siemens, Omron, AEG, Schneider, Allen Bradley, Pep, etc. Basicamente consiste en tener una tarjeta principal (CPU) donde se aloja nuestro controlador del sistema y unir por un bus de datos una serie de tarjetas auxiliares de funciones definidas. Cada una de estas tarjetas contiene la electrónica (y los microprocesadores necesarios para ejecutar la lógica de funcionamiento de la tarjeta de ampliación y las comunicaciones con la CPU) específicos para llevar a cabo la tarea que se le encomienda.

    Lec09 001

    Vamos a conectar nuestro equipo Arduino con los 'equipos periféricos' utilizando un sistema de comunicaciones del tipo bus serie. En el mercado hay distintas opciones para desempeñar estos trabajos. Nosotros vamos a utilizar una especificación concreta: el bus i2c. Hacemos esta selección porque es un bus que utilizan muchos dispositivos por lo que contaremos con un amplio abanico de soluciones específicas. Además, porque su funcionamiento e implantación, tanto de hardware como de software, es sencilla y económica. Y, finalmente, porque existen librerías estándar en Arduino que nos ayudan en esta tarea.

    Lec09 002

    Para poder llevar a cabo nuestro trabajo, tenemos que ir recorriendo un camino compuesto de varias etapas intermedias que nos obligará a familiarizarnos con conceptos nuevos: uso de librerías en Arduino, utilización de un bus de comunicaciones serie, conocimiento del estándar i2c, exploración de los dispositivos disponibles y de sus características específicas, etc. Lógicamente según nuestro conocimiento y experiencia del mundo del control industrial, este camino puede ser muy rápido o un poco más largo. 

    Para darnos una idea de la enorme variedad de dispositivos i2c disponibles, podemos utilizar a modo de muestra los dos enlaces siguientes:

    Dispositivos i2c de Philips Semiconductor

    Dispositivos i2c de Texas Instruments

    Podemos disfrutar comprobando que existen dispositivos para aumentar el número de las entradas y salidas digitales, ampliar las entradas y salidas analógicas, acceder a nuevos sistemas de comunicaciones serie, expandir el alcance del bus, controlar teclados, controlar leds, ampliar la memoria, medir la temperatura, medir la corriente y la tensión y un largo etc. más. No nos tenemos que dejar llevar por la euforia de las posibilidades que se nos abren, ni caer en el pesimismo pensando que el camino a recorrer sea técnicamente demasiado complicado. Vamos a recorrer todo el trayecto en diferentes fases, de una en una para comprenderlas bien, dominarlas y llegar a buen puerto. Como este curso siempre se ha marcado como objetivo ir dirigido a quien no sabe (nadie estudia un curso de algo que ya conoce) no tenemos más remedio que plantearnos abordar este tema desde el principio, paso a paso y dividiéndo nuestra tarea entre varias lecciones.

    En ésta que nos ocupa ahora, sólo vamos a esbozar las posibilidades del sistema propuesto. Utilizaremos el siguiente esquema electrónico.

    Lec09 003

    En primer lugar, como es lógico, está nuestro equipo Arduino. A continuación, preparamos el bus (en nuestro caso, el i2c, se compone de dos hilos). Por ahora nos sirve con considerar el bus como dos hilos que unen todos nuestros dispositivos i2c. Uno de estos hilos lo llamaremos SCL y el otro SDA. Estos dos hilos están conectados ambos a +5V por medio de una resistencia de 4k7 Ohms. La solución propuesta por nuestro estánar i2c sería como sigue:

    Lec09 005

    En nuestro esquema de prueba, lo llevamos a cabo utilizando este montaje:

    Lec09 004

    Conectamos a este bus, nuesto Arduino (pines AD4 y AD5) utilizando la técnica de los terminales de la siguiente manera:

    Lec09 006

    Para nuestro primer ejemplo, vamos a utilizar como equipo periférico un chip de Phillips llamado PCF8574 que nos permite utilizar desde nuestro Arduino a través del bus i2c ocho salidas digitales. Como es lógico, para funcionar, tiene que estar conectado al bus. Además, en este caso, lo vamos a utilizar para controlar con sus ocho salidas digitales ocho leds. El montaje se recoje en la siguiente imagen:

    Lec09 007

    Además hemos incluido el analizador del bus i2c incorporado en Proteus que nos permite 'espiar' todos los datos que circulan por nuestro bus. Es muy pronto para entender su utilidad, pero queremos presentarlo en esta primera aproximación, para que tengamos presente su existencia y que sepamos que contamos con potentes herramientas de depuración que nos ayudarán a solucionar los fallos que puedan surgir en la utilización de los periféricos i2c.

    Lec09 008

    El software que corre en nuestro Arduino, no puede ser más sencillo. Todo el código se recoge en el siguiente listado:

    Lec09 009

    Con muy poco esfuerzo estamos implementando nuestro módulo de expansión de ocho salidas digitales. Como ya dijimos, en próximas lecciones, destriparemos todo este código para entenderlo perfectamente y dominarlo. Pero, por ahora sólo vamos a limitarnos a activar la simulación y ver como las diferentes salidas digitales van encendiéndose y apagándose mostrando un contador descendente (un byte (desde 1111 1111 en formato binario hasta 0000 0000).

    Lec09 010

    En la zona inferior derecha se puede observar la ventana de nuestro depurador del bus i2c donde van apareciendo toda la actividad que se produce en el bus. Tampoco en este momento sabemos interpretar los datos que nos muestra. Pero no tardaremos en aprender a utilizarlo y comprobar su utilidad.

    Esperamos haber despertado vuestro interés y que animados por las posibilidades que nos abre el bus i2c nos acompañéis en las siguientes lecciones que dedicaremos a desgranar todo este trabajo.

  • Shields001: 4 salidas de relé

    [Proteus versión 8.2 sp1]

    En esta entrada queremos mostrar un ejemplo sencillo de utilización de un shield del tipo salida de cuatro relés. El shield está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino. En concreto este shield se suministra como un recorte de proyecto con el nombre: Arduino 4 Channel Relay Shield.pdsclip.

    Los conocimientos básicos utilizados en este proyecto se han explicado todos con sumo detalle en nuestro curso de programación de Arduino utilizando Proteus (http://huborarduino.com/programacion/curso-programacion).

    La información completa sobre la librería Hubor.cpp utilizada en este proyecto se puede encontrar en http://huborarduino.com/programacion/curso-programacion/44-leccion22

    En el siguiente enlace podemos encontrar información de una tarjeta de este tipo disponible en el mercado: http://www.seeedstudio.com/wiki/Relay_Shield_V2.0

    El montaje de ejemplo utilizado se muestra en la siguiente imagen:

    CursoShields001 001

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace: https://app.box.com/s/zbr65zk9kcjl3rak0eig

    Esperamos, como siempre, que le resulte útil al lector.

     

  • Shields002: Display 7 segmentos ánodo común

    [Proteus versión 8.2 sp1]

    En esta entrada queremos mostrar un ejemplo sencillo de utilización de un shield del tipo display de 7 segmentos de ánodo común. El shield está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino. En concreto este shield se suministra como un recorte de proyecto con el nombre: Arduino 7-segment Common Anode Breakout Board.pdsclip

    Los conocimientos básicos utilizados en este proyecto se han explicado todos con sumo detalle en nuestro curso de programación de Arduino utilizando Proteus (http://huborarduino.com/programacion/curso-programacion/42-leccion21).

    La información completa sobre la librería Hubor.cpp utilizada en este proyecto se puede encontrar en http://huborarduino.com/programacion/curso-programacion/44-leccion22

    En el mercado no hemos encontrado ningún shield propiamente dicho para arduino con un sólo display de 7 segmentos de ánodo común. Pero es fácil encontrar displays de 7 segmentos de ánodo común en el mercado.

    El montaje de ejemplo utilizado se muestra en la siguiente imagen:

    CursoShields002 001

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace: https://app.box.com/s/dktykwr2ay5z0ys7uerh

    Esperamos, como siempre, que le resulte útil al lector.

     

  • Shields003: Matriz de leds 8x8

    [Proteus versión 8.2 sp2]

    En esta entrada queremos mostrar un ejemplo sencillo de utilización de un shield del tipo matriz de leds de 8x8. El shield está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino. En concreto este shield se suministra como un recorte de proyecto con el nombre: Arduino 8x8 LEDs.pdsclip

    La información completa sobre la librería Hubor.cpp utilizada en este proyecto se puede encontrar en http://huborarduino.com/programacion/curso-programacion/44-leccion22

    En el mercado no hemos encontrado ningún shield propiamente dicho para arduino con una única matriz de leds de 8x8, porque los existentes en el mercado suelen incluir algún tipo de controlador tipo MAX7219 (http://www.ebay.com/itm/MAX7219-Dot-Matrix-Module-MCU-Control-Display-Module-DIY-Kit-for-Arduino-/221443810162). Pero es fácil encontrar matrices de leds de 8x8 en el mercado.

    El montaje de ejemplo utilizado se muestra en la siguiente imagen:

    CursoShields003 001

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace:

    https://app.box.com/s/obfqvopn2xhx7moi5y612np6phq3577w

    Esperamos, como siempre, que le resulte útil al lector.

     

  • Shields004: Controlador de 16 servos (Adafruit)

    [Proteus versión 8.2 sp2]

    En esta entrada queremos mostrar un ejemplo sencillo de utilización de un shield del controlador de servomotores mediante PWM de 16 canales. El shield está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino. En concreto este shield se suministra como un recorte de proyecto con el nombre: Arduino 16-channel PWM Servo Adafruit Shield.pdsclip.

    Además, aprovechamos y tenemos también un ejemplo de utilización del shield con un terminal virtual. Su nombre como recorte de proyecto es Arduino Terminal.pdsclip y también está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino.

    Este shield está disponible desde la web de Adafruit en la siguiente dirección: https://learn.adafruit.com/adafruit-16-channel-pwm-slash-servo-shield?view=all#

    La librería de Adafruit que se utiliza en este proyecto se puede descargar desde el siguiente enlace: https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library. Los ficheros de esta librería deben guardarse en una carpeta llamada Adafruit_PWMServoDriver en la carpeta donde se encuentran todas las librerías de Arduino en nuestro equipo (en nuestro ejemplo se encuentran en: C:\Program Files (x86)\Arduino\libraries). Después de instalar todos los ficheros de la librería en la carpeta correspondiente es posible que sea necesario dentro de Proteus ejecutar la opción de menú Construir -> Reconstruir el proyecto de la pestaña código fuente.

    La información completa sobre la librería Hubor.cpp utilizada en este proyecto se puede encontrar en http://huborarduino.com/programacion/curso-programacion/44-leccion22

    El montaje de ejemplo utilizado se muestra en la siguiente imagen:

    CursoShields004 001

    El proyecto gobierna 16 servomotores. Por el terminal se muestra el número del servomotor que se está gobernando en cada momento. Cada servomotor se gira por orden desde -90 a +90 grados y se vuelve a dejar en la posición inicial. La tarjeta shield se direcciona con la posición 0x40 por hardware utilizando los pines A0 a A5 de la placa shields y por software.

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace:

    https://app.box.com/s/856oqll3rehx6kggh3mmemlyy1tw05po

    Esperamos, como siempre, que le resulte útil al lector.

     

  • Shields005: Conversor serie (bus SPI) a Paralelo 74HC595

    [Proteus versión 8.2 sp2]

    En esta entrada queremos mostrar un ejemplo sencillo de utilización de un shield para utilizar el convertidor serie (bus SPI) a paralelo 74HC595. El shield está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino. En concreto este shield se suministra como un recorte de proyecto con el nombre: Arduino 74HC595 Shift Register Breakout Board.pdsclip

    Además, aprovechamos y tenemos también un ejemplo de utilización del shield que nos permite implementar una barra de leds. Su nombre como recorte de proyecto es Arduino Arduino LED Bar Breakout Board.pdsclip y también está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino.

    Por último, completamos el proyecto utilizando el instrumento virtual de Proteus depurador SPI, que nos permite analizar todas las comunicaciones establecidas sobre el bus serie SPI.

    Este shield está disponible desde la web de SparkFun en la siguiente dirección: https://www.sparkfun.com/products/10680

    Podemos encontrar una información muy interesante sobre el funcionamiento del conversor 74HC595 en la siguiente dirección: http://electrocirc.blogspot.com.es/p/simulacion-de.html. Y también podemos encontrar información del uso de este conversor con Arduino en este otro enlace (esta información está en inglés): http://arduino.cc/en/pmwiki.php?n=tutorial/ShiftOut. En este segundo enlace podemos encontrar ejemplos de utilización de varios 74HC595 al mismo tiempo, que pueden también simularse con Proteus.

    La información completa sobre la librería Hubor.cpp utilizada en este proyecto se puede encontrar en http://huborarduino.com/programacion/curso-programacion/44-leccion22

    El montaje de ejemplo utilizado se muestra en la siguiente imagen:

    CursoShields005 001

    El proyecto gobierna los ocho primeros leds de una barra de 10 leds, enviando una secuencia de números del 1 al 255 y mostrando su valor binario con ayuda de los leds. En el instrumento virtual depurador SPI podemos comprobar todas la información que se transmite a través del bus serie.

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace:

    https://app.box.com/s/wscp6jdic1axcg1xessuhcp848nsyff0

    Una vez más, esperamos que este ejemplo sirva de guía al lector para iniciarse en la utilización de este tipo de shield.

     

  • Shields006: Conversor de 16 bits serie (bus SPI) a Paralelo con dos 74HC595

    [Proteus versión 8.2 sp2]

    En nuestra entrada del proyecto Shields005 comentábamos que era posible utilizar más de un 74HC595 para aumentar a más de ocho las salidas disponibles. Por ejemplo tener 16 salidas utilizando 2 shields de este tipo. Durante este tiempo, son varios los lectores que nos han pedido que escribamos un ejemplo de cómo se hace. Así que esta entrega trata precisamente de esta cuestión: cómo utilizar dos shields idénticos con un conversor 74HC595 cada uno de ellos para obtener un conversor serie paralelo de 16 bits.

    La información básica disponible en internet sobre el uso de un shield de este tipo debe consultarse en nuestro proyecto anterior: http://huborarduino.com/proyectos/52-shields005

    En este proyecto utilizamos los dos shields para mandar un número entero (16 bits) a los dos conversores. Por lo tanto podemos gobernar 16 leds colocados en sus salidas. La salida Q7 del primer 74HC595 se une con el pin DS del segundo. De esta forma al ir desplazándose los bits de uno al otro, conseguimos nuestro propósito de gobernar 16 salidas.

    Hemos numerado las salidas del primer 74HC595 del 0 al 7 (los primeros 8 bits) y las del segundo del 8 al 15 (los úlitmos 8 bits. Así, logramos de forma sencilla saber que bit de nuestro número entero ilumina que led. La salida 0 muestra el bit menos significativo y la salida 15 el de más peso.

    Para facilitar la compresión del momento concreto en que se encuentra la ejecución de nuestro programa, utilizamos el shield del terminal virtual y le mandamos un mensaje en cada iteración de nuestro bloque con el valor del contador en decimal y en hexadecimal.

    Como empezar con el contador a cero significaría tener que esperar mucho tiempo (un poco más de dos minutos) hasta que entra en juego el segundo 74HC595, hemos inicializado la variable usada como contador -Contador.numInt[0]- con un valor de 4090 en decimal (0x0FFB) en la línea 40 de nuestro código. De esta manera, ya se ve desde el principio el funcionamiento en las dos barras de leds. Por supuesto que el lector podrá de forma muy rápida y sencilla empezar con el contador al valor que desee sólo cambiando esta línea.

    Puesto que la función que utilizamos para enviar la información al 74HC595 sólo envía un byte de cada vez y nuestra variable es entera (dos bytes, para poder enviar los dos bytes hemos tenido que utizar dos veces seguidas la función shiftOut(). La primera vez mandamos el byte más significativo y la segunda el de menos peso.

    shiftOut(dataPin, clockPin, MSBFIRST, Contador.numByte[1]);
    shiftOut(dataPin, clockPin, MSBFIRST, Contador.numByte[0]);

    Para facilitar el envío del byte alto y bajo de nuestra variable, hemos utilizado una "union". Para más información sobre este uso de las uniones del lenguaje C se puede consultar nuestra lección 20 del curso de programación de Arduino: http://huborarduino.com/programacion/curso-programacion/41-leccion20

    El montaje de ejemplo utilizado se muestra en la siguiente imagen:

    CursoShields006 001

    El proyecto gobierna los dieciseis primeros leds de dos barras de 10 leds, enviando una secuencia de números del 4090 al 65.535 y mostrando su valor binario con ayuda de los leds. En el instrumento virtual depurador SPI podemos comprobar todas la información que se transmite a través del bus serie.

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace:

    https://app.box.com/s/g2m386xjah4sj2rkyu6cwiamsb8i7c8x

    Una vez más, esperamos que este ejemplo sirva de guía al lector para profundizar en la utilización de este tipo de shield.

     

  • Shields007: Control de temperatura con sonda termopar

    [Proteus versión 8.2 sp2]

    En esta entrada queremos mostrar un ejemplo sencillo de utilización de un shield para utilizar una sonda termopar del tipo K con ayuda del amplificador operacional AD8495  El shield está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino. En concreto este shield se suministra como un recorte de proyecto con el nombre: Arduino AD8495 K-Type Thermocouple Amplifier Breakout Board.pdsclip

    Para poder probar el funcionamiento del shield, tenemos que utilizar una sonda de temperatura (el termopar del tipo K). Podemos encontrar información del funcionamiento de estas sondas de temperatura en la wikipedia: http://es.wikipedia.org/wiki/Termopar.

    Proteus nos proporciona en sus librerías muchos dispositivo diferentes para simular sondas de temperatura. En todos estos modelos podemos simular la temperatura que está leyendo la sonda y comprobar el funcionamiento de nuestro equipo a dichos cambios.

    shield007 001

     

    Este shield está disponible en diferentes webs para comprarlo. En la web de Adafruit se puede encontrar en el siguiente enlace: http://www.adafruit.com/products/177

    Podemos encontrar la información completa sobre el funcionamiento del amplificador AD8495 en la siguiente dirección: http://www.analog.com/static/imported-files/data_sheets/AD8494_8495_8496_8497.pdf

    La información completa sobre la librería Hubor.cpp utilizada en este proyecto se puede encontrar en http://huborarduino.com/programacion/curso-programacion/44-leccion22

    El montaje de ejemplo utilizado se muestra en la siguiente imagen:

    CursoShields007 001

    El proyecto utiliza el shield para controlar la lectura de la sonda termopar y vuelca por el terminal los valores leídos (unidades del conversor analógico digital, tensión, temperatura en grados centrígados y temperatura en grados fahrenheit.

    CursoShields007 002

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace:

    https://app.box.com/s/ptt1y16n5967di6cjbrlp2pho2e0ybuf

    Una vez más, esperamos que este ejemplo sirva de guía al lector para profundizar en la utilización de este tipo de shield dentro de sus proyectos con Arduino.

     

  • Shields008: Utilización de una tarjeta SD

    [Proteus versión 8.2 sp2]

    En esta entrada queremos compartir con vosotros un proyecto que utiliza un shield para leer y escribir datos en una tarjeta del tipo SD o MicroSD. De esta forma, podemos incluir en nuestros proyectos un histórico de nuestros procesos para su posterior análisis. El shield está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino. En concreto este shield se suministra como un recorte de proyecto con el nombre: Arduino SD Card Breakout Board.pdsclip.

    Para poder probar el funcionamiento del shield, vamos a escribir en la tarjeta los datos leídos a través de una entrada digital. Para simular los valores, utilizamos un potenciómetro conectado a dicha entrada con el que simulamos los valores de un lector de nivel de un pozo de agua. La conversión de las lecturas analógicas se han calculado para que las lecturas expresen valores entre 0 y 5mts de altura del nivel del agua.

    Este shield está disponible en diferentes webs para comprarlo. En la web de Adafruit se puede encontrar en el siguiente enlace: http://www.adafruit.com/products/1141 . También es sencillo encontrar varios shields similares en otras webs a precios muy reducidos: http://www.dx.com/p/spi-microsd-card-adapter-v0-9b-for-arduino-works-with-official-arduino-board-246784#.VSF1VF2sUpR.

    Para utilizar el lector de tarjetas SD con comodidad, nos ayudaremos de la librería SD.h. Podemos encontrar toda la información en la web oficial de Arduino: http://arduino.cc/en/Reference/SD.

    La información completa sobre la librería Hubor.cpp utilizada en este proyecto se puede encontrar en http://huborarduino.com/programacion/curso-programacion/44-leccion22 En este caso la utilizamos para usar las funciones de tiempo y realizar las escrituras en la tarjeta a intervalos de un segundo.

    El montaje de ejemplo utilizado para este proyecto se muestra en la siguiente imagen:

    CursoShields008 001

    El proyecto utiliza el shield para controlar la lectura de la entrada analógica y almacenar los datos en un fichero guardado en la tarjeta SD. La primera tarea consiste en inicializar la tarjeta SD para asegurarnos que la tarjeta está accesible. Para comprobar el funcionamiento en la simulación, podemos utilizar los botones al lado del modelo de lector de tarjetas SD para expulsarla y comprobar que funciona bien esta parte del programa. La tarjeta aparece más fuera o más dentro del lector según esté introducida o expulsada.

    CursoShields008 002

    A continuación, comprobamos si existe en la tarjeta SD un fichero llamado FICH001.TXT.Si existe lo borramos y si no existe lo creamos nuevo en el directorio raíz de la tarjeta.

    Una vez hecho esto, realizamos diez lecturas analógicas (una cada segundo) y las almacenamos en la tarjeta SD. Al grabar las diez lecturas, cerramos el fichero y ya dejamos el programa sin más trabajo.

    También utilizamos el terminal para ayudar al lector a saber en qué fase del trabajo nos encontramos mediante mensajes informativos.

    CursoShields008 003

    El modelo del lector de tarjetas SD utiliza un fichero para simular el contenido de la tarjeta SD. En el menú contextual podemos elegir el fichero que vamos a utilizar. En el fichero con el proyecto, hemos incluido un fichero para simular una tarjeta SD de 4Mb.

    CursoShields008 004

    Al acabar la ejecución del proyecto, podemos comprobar el contenido del fichero FICH001.TXT y ver que se han apuntado correctamente las lecturas efectuadas.

    CursoShields008 005

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace:

    https://app.box.com/s/f0ev07ur1croz6zomsasliyq1csj0lie

    Una vez más, esperamos que este ejemplo sirva de guía al lector para profundizar en la utilización de este tipo de shield dentro de sus proyectos con Arduino.

     

  • Shields009: Un histórico en tarjeta SD con marcado de hora.

    [Proteus versión 8.2 sp2]

    En esta entrada queremos compartir con vosotros un proyecto derivado del anterior, pero aumentando un poco la complejidad y mejorando la funcionalidad para implementar un registro histórico con marcado de la hora en que se produce cada medida. Utilizaremos una entrada analógica para leer los datos de la altura de nivel de un pozo de agua (simularemos los valores de lectura del sensor de nivel con un potenciómetro),  un shield para leer la hora actual desde un reloj de tiempo real y escribir los datos en un fichero almacenado en una tarjeta del tipo SD o MicroSD. El shield está disponible con la versión de Proteus dedicada a la simulación de microprocesadores Arduino. En concreto este shield se suministra como un recorte de proyecto con el nombre: Arduino Data Logger Shield.pdsclip.

    Para poder probar el funcionamiento del shield, vamos a leer la hora actual y escribir en la tarjeta los datos leídos a través de una entrada digital con el marcado de hora. Para simular los valores, utilizamos un potenciómetro conectado a dicha entrada con el que simulamos los valores de un lector de nivel de un pozo de agua. La conversión de las lecturas analógicas se han calculado para que las lecturas expresen valores entre 0 y 5mts de altura del nivel del agua.

    Este shield está disponible en diferentes webs para comprarlo. En la web de Adafruit se puede encontrar en el siguiente enlace: http://www.adafruit.com/products/1141 . También es sencillo encontrar varios shields similares en otras webs a precios muy reducidos: http://www.dx.com/p/spi-microsd-card-adapter-v0-9b-for-arduino-works-with-official-arduino-board-246784#.VSF1VF2sUpR.

    Para utilizar el lector de tarjetas SD con comodidad, nos ayudaremos de la librería SD.h. Podemos encontrar toda la información en la web oficial de Arduino: http://arduino.cc/en/Reference/SD.

    Para leer los valores desde el reloj de tiempo real (RTC) utilizaremos la librería RTCLib.h. Podemos encontar toda la información en el siguiente enlace: https://github.com/adafruit/RTClib.

    La información completa sobre la librería Hubor.cpp utilizada en este proyecto se puede encontrar en http://huborarduino.com/programacion/curso-programacion/44-leccion22 En este caso la utilizamos para usar las funciones de tiempo y realizar las escrituras en la tarjeta a intervalos de un segundo.

    El montaje de ejemplo utilizado para este proyecto se muestra en la siguiente imagen:

    CursoShields009 001 

    La primera tarea consiste en inicializar la tarjeta SD para asegurarnos que la tarjeta está accesible. Para comprobar el funcionamiento en la simulación, podemos utilizar los botones al lado del modelo de lector de tarjetas SD para expulsarla y comprobar que funciona bien esta parte del programa. La tarjeta aparece más fuera o más dentro del lector según esté introducida o expulsada.

    CursoShields008 002

    A continuación, comprobamos si existe en la tarjeta SD un fichero llamado FICH001.TXT.Si existe lo borramos y si no existe lo creamos nuevo en el directorio raíz de la tarjeta.

    El segundo paso consiste en inicializar el reloj de tiempo real. Para ello nos ayudamos de las funciones estándar de la librería.

    Una vez hecho esto, realizamos diez lecturas analógicas (una cada segundo) y las almacenamos en la tarjeta SD con el marcado de tiempo de la hora en la que se produjo la lectura. Al grabar las diez lecturas, cerramos el fichero y ya dejamos el programa sin más trabajo.

    También utilizamos el terminal para ayudar al lector a saber en qué fase del trabajo nos encontramos mediante mensajes informativos.

    CursoShields009 002

    El modelo del lector de tarjetas SD utiliza un fichero para simular el contenido de la tarjeta SD. En el menú contextual podemos elegir el fichero que vamos a utilizar. En el fichero con el proyecto, hemos incluido un fichero para simular una tarjeta SD de 4Mb.

    CursoShields008 004

    Al acabar la ejecución del proyecto, podemos comprobar el contenido del fichero FICH001.TXT y ver que se han apuntado correctamente las lecturas efectuadas.

    CursoShields009 003

    Puesto que nuestro shield incluye dos leds (uno rojo y otro verde), hemos aprovechado y los hemos utilizado también en nuestro proyecto. Si se produce algún fallo en la inicialización de la tarjeta SD o al crear el fichero encendemos el led rojo (para probar esta funcionalidad, es suficiente con pulsar sobre la tarjeta SD y extraerla). Si todo se ha desarrollado correctamente y se han completado las diez lecturas y sus correspondientes escrituras en el histórico, encendemos el led verde.

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace:

    https://app.box.com/s/5x5vcmlco5mkd8y558zq2dqve0448f0a

    Una vez más, esperamos que este ejemplo sirva de guía al lector para profundizar en la utilización de este tipo de shield dentro de sus proyectos con Arduino.

     

  • Shields010: Sensor de humedad HYT 271

    [Proteus versión 8.3 sp2]

    En esta entrada queremos compartir con vosotros un proyecto para medir la humedad. Para ello utilizaremos el sensor de humedad HYT 271 de la firma IST (innovative sensor technology). Podemos encontrar toda la información relativa a este sensor en su página web siguiendo el siguiente enlace: http://www.hygrochip.com/index.php?id=3837&L=1

    La hoja técnica del producto se puede consultar siguiendo este enlace: http://www.hygrochip.com/fileadmin/user_upload/Produkte/Sensorelemente/Digitale_Feuchtesensoren/HYT_271/IST_PDF/EN_HYT271.pdf

    El sensor nos proporciona dos valores: la temperatura y la humedad relativa. La humedad relativa, o "RH", mide la cantidad de agua en el aire en forma de vapor, comparándolo con la cantidad máxima de agua que puede ser mantenida a una temperatura dada. Por ejemplo, si la humedad es del 50% a 23°C, esto implicaría que el aire contiene 50% del nivel máximo de vapor de agua que podría mantener a 23°C. 100% de humedad relativa, indica que el aire está en la máxima saturación.

    Proteus nos facilita un modelo llamado HYT 271 que simula el funcionamiento de este sensor

    img010 001

    El modelo incorpora unos mandos con los que podemos seleccionar si vamos a modificar el valor de la temperatura o de la humedad relativa y el valor actual de cada uno de estos sensores.

    img010 002

    Además, Proteus también nos facilita un shield llamado "Arduino HYT271 Precision Humidity & Temperature sensor Breakout Board.pdsclip". Este shield incluye el modelo de simulación del sensor HYT-271 y las resistencias necesarias para enlazar nuestro sensor con el bus i2c. De esta manera el sensor se conecta al puerto i2c de nuestro arduino utilizando los pines AD4 y AD5.

    img010 003

     

    El montaje lo completamos con un terminal virtual para poder monitorizar el funcionamiento de nuestro equipo. La siguiente imagen muestra el montaje completo.

    img010 004

     

    Funcionamiento:

     

    img010 005

    El funcionamiento del programa es muy sencillo. Cada segundo se efectúa una lectura desde el sensor HYT 271 y se vuelca en el terminal virtual. Podemos de forma muy sencilla modificar los valors de temperatura y humedad relativa utilizando los mandos del modelo.

    img010 006

    El fichero con el proyecto completo se puede descargar desde este enlace:

    https://app.box.com/s/xhk4vuekr0hoz8etx39yafy0ajdkrvr8

    Una vez más, esperamos que este ejemplo sirva de guía al lector para profundizar en la utilización de este tipo de sensores dentro de sus proyectos con Arduino.

     

  • Shields011: Ejemplo de uso de un chip con tecnología OneWire (DS18B20)

    [Proteus versión 8.3 sp2]

    Aunque en proyectos anteriores ya vimos otra forma de medir temperaturas desde nuestro equipo Arduino, nos ha parecido interesante mostrar un ejemplo sencillo de utilización de un shield que nos permite conocer la temperatura utilizando un chip DS18B20.La peculiaridad de utilizar este chip es que se enlaza con nuestro equipo Arduino utilizando la tecnología "1-Wire Protocol" de Dallas Semiconductor.

    En concreto este shield se suministra como un recorte de proyecto con el nombre: Arduino DS18B20 Breakout Board.pdsclip.

    Dallas Semiconductor fabrica una serie de componentes que se conectan todos ellos utilizando la tecnología 1-Wire. Dallas ha permitido la utilización de forma libre y gratuita de estos drivers a los diseñadores de equipos electrónicos. Básicamente, el protocolo 1-Wire es un bus serie que usa un único hilo para conectar los dispositivos, utilizando la tierra como referencia. Se puede encontrar más información del uso de este protocolo en equipos Arduino desde el siguiente enlace: http://playground.arduino.cc/Learning/OneWire.

    También está disponible una librería que nos permite utilizar este protocolo de forma muy cómoda y sencilla. Se puede descargar desde este enlace a la página web del autor Paul Stoffregen: http://www.pjrc.com/teensy/arduino_libraries/OneWire.zip. En la página también se puede encontrar información del funcionamiento y ejemplos de utilización de la librería http://www.pjrc.com/teensy/td_libs_OneWire.html. Para que este proyecto funcione correctamente es necesario que instalemos esta librería en la carpeta libraries de nuestro directorio de Arduino.

    La información del sensor DS18B20 la podemos consultar en este enlace: http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf.

    CusroShields011 001

    Este shield está disponible en diferentes webs para comprarlo. En la web de BangGood se puede encontrar en el siguiente enlace: http://www.banggood.com/DS18B20-Digital-Temperature-Sensor-Module-For-Arduino-p-76516.html

    Podemos encontrar la información completa sobre el uso del sensor DS1820 en un proyecto Arduino en la siguiente dirección: https://arduino-info.wikispaces.com/Brick-Temperature-DS18B20

    La información completa sobre la librería Hubor.cpp utilizada en este proyecto se puede encontrar en http://huborarduino.com/programacion/curso-programacion/44-leccion22

    El montaje de ejemplo utilizado se muestra en la siguiente imagen:

    CusroShields011 002

    El proyecto utiliza el shield para controlar la lectura del sensor y vuelca por el terminal los valores leídos, mostrando la temperatura en grados centrígados y temperatura en grados fahrenheit.

    CusroShields011 003

    El fichero con el proyecto completo (esquema y software) se puede descargar desde este enlace:

    https://app.box.com/s/b016w9e7tjpa1150kfqjq0olieyphd1n

    A continuación os mostramos dos fotos del proyecto en el mundo real. Simplemente hemos tenido que enviarlo desde nuestro entorno Proteus, según se explica en la guía de simulación de equipos Arduino.

    CursoShields011 004

     

    CursoShields011 005

    Una vez más, esperamos que este ejemplo sirva de guía al lector para profundizar en la utilización de este tipo de shield y de otros productos que utilizan la tecnología del protocolo 1-Wire dentro de sus proyectos con Arduino.

     


Esta página es propiedad de Hubor.

2014.

Guardar
Preferencias de usuario para las Cookies
Estamos utilizando las cookies para asegurarnos de que le podemos ofrecer el mejor servicio posible en este sitio web. Si rechaza el uso de las cookes, es posible que esta web no funcione como estaba previsto por el equipo de programadores.
Aceptar todo
Rechazar todo
Leer más
Estadísticas
Las cookies de preferencias permiten a la página web recordar información que cambia la forma en que la página se comporta o el aspecto que tiene, como su idioma preferido o la región en la que usted se encuentra.
Google Analytics
Aceptar
Rechazar