Objetivos
Material requerido.
Tienda España | |
---|---|
Display 8 digitos 7 segmentos. |
MAX7219 y display BCD de 8 dígitos
Andaba yo por casa buscando un display de buena visibilidad y al menos 6 0 7 dígitos, para una sesión que estoy preparando (Y que espero pueda ver la luz en breve) y sin pretenderlo me encontré con la caja de cosas raras, a la espera de que un día tenga ganas y tiempo de ponerme a ver qué puedo hacer con todo eso, cuando de repente me encontré con una tanda de estos displays.
No recuerdo ni haber comprado, y me había olvidado por completo, pero que me venían de perlas para lo estaba pensando y además no me iba a costar 15 días que me llegara de cualquier proveedor más o menos lejano. Tenia muy buena pinta. 8 dígitos LED de 7 segmentos y conectores por delante y por detrás para poder conectarlos en cascada por si necesito mas dígitos. Bien, bien. Esto marcha.
Usa un chip MAX 7219 que es estándar para el manejo de esto tipo de displays, pero un poco latazo para andar controlando a pelo y ni puñetera idea de como manejarlo. Así que aplique la regla de oro cuando no tienes ni idea de como poner en marcha algo: Le pregunte a Google quien había hecho una librería para este chisme.
- Afortunadamente en el mundo Arduino siempre hay un alma caritativa que se curra lo que sea y lo pone a nuestra disposición. ¡¡Gracias, Gracias!!
Enseguida encontré la página de Arduino Learning que tenía un tutorial sobre este chisme y que usaba una librería para manejarlo, de forma cómoda esperaba yo. Me puse a estudiar el tema y aunque me daba todas las claves para manejarlo incluida la librería, ésta no ayudaba mucho a la hora de disponer de alguna función del tipo print (“83456.2”) y solo disponía de funciones rudimentarias para imprimir digito a digito. Bien, pues me toca currarme las funciones que impriman algo en el display de la forma que a mi me gusta, para vagos, y de eso va la sesión de hoy. Porque la verdad es que son unos displays de lo más útil si el tamaño del display LCD de 2 líneas se te queda corto y necesitas 8 dígitos (O más)
Conectando el display a nuestro Arduino
La conexión es trivial. El display cuenta con 5 pines para el control de lo que muestra, y la conexión se debe hacer algo así:
Display | VCC | GND | DIN | CIS | CLK |
---|---|---|---|---|---|
Arduino | 5V | GND | 12 | 10 | 11 |
Aunque en esta sesión solo vamos a usar uno de estos displays, podríamos conectar en cascada hasta 8 de estas unidades sin tocar la librería, sin mas que unir los pines de salida de uno del display al siguiente (Aunque no se ve muy claro en la imagen de arriba, a la derecha del display están repetidos los conectores de entrada para hacer la conexión en cadena)
Programando el display
Empezaremos descargando la librería pertinente en Github: wayoda/LedControl , Y para los que tengan interés, aquí está la documentación de la librería. En líneas generales las funciones del display son bastante sencillas. Empezamos con:
#include "LedControl.h" LedControl lc=LedControl(12,11,10,1); // (DIN, CLK, CS, Numero de displays en cascada)
La primera incluye la librería y la segunda crea una instancia del display, Indicando los pines Arduino a los que van los del display en que está conectado y el numero de displays en línea.
En el setup(), encendemos el display, fijamos el brillo y borramos el contenido.
void setup() { Serial.begin(115200); // the zero refers to the MAX7219 number, it is zero for 1 chip lc.shutdown(0,false); // Apaga el ahorro de energia lc.setIntensity(0,5); // Pone el brillo a (0~15 possible values) lc.clearDisplay(0); // clear screen }
Si queremos mostrar un número entero en el display, resulta que no disponemos de funciones para ello, sino que parece que solo disponemos de una función:
lc.setDigit(disp,pos,valor,false);
Donde disp es el número de orden del display en la cadena con 0 para el primero, nuestro caso. Pos representa el número de dígito que queremos imprimir con 0 el mas a la derecha (Las unidades), después valor representa el dígito a mostrar ( de 0 a 9) y por ultimo el false es que el display este encendido. Así, por ejemplo, para mostrar un dígito 7 en la tercera posición (Los miles):
lc.setDigit(0,3,7,false);
Como todo esto es un poco rollo, pensé que sería más cómodo (Pero que mucho más cómodo) hacer un par de funciones que se puedan usar de forma similar al print y a cascarla. Por eso vamos a escribir una función que imprima números enteros en el display.
¿Cómo podemos hacer una función, del tipo displayInt( long N) a la que pasamos un entero long y se encargue de toda la mandanga de descomponer el numero en sus dígitos y posicionarlo en el display? Piensa un poco. ¿Cómo lo harías? Estas a tiempo antes de seguir leyendo.
El truco básico puede ser calcular el resultado entero (Despreciando decimales) de un número dividido por 10, lo que nos da todas las cifras menos la primera y a su vez, la primera cifra es el resto de esa misma división. UUUUUyyyy , que lio!!! Tranquilos, es una tontería. Imagina un número como el 34279. Hagamos un ejemplo.
34.279 / 10 --> 3.427 Despreciando los decimales 34.279 % 10 --> 9 El dígito de las unidades
Si escribimos esto e C++ podría quedar así:
long r, n = N ; // Long es un entero de doble tamaño int pos = 0 ; r = n % 10 ; n = n / 10
Con esto habremos guardado el dígito de las unidades en r y en n conservamos la división entera entre 10. Si hacemos un bucle que continúe mientras n> 0 (Mientras queden dígitos) obtendremos una función como esta:
void displayInt( long N) { long r, n = N ; int pos = 0 ; lc.clearDisplay(0);// clear screen while ( n >0 ) { r = n % 10 ; n = n / 10 ; lc.setDigit(0,pos++,r,false); } }
La única línea nueva es la imprime cada dígito en secuencia:
lc.setDigit(0,pos++,r,false);
La variable pos empieza con valor cero, apuntando a la posición de las unidades, la más a la derecha del display, imprime el valor en r (Unidades en la primera iteración), después incrementamos pos (con pos++) con lo que en la próxima vuelta apuntamos al segundo digito (Las decenas) y así sucesivamente. Tampoco era para tanto,
Podemos hacer una pequeña prueba con este programa: displayInt
Parece que va todo bien. Descompone los números de menor a mayor peso, que es el orden en el que los iremos imprimiendo (De derecha a izquierda en lugar del orden normal al escribir)
Descomponer un entero es un poco más fácil que descomponer un número float con decimales, porque necesitamos separar la parte entera (Que ya sabemos como imprimir en el display) de la parte con decimales tras la coma.
Además, lo lógico en un número con decimales (Para mi otro proyecto) es que pueda elegir el numero de dígitos que necesitemos tras la coma. Por eso la nueva función displayFloat() debería recibir dos parámetros. Primero el numero en cuestión a representar y un segundo valor con el numero de decimales requeridos. Podemos intentar algo así: Calculamos un entero k, como la potencia de 10 del número de decimales que nos piden (10^2 para dos, 10^3 para tres… etc). De este modo, si tomamos el numero dado N y lo multiplicamos por k lo hemos convertido en un entero, cuyas últimas cifras son los decimales que queremos. Si hacemos la división entera(Sin decimales) de (N * k )/k, tendremos los dígitos antes de la coma. A su vez (N-p)*k será un entero que representa las cifras tras la coma.
Y si hacemos (N – p) *k conseguiremos un entero de la parte tras la coma como número entero. Hagamos una prueba con un número, por ejemplo 256,8354, para tres decimales tendríamos:
La parte entera p= 256,8354 * 1000) / 1000 = (256835,4/1000)1000 = 256.835,4 /1000
La división entera de 256.83,54 /1000 sale 256 que corresponden a los números antes de la coma. Para calcular la parte decimal como un entero: (256,8354 – 256) * 1000 = 835,4 y su parte entera será 835. Es mucho más largo explicarlo en palabras que hacerlo en código C++:
void displayFloat(float N, int dec) { long r, n = N ; // 256.8345 int pos = 0 ; int k = pow(10,dec); lc.clearDisplay(0); // clear screen int p = (int)((N * k )/ k) ; // Parte entera int q = (N - p)* k ; // Parte decimal
Lo que toca ahora es simplemente modificar el while de la función de mostrar enteros anteriores, para acomodarlo a las dos partes de un numero con decimales:
void displayFloat(float N, int dec) { long r, n = N; int pos = 0 ; int k = pow(10,dec); lc.clearDisplay(0);// clear screen int p = (int)((N * k )/ k) ; // Parte entera int q = (N - p)* k ; // Parte decimal Serial.println(q); while ( q >0 ) // Imprime la parte decimal { r = q % 10 ; q = q / 10 ; lc.setDigit(0,pos++,r,false); } lc.setChar(0,pos++,char('.'),false); // Imprime el punto decimal while ( p >0 ) // Imprime la parte decimal { r = p % 10 ; p = p / 10 ; lc.setDigit(0,pos++,r,false); } }
Nos toca probar ahora que nuestra función de impresión funciona para números con decimales, pero resulta que para generar números aleatorios hay al menos dos maneras posibles. La primera, es lo típico, algo como esto:
float N = 27.33 * random(1, 299) / random( 1, 27);
calculamos un numero entre 1 y 299, y un segundo entre 1 y 27, para hacer el cociente.
- La fórmula de arriba funciona bien, pero como random siempre devuelve números enteros, el cociente de dos enteros, es un entero y adiós a los decimales. Por ese le multiplico por 27.33 y con eso forzamos a que el número sea float.
- Otra forma de hacerlo sería forzar un type casting a float de los operandos, pero al final tenia que multiplicarlo por algún numero de 2 cifras para que el resultado tuviera varios dígitos en la parte entera.
Esto funciona muy bien pero no me resisto a usar otro generador de números realmente aleatorios que tenemos en Arduino y que creo que nunca hemos utilizado: Leer una puerta analógica con un cable conectado y dejado al aire.
Los pines flotantes no tienen un valor definido y si le añadimos un cable colgando a una puerta como la A0, lo que lea serán interferencias de radio impredecibles, es decir, justo lo que queremos, un generador de números aleatorios entre 0 y 1024. Podemos forzar a ese generador aleatorio de números con decimales con algo así:
float N = 101.3 * analogRead(A0) / 29.7 ;
Al final el loop podría quedar como esto:
void loop() { float N = 101.3 * analogRead(A0) / 29.7 ; // float N = 27.33 * random(1, 299) / random( 1, 27); displayFloat(N , random(1,4)); delay (1000); }
Programa: displayfloat
Consideraciones finales.
El objetivo de esta sesión era aprender a usar este magnífico display de 8 dígitos de 7 segmentes, y de paso desarrollar unas funciones que nos permitieran imprimir con comodidad enteros y números en coma flotante, de un modo conveniente sin preocuparnos de separar posiciones de cada número y otras zarandajas.
Las funciones displayInt() y displayFloat cumplen con su función , pero tienen algunos inconvenientes que interesa destacar aquí y que muestra el camino a seguir para futuros cambios: