Objetivos
Material requerido.
Kit inicio UNO | |
Kit Inicio Mega |
Las matrices LED
Parece que los LEDs se fabrican en todos los tamaños y formatos imaginables, y este componente que os presentamos hoy, hace gala de esa creatividad. Las matrices de LEDs (o LED arrays) son, como su nombre indica, una matriz de diodos LED normales y corrientes que se comercializa en multitud de formatos y colores. Desde las de un solo color, a las que tienen varios colores posibles, e incluso las hay de una matriz RGB (Os dejo imaginar la de pines que tiene).
En esta sesión, de nuestro tutorial Arduino, vamos a usar una matriz de 8×8 LEDs de color rojo, que espero, sirva como demostración de cómo manejarla y de lo que se puede hacer con este tipo de material. Utilizaremos una única matriz e iremos arrastrando, letras y símbolos, para demostración de lo que podríamos hacer con una docena de estos simpáticos displays.
La idea, es que como tenemos una matriz de 8×8 podemos dibujar letras y símbolos de la misma manera que ya hicimos en el pasado, cuando definimos el símbolo de grado, para el display LCD. Es decir, definiendo matrices de puntos para representar las letras, que luego sacaremos por el Display. [one-half]
Por lo demás, son diodos LED totalmente normales, organizados en forma de matriz, que tendremos que multiplexar para poder iluminar uno u otro punto, tal y como hicimos en la sesión del teclado matricial. Este componente se presenta con dos filas de 8 pines cada una, que se conectan a las filas y las columnas.
- Si los diodos se unen por el positivo, se dice que son matrices de Ánodo común (El nombre pedante del positivo) y se se une por el negativo decimos que son de Cátodo común.
- Dependiendo del fabricante podéis encontrar de ambos tipos. [/fancy-ul] [/three-fourth]
Si ponemos HIGH en una columna, digamos la 2, no se iluminara nada aun. Pero cuando hagamos LOW en, digamos la fila 4, se cerrara el circuito a GND (con una resistencia de limitación, por supuesto) y el pin col 2 x fila 4, se encenderá.
Si alguno creíais que las 8 filas y 8 columnas de la matriz corresponderían a las dos filas de pines, donde una fila serían las columnas y la otra las filas, lo lógico ¿no?, vais dados. Porque este es uno de esos casos en los que el fabricante, por razones inescrutables a los mortales comunes, ha decidido mezclar unas y otras de forma aleatoria, haciendo poco menos que imposible adivinar cuál es cual sin el manual y convirtiendo el cableado en una pesadilla.
Así pues, aplicar la regla número uno. Buscad el manual del fabricante en Google (es más fácil de lo que crees).
- Por cierto en Internet encontrareis muy a menudo lo de léete el manual. De hecho, hasta han acuñado un acrónimo en inglés para ello: RTFM, iniciales de una bonita frase, que me ahorraré traducir.
Así pues vamos con el manual del 1588AS :Fíjate que aquí lo importantes es, la descripción de la matriz, donde nos habla de filas y columnas, pero sobre todo nos dice que pines del chip son cada fila y columna. Ya solo nos falta saber cuál de los pines es el uno, y aquí, yo no he sido capaza de localizar una marca que lo indique, a pesar de que si os fijáis, el manual marca con una cruz el pin 1, pero en mi chip, no he sido capaz de encontrar ninguna marca asimétrica.
- Lo que yo he hecho ha sido conectar 5V y GND, con una resistencia de 330Ω, y buscar los pines 13 y 9 correspondientes a la columna 1, fila 1, hasta que el punto 1,1 se encendió. A partir de ahí, el resto es fácil.
Os recomiendo que una vez que lo hayáis localizado, vayáis conectando el resto de los pines poco a poco y vayáis probando las conexiones. En mi caso he conectado las columnas de la siguiente manera:
Matriz | 13 | 3 | 4 | 10 | 6 | 11 | 15 | 16 |
---|---|---|---|---|---|---|---|---|
Arduino | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Y las filas:
Matriz | 9 | 14 | 8 | 12 | 1 | 7 | 2 | 5 |
---|---|---|---|---|---|---|---|---|
Arduino | 10 | 11 | 12 | 13 | A0 | A1 | A2 | A3 |
Para los que uséis el nuevo kit de inicio de Prometec, el display que viene es un 1388ASR, que es un modelo de Ánodo comun, la disposición de los pines es diferente y su definición es esta:
La conexión a vuestro Arduino es como sigue:
Matriz | 1 | 2 | 6 | 11 | 7 | 13 | 14 | 4 |
---|---|---|---|---|---|---|---|---|
Arduino | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
Y para las filas:
Matriz | 12 | 15 | 10 | 16 | 5 | 9 | 3 | 8 |
---|---|---|---|---|---|---|---|---|
Arduino | 10 | 11 | 12 | 13 | A0 | A1 | A2 | A3 |
Fijaros en que como conectamos la misma disposición de filas y columnas a nuestro Arduinoy en el mismo orden de pines, la configuración e sintercambiable.
Creo que esta va a ser la primera, y espero que ultima vez, que no vamos a incluir esquema ni protoboard, para mostrar las conexiones, porque no he encontrado ninguna pieza para Fritzing con una matriz como esta, y porque creo que añadiría muy poco, a las tablas de arriba.
El Programa de control
El programa de barrido que hemos usado para comprobar que teníamos bien conectados los puntos, contiene ya la base de nuestro programa de control.
void setup() // Prog_37_1 { for (int j=2; j<19; j++) pinMode(j, OUTPUT); } void loop() { for (int j=2; j<10; j++) { digitalWrite(j, HIGH); //Levantamos la columna for (int k= 10 ; k<18 ; k++) { digitalWrite(k, LOW); //Encendemos el punto delay(250); digitalWrite(k, HIGH); //Apagamos el punto } digitalWrite(j, LOW); //Bajamos la columna } }
Lo importante en el, es que en primer lugar definimos en el setup como salidas de los pines 2 al 19.
¿ AL 19 ?. ¡No existen tantos pines!. Bueno, en realidad sí.
Ya vimos en su momento que los pines analógicos e A0 al A5 se podían usar como pines digitales normales. Y cuando es así podemos numerarlos como pines digitales del 14 al 19.
Me he aprovechado de esta curiosidad, para que sea más cómodo escribir el programa. Para iluminar un punto concreto, en primer lugar poner en LOW todas las columnas y en HIGH todas las filas, lo que apaga todo.
Después levantar la columna que contiene el punto a iluminar. Fíjate que esto de por sí, no va a encender nada aun. Sera cuando pongamos una de las filas en LOW, cuando se cerrara el circuito y un punto se iluminara. La curiosidad de esto, es que el diodo se ilumina cuando ponemos 0 en una fila, no un 1 como hasta ahora.
A esto se le llama, lógica negativa, porque la acción se ejecuta con un valor bajo y no alto.
Entonces ¿ Cómo hacemos para mostrar un carácter, digamos la P? Pues tal y como vimos en la sesión previa. Dibujando cuadraditos en un papel cuadriculado.
Pero, ¿Entonces, tenemos que definir una matriz por cada letra que quiero representar?
La respuesta, querido amigo, es un rotundo SI. Y no solo para cada letra, mayúscula y minúscula, sino también para cada número y símbolo que quiera representar.
- De hecho este es el primer sistema que los ordenadores han usado desde el principio para dibujar los caracteres en pantalla.
Para escribir PROMETEC, tendremos que definir las letras correspondientes:
byte P[] = { 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x40 }; byte R[] = { 0x78, 0x44, 0x44, 0x78, 0x70, 0x58, 0x4C, 0x46 }; byte O[] = { 0x3C, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3C }; byte M[] = { 0x66, 0x7E, 0x5A, 0x42, 0x42, 0x42, 0x42, 0x42 }; byte E[] = { 0x7C, 0x40, 0x40, 0x78, 0x78, 0x40, 0x40, 0x7C }; byte T[] = { 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }; byte C[] = { 0x7c, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7C }; byte N[] = { 0x42, 0x62, 0x72, 0x5A, 0x4E, 0x46, 0x42, 0x42 }; byte dot[]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06 }; byte sp[]= { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Vamos ahora, a ver como sacamos una de estas letras en el display. Lo primero es que vamos a usar la función GetBit de la sesión anterior para sacar la información a presentar en el display.
bool GetBit( byte N, int pos) { // pos = 7 6 5 4 3 2 1 0 int b = N >> pos ; // Shift bits b = b & 1 ; // coger solo el ultimo bit return b ; }
Por tanto barreremos filas y columnas, para posicionar cada punto, y leeremos con GetBit si debemos iluminarlo o no. Vamos a empezar definiendo el setup y ell array para la P:
byte P[] = { 0x78, 0x44, 0x44, 0x78, 0x40, 0x40, 0x40, 0x40 }; void setup() { for (int j=2; j<19; j++) pinMode(j, OUTPUT); Serial.begin(9600); }
Y ahora definimos un esqueleto que barra filas y columnas, sin preocuparnos de los números de pin:
void loop() { for (int fil = 0; fil <8 ; fil++) { byte F = P[fil] ; for (int col =7; col >= 0 ; col--) { } Serial.print(F, HEX); Serial.print(", "); } Serial.println(); }
Los print centrales han sido para comprobar que lee correctamente (que nunca se sabe). Vamos ahora a buscar el bit para cada fila y columna, y lo sacaremos en pantalla, para asegurarnos de que lee bien los bits:
void loop() { for (int fil = 0; fil <8 ; fil++) { byte F = P[fil] ; for (int col =7; col >= 0 ; col--) { bool b = GetBit(F, col) ; Serial.print(b); Serial.print(","); } Serial.println(); } Serial.println("----------------------"); }
Volvemos a comprobar, que presenta correctamente los valores de cada bit, con lo que ya hemos desempaquetado el valor de cada punto del display. Si entornáis los ojos casi se ve la P entre los 1s y 0s Ahora nos queda convertir los valores fila y columna a pines de Arduino. Recordad que las columnas van del pin2 al pin9 de Arduino, pero la dos corresponde a nuestra columna 7, según lo definimos en GetBit, Como este no es mi primer baile, vamos a ver despacito como hacemos esto. Queremos convertir valores de columnas que van del 7 al 0, en valores de pin que van del 2 al 9. ¿Qué formula aplicamos?. ¡VA! Pensad un poco. Basta con esto, Pin = 9 – col , y no hay más. Vamos a comprobarlo
Col | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
9 – 0 | 9 -1 | 9-2 | 9-3 | 9-4 | 9-5 | 9-6 | 9-7 | |
pin | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 |
A que no era para tanto. Vamos ahora a ver como convertimos las filas del 0 al 7 en pines del 10 al 17….con este sí que os atrevéis, ¿No? Pin = fil + 10. Nos falta otra pequeña función antes de escribir el programa completo. Una que apague todos los puntos LED para borrar el display:
void Clear() { for (int j=2; j<10; j++) // Valores de los pines de columna digitalWrite(j, LOW); // Todos apagados for (int k= 10 ; k<18 ; k++) digitalWrite(k, HIGH); // Todas las filas cortadas }
Las columnas son activas en HIGH, así que ponemos todas a LOW. Y las filas son activas en LOW, por lo que que todas a HIGH. Esto nos garantiza que todos los puntos se apaguen. Ya encenderemos luego los que nos interesen. El programa quedara más o menos así
void loop() // Prog_37_1 { Clear(); for (int fil = 0; fil <8 ; fil++) { digitalWrite( fil + 10 , LOW) ; // Activamos la fila para el barrido byte F = P[fil] ; for (int col =7; col >= 0 ; col--) { bool b = GetBit(F, col) ; if (b) digitalWrite( 9 - col ,HIGH); //Levantamos la columna else digitalWrite( 9 - col ,LOW); // Si 0, apagamos } digitalWrite( fil + 10 , HIGH) ; // Apagamos fila antes de salir } }
Podéis comprobar que pinta una bonita P en el display.Prog_37_2
byte Selecciona( char c, byte fil) { if ( c == 'P') return(P[fil]) ; if ( c == 'R') return( R[fil]) ; if ( c == 'O') return( O[fil]); if ( c == 'M') return( M[fil]); if ( c == 'E') return( E[fil]); if ( c == 'T') return( T[fil]); if ( c == 'C') return( C[fil]); if ( c == 'N') return( N[fil]); if ( c == '.') return( dot[fil]); if ( c == ' ') return( sp[fil]); }
Y además, modificaremos nuestro anterior loop, para convertirlo en una función, a la que pasamos el carácter que queremos mostrar, y dentro de esta función llamaremos a Selecciona () para leer la información del array correspondiente.
void SetChar(char p) { Clear(); for (int fil = 0; fil <8 ; fil++) { digitalWrite( fil + 10 , LOW) ; // Activamos la fila para el barrido byte F = Selecciona( p, fil); for (int col =7; col >= 0 ; col--) { digitalWrite(8-col, LOW); //Bajamos la columna bool b = GetBit(F, col) ; if (b) //Levantamos la columna, con su pin digitalWrite( 9 - col ,HIGH); else digitalWrite( 9 - col ,LOW); // Si 0, apagamos } digitalWrite( fil + 10 , HIGH) ; // Apagamos fila antes de salir } }
Ya solo falta escribir el loop:
void loop() { String s = "PROMETEC.NET " ; int l = s.length(); // Calcula la longitus de s for ( int n = 0; n< l; n++ ) { long t = millis(); char c = s[n]; while ( millis()< t+ 400) SetChar(c); } }
Que lo único que hace, es ir tomando una a una, las letras del String s y llamar a Setchar para que la represente. Pero con una complicación adicional, para que no os aburráis. Ya comentamos en alguna sesión previa, que cuando multiplexas segmentos, o en este caso puntos, cualquier intento de utilizar un delay acabará en desastre ( No me creáis, probadlo) porque la imagen en el display depende de la velocidad a la que refrescas los puntos, y el delay sencillamente lo impide. Cuando necesitéis un retraso, como ahora, y no podáis poner un delay, utilizar un while como este suele ser seguro. [one-fourth] [/one-fourth][three-fourth last][margin value=»5″ /][fancy-ul style=»rounded-tick»] [three-fourth last][margin value=»5″ /][fancy-ul style=»rounded-tick»]
- Fijaros que he añadido un par de espacios al final del string s. Como he definido los espacios como todo apagado. Esto nos da un retraso adicional, antes de que vuelva a empezar con la función.
Aqui os dejo un pequeño video con el resultado
Resumen de la sesión
En este curso arduino hemos aprendido lo siguiente: