Panel de texto con Arduino

En la revista Elektor nº 255 de agosto de 2001 se publicó un artículo para montar un panel de texto. Ese mismo mes empecé a hacerme con el material necesario.
Primero lo hice sólo con tres módulos, pero no iba bien, ya que en el circuito de Elektor usan un 74LS164 para activar los leds y no los mantiene encendidos mientras se carga la línea siguiente. El efecto era muy malo.
Lo dejé algún tiempo y lo retomé añadiendo un integrado más entre cada 74LS164 y los leds. Todo se controlaba desde un 16F84A.
No recuerdo qué paso ni por qué lo dejé aparcado.
Un tiempo después apareció el Arduino y decidí retomarlo.
Encontré un integrado que hacía la misma función que los dos anteriores, el MBI-5026, que controla 16 LEDs cada uno y es muy fácil de manejar.
No sé por qué volví a aparcar el asunto.
Como cada vez cacharreo menos y a este paso se me va a olvidar la electrónica, lo he vuelto a retomar.
Antes hacía las placas con el EAGLE y con insoladora. Ya no me acuerdo del EAGLE y he hecho las placas con rotulador a mano y también he usado placas de estas con agujeros.

De momento y después de 21 años este es el resultado. Todo el hardware y software es diseño mío, menos la placa del Arduino.



El panel va con un Arduino nano, pero no sé por qué, funcionan sólo una vez y se quedan inutilizados, ya me he cargado dos, por eso de momento he puesto un Arduino UNO. No sé si tendrá algo que ver el Windows 10 y el CH340, ya que hasta hace poco tenía el Windows 7.



Cada fila está controlada por un darlington TIP125. Entre el pin de salida del Arduino y la base del TIP125 hay una resistencia de 6K8 ohms, que es suficiente para que entre en saturación.



Una cosa curiosa que ocurre es que la primera línea sale desplazada un led a la izquierda, como si al acabar la primera línea se produjera un impulso extra de CLK. Es raro que sólo ocurra en la primera línea y no en las otras, ya que el código es igual para las siete.
De momento lo he solucionado desplazando los datos de esa línea una posición a la derecha.

Código:
// Visualiza palabra => HOLA GENTE
#define CLK 5
#define SDI 4
#define OE 3
#define LE 2

const byte elementos[7][8] = {
  {0x02,0x4C,0x83,0x03,0x3D,0x2F,0xA5,0xE6},
  {0x04,0xA5,0x09,0x09,0x43,0x44,0x48,0x52},
  {0x04,0xA5,0x09,0x08,0x42,0xC4,0x48,0x92},
  {0x07,0xA5,0x0F,0x0B,0x72,0x44,0x49,0x1E},
  {0x04,0xA5,0x09,0x09,0x42,0x44,0x4A,0x12},
  {0x04,0xA5,0x09,0x09,0x42,0x44,0x4A,0x12},
  {0x04,0x99,0xE9,0x06,0x7A,0x44,0x33,0xD2},
};

void setup() {

  DDRD = DDRD | 0b11111100; // D2 a D12
  DDRB = DDRB | 0b00011111; // OUTPUTs
  PORTD = PORTD | 0b11000000; // Salidas TIP125
  PORTB = PORTB | 0b00011111; // apagadas
}

void loop() {

  for (byte fila=0; fila < 8; fila++) {
    digitalWrite(LE, LOW);
    for (byte columna=0; columna < 8; columna++) {
      digitalWrite(SDI, (elementos[fila][columna] & 0x80) ? HIGH : LOW);
      digitalWrite(CLK, HIGH);
      delayMicroseconds(10);
      digitalWrite(CLK, LOW);
      digitalWrite(SDI, (elementos[fila][columna] & 0x40) ? HIGH : LOW);
      digitalWrite(CLK, HIGH);
      delayMicroseconds(10);
      digitalWrite(CLK, LOW);
      digitalWrite(SDI, (elementos[fila][columna] & 0x20) ? HIGH : LOW);
      digitalWrite(CLK, HIGH);
      delayMicroseconds(10);
      digitalWrite(CLK, LOW);
      digitalWrite(SDI, (elementos[fila][columna] & 0x10) ? HIGH : LOW);
      digitalWrite(CLK, HIGH);
      delayMicroseconds(10);
      digitalWrite(CLK, LOW);
      digitalWrite(SDI, (elementos[fila][columna] & 0x08) ? HIGH : LOW);
      digitalWrite(CLK, HIGH);
      delayMicroseconds(10);
      digitalWrite(CLK, LOW);
      digitalWrite(SDI, (elementos[fila][columna] & 0x04) ? HIGH : LOW);
      digitalWrite(CLK, HIGH);
      delayMicroseconds(10);
      digitalWrite(CLK, LOW);
      digitalWrite(SDI, (elementos[fila][columna] & 0x02) ? HIGH : LOW);
      digitalWrite(CLK, HIGH); 
      delayMicroseconds(10);
      digitalWrite(CLK, LOW);
      digitalWrite(SDI, (elementos[fila][columna] & 0x01) ? HIGH : LOW);
      digitalWrite(CLK, HIGH); 
      delayMicroseconds(10);
      digitalWrite(CLK, LOW);
}

  digitalWrite(fila + 5, HIGH);
  digitalWrite(fila + 6, LOW);
  digitalWrite(LE, HIGH);
  delayMicroseconds(10);
  }
}

troglodita dijo:
Una cosa curiosa que ocurre es que la primera línea sale desplazada un led a la izquierda, como si al acabar la primera línea se produjera un impulso extra de CLK. Es raro que sólo ocurra en la primera línea y no en las otras, ya que el código es igual para las siete.
De momento lo he solucionado desplazando los datos de esa línea una posición a la derecha.
Este problema estaba causado porque en un momento del programa me dirigía al pin 5 que es el CLK y metía un impulso de más. Ya está corregido con la función "if" añadida.

Otro problema curioso es que en la fila de arriba aparecía una secuencia fantasma que se puede ver en este vídeo:


Con respecto a ese problema, después de tirar de osciloscopio, ver que todas las señales son un caos, con lo cual el osciloscopio me ha liado más todavía y hacer muchos cambios y programas y usar varios arduinos distintos, uno antiguo con chip DIL y otro con chip pequeño, he llegado a la conclusión de que en los arduinos el pin 6 está embrujado.

Todo ese "ruido" sale por el pin 6 y entra por la base del TIP 125, sale por el colector, atraviesa los LEDs y llega a los MBI-5026. Esto no tiene mucho sentido pero es la conclusión a la que he llegado.

Para deshacerme del pin 6 he usado los pines del 7 al 13, pero el pin 13 de mis arduinos no funcionan en este circuito.

Como el pin 3 (señal OE), está siempre a cero, he pasado la última fila a ese pin y he hecho una chapucilla momentánea (poco elegante) para dirigirme a ese pin a su tiempo.

Aunque con los cambios anteriores ya funciona bien, he vuelto a dejarlo como al principio para seguir investigando lo de la secuencia fantasma en la primera fila.
Si conecto otra fila al pin 6 la secuencia fantasma aparece en esa otra fila, luego se descarta el TIP125 de la primera fila, ya que todos lo hacen si se conectan al pin 6.
Esa secuencia fantasma aparece en los LEDs controlados por los MBI-5026 1º, 2º y 4º, no en el 3º. Si cambio el 3º por el 2º sigue apareciendo en el 2º y no el 3º, luego se descartan los MBI-5026.

Si desconecto el TIP125 del pin 6 y lo conecto directamente a GND (se activa por 0) sigue apareciendo la secuencia fantasma, luego no le entra por el pin 6 como parecía lógico. :cry:
Es decir, si uso en el programa el pin 6, aunque luego no lo use físicamente, aparece el problema. Sólo desaparece si en el programa no uso el pin 6.
Después de hacer muchas pruebas y usar otros Arduinos, (he usado Nano, Uno con chip pequeño, Uno con chip DIL y Diecimilla con ATmega 168), con todos ocurría lo mismo.
La frecuencia de esa secuencia fantasma no variaba si aumentaba los delays o incluso si los quitaba (he visto que no hacen falta).
Por si fuese por la fuente de alimentación conmutada, lo he conectado a una fuente con transformador, puente, condensador y 7805, pero sigue igual. Esa secuencia tiene que salir de los arduinos.

Lo siguiente que he hecho ha sido desactivar las interrupciones, ya que no uso ni interrupciones ni delays, con "noInterrupts()" y mano de santo. Ha desaparecido la secuencia fantasma. :D
He pasado el programa a un Arduino Nano para dejarlo integrado en el panel (sólo he conseguido programarlo con el IDE 1.0.3). :unsure:
Y así es como ha quedado el programa:



Código:
// Visualiza => HOLA GENTE

#define CLK 5 //Relog para MBI-5026
#define SDI 4 //Datos al MBI-5026
#define OE 3 //MBI-5026 enable
#define LE 2 //Pasar información de los latches a los LEDs

const byte elementos[7][8] = { //Texto en exadecimal {fila}[columna}

{0x02, 0x27, 0x20, 0x70, 0x73, 0xE8, 0xBE, 0xF8},
{0x02, 0x28, 0xA0, 0x88, 0x8A, 0x0C, 0x88, 0x80},
{0x02, 0x28, 0xA0, 0x88, 0x82, 0x0A, 0x88, 0x80},
{0x03, 0xE8, 0xA0, 0xF8, 0xBB, 0xC9, 0x88, 0xF0},
{0x02, 0x28, 0xA0, 0x88, 0x8A, 0x08, 0x88, 0x80},
{0x02, 0x28, 0xA0, 0x88, 0x8A, 0x08, 0x88, 0x80},
{0x02, 0x27, 0x3E, 0x88, 0x73, 0xE8, 0x88, 0xF8},
};

void setup() {

  DDRD = DDRD | 0b11111100; // D2 a D12
  DDRB = DDRB | 0b00011111; // OUTPUTs
  PORTD = PORTD | 0b11000000; // Salidas TIP125
  PORTB = PORTB | 0b00011111; // apagadas
  noInterrupts(); // Evita secuencia fantasma tenue en fila 1

}

void loop() {
  for (byte fila=0; fila < 8; fila++) { //elementos{fila}[columna}
    digitalWrite(LE, LOW);
    for (byte columna=0; columna < 8; columna++) {
      byte data = elementos[fila][columna];
      for (byte bits=0; bits < 8; bits++) {
       digitalWrite(SDI, (data & 0x80) ? HIGH : LOW);
       digitalWrite(CLK, HIGH);
       digitalWrite(CLK, LOW);
       data<<=1;
    }
  }
if (fila == 0) {                             //Este if es para evitar que al apagar la
  digitalWrite(12, HIGH);            //fila anterior cuando estamos en la fila
 }                                                 //primera (pin 6) meta un impulso de CLK
else {                                          //en el pin 5, ya que los TIP125 se apagan
  digitalWrite(fila + 5, HIGH);     //con HIGH y se encienden con LOW.
}

  digitalWrite(fila + 6, LOW);
  digitalWrite(LE, HIGH);
  }
}

La simplificación de la tercera secuencia "for" ha sido trabajo de Heli de CCAA.
 
Hola Don Troglodita, con tu relato y montaje has logrado "desviejarme", entonces decidí escribir un software para tu montaje, pues siempre llamaron mi atención esas historias de los leds display cuando son de a muchos (muchísimos).

No tengo posibilidad alguna de montar tu hardware y probar en el mundo real, por eso estoy aquí, pues en Argentina (super devaluados) y en un pueblito igual o peor que Macondo, imposible hacerme con los componentes, por ello va solo la simulación en Proteus.

Obviamente todos sabemos que existen módulos armados de matríz de puntos o integrados controladores como el MAX 7219 o los TM16xx (tm1637, tm1638 y otros)
Sin embargo como somos unos Viejos Mañosos, de la Vieja Escuela, lo haremos a fuerza de conmutaciones desde el micro mísmo, de eso se tratan estos programas.

El código está compilado con AVR-GCC, es lenguaje C, lo simulé en el Isis Proteus 8.6 SP2, se trata de una matríz de leds de 7x60, controlada por columnas con shift register 74hc595 que serían los sumideros o tierras, el "barrido" es por filas, serían siete conmutaciones, que a primera se corresponden a tu montaje y son los Vcc controlados por los transistores TIP, en mi simulador no hay modelo del MBI5026, por eso los H595, creo que para el caso es lo mísmo, no use en el montaje ni en el software las líneas EN (enable), están todas puenteadas en ON.

Como se darán cuenta, están faltando las resistencias que limitan las corrientes en los leds. El control de las filas requiere de transistores de cierta potencia como los empleados en el montaje de Troglodita. Además de capacitores de filtrado, o el cristal del micro y demás yerbas (componentes), están ausentes para no hacer lenta y complicada la simulación, ya sabemos que son necesarios todos estos componentes en un montaje real.


1ro-
La primer simulación es el archivo:
TrogloditaArduino.pdsprj
y el micro 328p a 16mHz con cristal externo idem a la placa Arduino, con el archivo objeto
text2.troglodita.ino.hex <-- text2.troglodita.ino
que salió de compilar Tú código, el segundo del post, sin tocar nada. Es el texto estático que propones, considerando eso supongo que las conexiones del simulador se corresponden con las de tu montaje, dado que se deja ver el mensaje sin problemas, el mísmo archivo del simulador también funciona con el .hex de tu primer código.
text1.troglodita.ino.hex <-- text1.troglodita.ino
TrogloditaArduino.pdsprj
text2.troglodita.jpg

2do-
Seguimos con nuestra primer simulación, pero el archivo objeto cargado en el micro, salió de un programa escrito en C plano,
TrogloditaText.hex <-- TrogloditaText.hex
(TrogloditaText.c)
digamos que traduje la filosofía Tú código, entonces emplea los mísmos pines del AVR y el SPI es haciendo bit bangins (software).
A primera veo que el archivo .hex, es más chico y se ejecuta más rápido que el obtenido del código Arduino, eso lo deduzco mirando el frecuencimetro que puse en el simulador, en uno de los pines del barrido.


3ro-
El circuito es el mísmo, excepto que tiene otra conexión las líneas del barrido y las del SPI es tal que permite emplear el SPI del micro por hardware, o sea que el trabajo de empujar todos esos bits, se lo dejamos al módulo SPI del AVR y está funcionando con interrupciones, entonces cada vez que inicia la transmisión de un byte, continúa en segundo plano, liberando al micro de esa tarea y podría ocuparse de otra actividad, p.e. encender y apagar ese led que está en la simulación.
El programa es el mísmo anterior, el texto estático que Vos has escrito.
TrogloditaHardware.pdsprj <-- TrogloditaHardware.hex
(TrogloditaHardware.c)
El SPI, está a la velocidad mínima de transmisión posible, para aprovechar e involucar éstos tiempos en el tema de refresco (barrido) de los leds para lograr la persistencia visual. Con esto de la velocidad mínima nos evitamos que nuestros cables se conviertan en antenas irradiadoras de interferencias, o que la línea SCK se vea afectada e interferida por la más mínima interferencia externa.


4to-
Aquí viene lo bueno, el conexionado idem al anterior, para poder emplear SPI por hardware, ahora el texto se desplaza letra a letra, por el arreglo de leds. Todo a fuerza de conmutaciones y envios SPI desde el microcontrolador que se hace en segundo plano empleando interrupciones.
TrogloditaHardware.pdsprj <-- TrogloditaHardBanner.hex
TrogloditaHardBanner.c
Se podría poner cualquier texto en la variable 'msg[]' que sería mostrado en el arreglo de leds.


Los patrones (bits maps) de las letras son tomadas crudamente de la libreria Nokia5510 que andan dando vuelta por ahí, esos son caracteres de 8x6 (ocho lineas por 6 columnas), así que algunos no se verán bien, porque falta la octava línea, especialmente en los caracteres minúscula altos (q, g ...)

Los .hex los pueden subir facilmente desde WinGate, empleando AVRDUDESS, eso lo bajan desde aquí, también un video de U2 de su empleo:


Los archivos .pdf son las capturas del circuito del simulador Proteus.
El archivo "prnBits.exe.RenameMe" deben renombrarlo, a "prnBits.exe" si lo quieren correr, es un demo para la consola de Windows, mostrando lo que en realidad sucede en esas matrices de led, pero a puro texto plano ASCII, "prnBits.txt" es la salida de este código.

Si requieren re-compilar (seguramente), pues deberían hacerlo con el AVR-GCC, o el Atmel Studio, o la mounstrosidad de Microchip y su IDE, pero con maña cambiando los path del archivo makefile, puenden hacerlo directamente con la instalación de Arduino IDE que tienen instalada.

Si te animas, subí estos codigos .hex en tu montaje, deberían funcionar a primera, los dos últimos, requieren modificar la conexión, pero es fácil, porque solo son los pines de salida de la placa Arduino.
Si tenés interés, te comento una mejora que se le puede implementar facilmente al montaje de los leds, cualquier duda pregunta (o pregunten).
 

Adjuntos

  • TrogloditaMatrixLed.ZIP
    290.3 KB · Visitas: 7
Gracias por tus comentarios.
Tengo previsto hacerle mejoras, como por ejemplo desplazar el texto. Además ahora está hecho al aire, sin meter dentro de una caja.
El MBI5026 es más o menos como el 74HC595 pero con 16 salidas y control de intensidad para no necesitar resistencias en los LEDs.
He aprovechado material que tenía, como por ejemplo los darlington TIP125, que al ser PNP me han complicado un poco el circuito.
 
Arriba