ARD14 – Display LED de siete segmentos

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

Estos displays se emplean para representación de dígitos, mediante siete LED’s en forma de línea, dispuestos en una estructura tal que permite representar, según los LED’s encendidos, diferentes números o letras. Además, hay un octavo LED dispuesto como un punto, para representación de cifras decimales. En la figura 14.1 vemos cómo son estos dispositivos.

Figura 14.1. Un display de siete segmentos

Figura 14.1. Un display de siete segmentos

El dispositivo cuenta con diez terminales distribuidos en dos filas de cinco, en la parte posterior. El terminal central de las dos filas está conectado internamente, y es el pólo común de todos los LED’s.

Figura 14.2. Patillaje de un display de siete segmentos.

Figura 14.2. Patillaje de un display de siete segmentos.

Por lo tanto, quedan ocho terminales (cuatro en cada fila) que activan cada uno de los LED’s, de modo independiente. Los LED’s y los pines se designan con una letra, de modo que cada pin corresponde a un LED. Esta nomenclatura es estándar para todos los LED’s de siete segmentos del mercado, y aparece en la figura 14.2.

 

Existen dos tipos de displays de siete segmentos: aquellos en los que el polo común es positivo (displays de ánodo común) y los LED’s se activan conectando cada terminal a masa, y aquellos en los que el polo común es negativo (cátodo común) y los LED’s se activan conectando cada terminal a una fuente de Vcc. Estos últimos son los más habituales. En la figura 14.3 vemos el esquema electrónico de ambos:

Figura 14.3. Esquema electrónico de LEDS de ánodo común y de cátodo común.

Figura 14.3. Esquema electrónico de LEDS de ánodo común y de cátodo común.

Lo que vamos a hacer es un montaje muy sencillo, con un sólo display, que nos permita visualizar los números del 0 al 9 y las letras de la A a la F, en secuencia, como si fuera un contador hexadecimal.

El motivo de usar uno sólo de estos displays es porque, dado que cada LED se gestiona de forma independiente, nuestra placa Arduino UNO no cuenta con suficientes pins para gestionar dos o más displays. En el próximo artículo volveremos sobre esta cuestión.

EL CIRCUITO

El circuito, como puedes ver en el esquema de la figura 14.4, es bastante sencillo, aunque un poco engorroso de montar. El display de siete segmentos va conectado de tal modo que el cátodo común va a masa a través de una resistencia de 1 kΩ. Esto se hace porque la tensión de alimentación que reciben los LED´s cuando se activan (5V, como sabes) podría destruirlos.

Figura 14.4. Esquema electrónico teórico del montaje.

Figura 14.4. Esquema electrónico teórico del montaje.

Además, cada LED va conectado a una reistencia de 10 KΩ, cuyo otro extremo va a masa, para evitar que, cuando los LEDS no reciben tensión, queden “al aire”.

En la figura 14.5 ves el montaje físico del circuito. Observa los pines del display, como se conectan a los pines Arduino, de modo que el pin del display que corresponde a cada segmento va a un pin concreto de la placa, siguiendo el esquema teórico, y ciñéndonos a la posición de los pines del display que vimos en la figura 14.2, de modo que sepamos siempre que segmento se va a encender al poner a HIGH cada pin de la placa Arduino.

Figura 14.5. Montaje cableado de este artículo.

Figura 14.5. Montaje cableado de este artículo.

EL SKETCH

Este sketch es muy simple conceptualmente, aunque muy engorroso de leer, pero nos va a permitir comprender como opera el sistema completo.

Lo primero que hacemos es definir los 8 pines que vamos a usar (los que son para cada uno de los siete segmentos del display, y el que es para el punto). A cada uno lo llamamos con la letra que coincide con el nombre del segmento sobre el que acturará, así:

byte a = 3;
byte b = 2;
byte c = 7;
byte d = 8;
byte e = 9;
byte f = 4;
byte g = 5;
byte punto = 6;

En la sección setup definimos todos los pines como de salida, OUTPUT. Esto es la forma habitual y no tiene más misterio.

En la sección loop, empezamos activando los pines que corresponden a los segmentos a, b, c, d, e y f, desactivando los que corresponden al segmento g y al punto. Esto hará que el display muestre el número 0. Mantenemos esta situación durante un segundo (1000 milisegundos) con la función delay() que ya conocemos.

digitalWrite(a, HIGH);
digitalWrite(b, HIGH);
digitalWrite(c, HIGH);
digitalWrite(d, HIGH);
digitalWrite(e, HIGH);
digitalWrite(f, HIGH);
digitalWrite(g, LOW);
digitalWrite(punto, LOW);
delay(1000);

Después cambiamos esta situación, activando los segmentos necesarios para visualizar el número 1, y desactivando explícitamente los demás, y así, sucesivamente. De esta forma, mostramos en secuencia los diez dígitos numéricos, así como las letras de la A a la F, y el punto.

UNA ALTERNATIVA

El sketch que hemos escrito funciona perfectamente, pero es un código muy largo y repetitivo. Una vez más, sale a escena la cuestión de la optimización de código. En este caso vamos a aprovechar para introducir un concepto muy interesante, que nos ayudará a escribir mejores sketches en el futuro. Si estás familiarizado con otros lenguajes de programación, seguro que alguna vez has empleado funciones de usuario. Veamos el listado completo en primer lugar, y luego lo desgranaremos:

Una función de usuario es un fragmento de código que se encapsula bajo un nombre definido por el autor del código. Este fragmento se carga en memoria, pero no se ejecuta hasta que no es invocado mediante el nombre bajo el que ha sido encapsulado. El formato genérico de definición de una función de usuario es el siguiente:

tipoDeRetorno nombreFuncion (arg1, arg2, ..., argN){
    // código que se almacena en memoria sin ejecutarse.
    return retorno;
}

Para definir una función de usuario, empezamos especificando el tipo de dato que va a devolver. Según la función que escribamos, y para que se pueda usar, devolverá un dato numérico, una cadena de texto, un carácter, etc, o nada. Si es así, el tipo de retorno se define como void (vacío), pero, en Arduino, siempre hay que especificar un tipo de retorno, aunque sea este último.

Dentro del cuerpo de la función (entre las llaves) escribiremos el código que se ejecutará cuando la función sea invocada. Este código puede generar un resultado que será devuelto a la sentencia que llama a la función, mediante la instrucción return. El tipo de dato que se devuelve debe ser el que se haya especificado antes del nombre de la función. Si el tipo es void (lo que indica que la función no devuelve ningún dato), no se incluirá instrucción return en el cuerpo de la función. Si hay que devolver algún dato, la instrucción return debe ser la última que incluya el código, porque, además de devolver un resultado, fuerza la finalización de la función, devolviendo el control al cuerpo principal del sketch.

Puede que para ejecutar su código, la función necesite recibir determinados parámetros. Estos se especifican entre paréntesis detrás del nombre de la función. Lo veremos más claro si analizamos nuestro código:

void mostrar (byte a, byte b, byte c, byte d, byte e, byte f, byte g, byte p) { // Función del mostrar
    digitalWrite (3, a);
    digitalWrite (2, b);
    digitalWrite (7, c);
    digitalWrite (8, d);
    digitalWrite (9, e);
    digitalWrite (4, f);
    digitalWrite (5, g);
    digitalWrite (6, p);
}

En primer lugar especificamos que el tipo de retorno es void, lo que, como hemos dicho, indica que la función no devolverá ningún valor. Después, aparece el nombre de la función. Es el nombre que hemos elegido nosotros (mostrar). Puede ser cualquiera. Entre paréntesis indicamos los argumentos que la función debe recibir, indicando el tipo de dato que es cada uno. Esta función recibe ocho argumentos. Luego, al usar la función, veremos que a cada uno de esos argumentos le damos el valor 0 o 1. Dentro de la función vemos que a cada uno de los pines de salida (que van cableados a los segmentos del display) le asignamos uno de esos argumentos. Asignarle a un pin digital un 1 es, como ya sabemos, como asignarle el valor HIGH, y un 0 es como el valor LOW.
Fíjate que estamos hablando de escribir valores en los pines preestablecidos, pero aún no hemos definido esos pines como de salida. Eso no importa, porque, como ya hemos mencionado, el código de la función se carga en memoria pero aún no se ejecuta, hasta que la función no sea invocada. En la sección setup definimos los pines adecuados como de salida, así:

void setup(){
    pinMode(2, OUTPUT);
    pinMode(3, OUTPUT);
    pinMode(4, OUTPUT);
    pinMode(5, OUTPUT);
    pinMode(6, OUTPUT);
    pinMode(7, OUTPUT);
    pinMode(8, OUTPUT);
    pinMode(9, OUTPUT);
}

Ahora ya están los pines definidos, así que ya podemos usarlos. En la sección loop lo primero que encontramos es lo siguiente:

// Mostrar 0;
mostrar (1, 1, 1, 1, 1, 1, 0, 0);
delay (1000);

Invocamos a la función, pasándole el valor 1 a los argumentos que corresponden a los segmentos que definen el número 0 en el display, y que son el a, el b, el c, el d, el e y el f. Los otros dos, el segmento g y el punto se desactivan específicamente.

Tras un retardo de un segundo, cambiamos el estado de los segmentos para que se visualice el número 1 (segmentos b y c) y continuamos, de modo que se visualicen sucesivamente los distintos números en el display.

OTRA ALTERNATIVA

Vamos a ver aquí una variante del código anterior. Esta vez no se trata de optimizar código, ni nada parecido, sino de aprovechar para aprender otra funcionalidad de la consola Serial de Arduino. Hasta ahora hemos visto ejemplos de montajes y sketches que mandaban datos de la placa al ordenador, y estos aparecían en la consola. Ahora vamos a ver como podemos usar la consola para introducir datos, y que el ordenador los envíe a la placa Arduino, para que esta los procese según le hayamos programado. El sketch completo es el siguiente:

En la primera sección definimos una variable llamada mensaje, de tipo char. En ella se almacenará cada carácter que enviemos desde la consola.

char mensaje = 0;

En la sección setup abrimos la comunicación con la consola serie, de la forma habitual:

Serial.begin(9600);

Dentro de la sección loop, comprobamos si hay algún dato disponible que proceda de la consola Serial. Lo hacemos con el método Serial.available(). Este método devuelve 0 si no hay datos disponibles. En caso contrario, devuelve los datos que haya disponibles. Los datos pueden ser un solo carácter, o una cadena. Si hay algún dato disponible, lo asignamos a la variable mensaje, mediante el método Serial.read(), que lee, precisamente, lo que hay disponible en la consola.

Según el carácter que hayamos tecleado, se mostrará en el display (ten en cuenta que las letras debes escribirlas en mayúsculas, que es como las comprueba el condicional).

Cuando cargues el sketch abre la consola con el botón correspondiente del IDE que, como sabes, es el siguiente:Botón que activa el monitor serie.

Recuerda que se encuentra en la parte superior derecha del IDE. Ahora observa el detalle de la parte superior de la consola, que aparece en la figura 14.6.

14.6. La consola serie.

14.6. La consola serie.

En la parte inferior está la zona de texto grande donde se muestran los datos que proceden de la placa Arduino, como ya sabemos. Sin embargo, lo que ahora nos interesa es la pequeña caja de texto de la parte superior. Posicionamos ahí el cursor. Escribimos, por ejemplo, el número 4 y pulsamos la tecla ENTER (o hacemos clic en el botón Enviar que aparece a la derecha). Veremos que, inmediatamente, aparece el 4 en el display de siete segmentos. Así vemos que la consola Serial puede programarse para funcionar en las dos direcciones.

LA CONSOLA Serial

Como ya sabemos, Serial es el objeto que provee el lenguaje Arduino para comunicarse con otros dispositivos. Normalmente se comunica con la consola que el IDE incluye al efecto, a través del cable USB, aunque la comunicación puede ser con otros dispositivos diferentes. El caso es tener un canal de comunicación para intercambio de datos. De hecho, sabemos que hay dos pines digitales (el 0 y el 1) de la placa Arduino que, cuando se emplea en el sketch el objeto Serial, se convierten en receptor y transmisor, respectivamente, de datos en serie. Esta es la razón por la que en nuestros montajes tratamos de evitar usarlos.

Serial nos proporciona varios métodos para la gestión de la comunicación. Hasta ahora hemos estado usando algunos de ellos. Vamos a comentarlos, para que sirva como referencia en el futuro.

Un caso especial lo supone la tarjeta Arduino Mega. Esta cuenta con seis pines digitales que pueden usarse para comunicación serie, y permite definir, en el sketch, hasta tres objetos Serial diferenciados. En los sketches para Arduino Mega, estos objetos se llaman Serial1, Serial2 y Serial3. Si nuestro sketch ha sido desarrollado en una de las versiones de Arduino que sólo tiene un canal de comunicaciones serie (Arduino UNO, Duemilanove, etc) y lo corremos sobre una placa Arduino Mega, deberemos sustituir las referencias a Serial, por Serial1, Serial2 o Serial3, según los pines digitales que queramos emplear en las transmisiones serie. En el caso de la placa Arduino Leonardo, aunque sólo tiene un par de pines para transmisiones serie, el objeto se llama Serial1, en lugar de Serial.

El método begin(). Como ya sabemos, inicializa la comunicación serie entre la tarjeta Arduino y otro dispositivo. Recibe, como argumento, la velocidad de transmisión de datos en baudios (bits por segundo). Cuando la comunicación se va a hacer a través del cable USB con el ordenador, las velocidades estándar son 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, o 115200. Recuerda que, en este caso, en tu consola Serial tendrás que establecer la misma velocidad que hayas especificado en tu sketch. Si la comunicación se va a realizar a través de los pines digitales (o del cable USB), con un dispositivo específico que no sea el ordenador, la velocidad que especifiquemos en el sketch al usar este método será acorde a la que necesite el dispositivo concreto con el que vayamos a comunicar. Por ejemplo, una pantalla de datos TFT podría necesitar una velocidad diferente a las que hemos listado.

Además, el método begin() puede recibir un segundo parámetro opcional que indica la estructura de los paquetes enviados o recibidos en serie, como modo de comprobación de la transmisión. Este segundo parámetro es una constante prefijada de Arduino, formada por la palabra SERIAL (en mayúsculas), un guion bajo, y una secuencia formada por un número, una letra, y otro número. El primer número indica cuantos bits de datos hay en cada paquete; la letra indica si la paridad es par (E), impar (O) o no hay paridad(N); el último número indica los bits de parada. Por ejemplo, el segundo parámetro del método begin() podría ser SERIAL_8N1, lo que indicaría que estamos tratando con paquetes de 8 bits, sin paridad, y con un bit de parada. De hecho, este es el valor que se asume por defecto, cuando no especificamos el segundo parámetro.

El funcionamiento “íntimo” de las transmisiones serie, que incluye, entre otras cosas, la explicación de los conceptos bits de datos, paridad y bits de parada, excede las pretensiones de estos artículos. Sin embargo, en Internet puede localizarse información detallada, si tienes interés en profundizar más en este tema. Puedes poner en tu buscador favorito transmisión de datos en serie, lo que te dará acceso a gran cantidad de información relevante al respecto.

Si el dispositivo que vas a conectar en un montaje concreto tiene entre sus especificaciones que la transmisión de datos serie se hace a 4800 baudios, con siete bits de datos en cada paquete, paridad par y un bit de parada, la instrucción adecuada para establecer comunicación sería:

Serial.begin (4800, SERIAL_7E1);

El método end(). No recibe ningún parámetro. Se usa para desactivar la conexión serie que, previamente, habíamos establecido con el método begin().

La condición if (Serial). Realmente esto no es un método. Es el uso de un condicional para determinar si hay una conexión serie disponible, es decir, que haya sido establecida con el método begin(), y aún no haya sido desactivada con el método end(). Siguiendo la nomenclatura de las conexiones serie que hemos recuadrado más arriba, en una placa Leonardo emplearíamos if (Serial1) y en una placa Mega podríamos emplear, en cada caso, if (Serial1), if (Serial2) o if (Serial3). En todo caso, obtendremos un valor true si el objeto correspondiente tiene su conexión activa o un valor false, en caso contrario.

El método available(). Como hemos visto en el último sketch, nos devuelve el número de datos disponibles para que nuestra placa Arduino pueda leer, procedentes de la conexión serie. Si no hay datos disponibles, devuelve 0. Cuando se haga la lectura de todos los datos disponibles, este método devolverá 0, hasta que vuelvan a entrar nuevos datos.

El método read(). También lo hemos usado en nuestro en nuestro último sketch. Lee el primer byte de los que están disponibles, reduciendo en uno el número que devolverá el método available(). Los datos son leídos por read() de byte en byte (con independencia de que se procesen como números de tipo byte, o como caracteres). Este es un concepto con el que, si no estás familiarizado, puede prestarse a confusión. Por esta razón, vamos a incluir aquí un pequeño sketch, que nos ayudará a entenderlo. Para cargar este scketch no es necesario que la placa Arduino tenga conectada ninguna circuitería, ya que no vamos a usar actuadores externos (pulsadores, potenciómetros o sensores de ningún tipo) ni transductores de respuesta (motores, LED’s, zumbadores, etc). Solo la placa conectada por USB al ordenador. El listado es el siguiente:

 

Como ves, es un código muy simple. Mientras haya contenido disponible procedente de la consola, leerá carácter a carácter y lo imprimirá en una línea. Prueba, por ejemplo, a escribir la palabra Texto en la línea superior de la consola, y pulsa ENTER. Verás un resultado como el de la figura 14.7.

Figura 14.7. Así funciona el método read() combinado con available().

Figura 14.7. Así funciona el método read() combinado con available().

El método peek(). No recibe ningún argumento. Devuelve el próximo carácter disponible procedente de la consola, SIN eliminarlo de la lista de caracteres disponibles almacenados en Serial.available().
El objeto Serial posee otros métodos. algunos de los cuales los usaremos a lo largo de estos artículos, en ciertos montajes. Aquí hemos reseñado brevemente los más relevantes. Si quieres ir “echando un ojo” por curiosidad, conéctate a https://www.arduino.cc/en/Reference/Serial.

 

     

2 comentarios:

  1. Pingback: ARD15 – Arduino Mega » eldesvandejose.com

  2. Pingback: ARD-SYN 01 – La sintaxis de Arduino (I) » eldesvandejose.com

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *