Mostrando entradas con la etiqueta arduino. Mostrar todas las entradas
Mostrando entradas con la etiqueta arduino. Mostrar todas las entradas

15 septiembre 2015

PinoLed. PINO DE NAVIDAD CON LED. Estudio de multiplexado de leds y sonido con Arduino.

(EN) LedTree. LED CHRISTMAS TREE. Study about sound and leds multiplexing with Arduino.
(FR) ArbreLed. ARBRE DE NOËL AVEC LEDS. Étude de multiplexage de leds et son avec Arduino.
(PT) ÁrvoreLed. ÁRVORE DE NATAL COM LEDS. Estudo de multiplexagem de led e som com Arduino.
------------------------------------------------------------------------------------------------------------------------------------ 


PARA REPASAR E INFORMARSE: Para este montaje usaré un Arduino Uno. Hay un montón de información en la web. Aquí un enlace a su página oficial. 

Básicamente se trata de manejar leds, así que es interesante tener claro como se polariza un led. En este blog ya se ha tratado el tema.

Mi idea ha sido con transistores como conmutadores. Así que es interesante tener una idea simple de como funcionan en corte y en saturación. Hay mucha información en la web. Aquí hay un ejemplo que me ha gustado. Más concretamente uso un array de transistores, el integrado ULN2003.



PLANTEAMIENTO INICIAL  Y MOTIVACIÓN DEL PROYECTO.

Con la proximidad de la navidad, allá por noviembre del 2014, comentamos  entre amiguetes makers, el montar un árbol de navidad con arduino. En principio, la idea de montar unos leds sobre una plaquita pcb en forma de pino, y encender y apagar, me pareció algo demasiado sencillo. Pero al poco tiempo caí en la idea de algo interesante:
Arduino tiene un número relativamente limitado de entradas y salidas (yo tengo un Arduino Uno; 14 digitales y 6 analógicas). Estas no pueden soportar más de 40mA cada una, y mejor no pasar de 20mA. Y en total no más de 300mA, todas juntas. Pensemos, intentando no pasar de 150 o 200mA, por si acaso.
Así que, imposible usar directamente más de 20 leds, y esta cantidad, con mucho cuidado.
¿Cómo hacer, entonces, para manejar más? Pues montarlos en una matriz, y no encenderlos todos al mismo tiempo. Una matriz que habrá que controlar continuamente filas y columnas y que si además sincronizamos con una musiquilla se convierte (para mi nivel al menos) en un ejercicio interesante de sincronismo y programación.
Además el propósito va a ser diseñar y montar yo el circuito. Activar filas y columnas parece una cosa factible con transistores...



MATRIZ. FILAS Y COLUMNAS.

En una red o matriz de leds podemos conectar una serie de filas a unas salidas del arduino, y una serie de columnas a otras. Si ponemos a nivel alto determinada salida alimentaremos positivamente toda una linea de leds (en nuestro dibujito las filas). De esos leds se encederán los que tengan su cátodo también a negativo o nivel bajo (en nuestro caso, la columna apropiada). Solo debemos cuidarnos de no activar más de una linea de leds al mismo tiempo para que no interfieran unas lineas con otras.

leds matrix
Graficamente se entiende mejor la activación o no de los leds en una matriz

En nuestro ejemplo (con lógica positiva), si activáramos a nivel alto dos filas, el activar una columna a nivel bajo encendería los dos leds de las diferentes filas. El truco es activar solo una fila, y las columnas apropiadas un instante y luego hacer lo mismo con otra fila. Todo lo suficientemente rápido como para que nuestro ojo no se de cuenta.



LIMITACIÓN DE LA CORRIENTE. Y USO DE TRANSISTORES.

Cada led debería tener una resistencia que limitase su corriente para que no se queme.
Y para no consumir demasiada corriente de las salidas de nuestro arduino, la primera idea es usar transistores que aislen las salidas del arduino respecto de la intensidad que consuman nuestros leds.
Así mi primera idea ha sido polarizar un par de transistores, que según estén sus bases, a nivel alto conducirán, o a nivel bajo estarán en corte.

Switch on led with transistors
El transistor en corte nos aisla de masa y conecta con tensión a través de la resistencia. En saturacíón conduce y nos conecta directamene a masa.



Una vez, así presentado el esquema,  es fácil caer en la cuenta de que podemos sacar la resistencia inferior y dejar el transistor conectado a colector abierto. Sólo nos interesa que el cátodo del diodo se conecte a masa o no.
Y de esta forma, la única resistencia que queda será la que nos limita la corriente por el diodo, de forma elemental.

Two transistors for a led
Cuando el led conduce e ilumina, el transistor superior "aisla" la masa, y el inferior la "une" por lo que el cirucuito se convierte en un sencillo "resistencia en serie con led".




EL INTEGRADO ULN2003A PARA NUESTRO MONTAJE.

 El integrado ULN2003A es un conjunto de resistencias, transistores y diodos. Se compone de siete circuitos iguales que en las hojas de características suelen venir representados como una puerta lógica NOT. Es básicamente un adaptador de potencia. Por la entrada nosotros introducimos una señal lógica TTL. Unas resistencias polarizan y atacan la base de un transistor tipo darlington (dos conectados para aumentar potencia) y esto nos da una salida negada (a masa si la entrada está a nivel alto, a colector de los transistores si la entrada es a nivel bajo)por la que puede pasar mucha más intensidad que por nuestros pines del arduino. Para simplificar y pensar electrónicamente tenemos nuestro transistor básico (pudiéndonos despreocupar de las adaptaciones de la señal ttl de nuestro arduino): un 0 o nivel bajo de nuestro arduino, nos pone a "nuestro" transistor en corte (o abierto colector-emisor) y por tanto aislado de masa; un 1 o nivel alto provocará a "nuestro" transistor a saturación (o cerrado colector-emisor) y por tanto conectado a masa.
También existen unos diodos, con un contacto en común, que no usaremos aquí, pensados para poner con selenoides, para cortocircuitar los transitorios de desconexión de las bobinas, generalmente de relés. Aquí no hay de eso, así que no los usamos.


PRUEBAS Y DISEÑO. 

Pues con estas ideas hacemos unas pruebas, para ver que nuestras ideas eléctricas funcionan. Y corregir errores y decisiones iniciales como la patilla común de los leds RGB, valores de las resistencias, etc.


LLevando a la práctica la teoría de nuestro PinoLed.


Y todas las conclusiones se han conviertido en el siguiente esquema definitivo

LedTree Diagram
Aquí está como ha quedado el esquema definitivo de mi PinoLed.

VALOR DE LAS RESISTENCIAS.

La corriente que circulará por la carga más importante (leds) la "recojo" por Vin y no por 5V por dos razones: para liberar de trabajo al regulador de tensión; y pensando en poder conectar con baterías diferentes y hacer que los leds alumbren más o menos.
Pensando, además, que hay leds de diferentes colores y tipos (finalmente he optado también por jugar con un par de leds RGB), y que éstos tienen diferentes umbrales de tensión a partir de los cuales conducen, la elección del valor de la resistencia limitadora de corriente ha sido un poco a prueba y error.
Originalmente, en el prototipo sobre protoboard he empezado con resistencias de 1K. Pensaba en una de las opciones menos favorables: 20v de entrada (Vin) y led rojo (tensión umbral aproximada 1,8v según pruebas propias) y curándome un poco en salud con la corriente (I diodo máx.<20ma aria-haspopup="true" class="goog-spellcheck-word" data-blogger-="" id=":1b.167" role="menuitem" span="" style="background: yellow;" tabindex="-1">escaped
-br="">
R=(Vin-Vd)/Idmax=(20-1,8)/0,020=910 ohmios.
Una vez, en fase de programación del Arduino, al multiplexar, el brillo de los leds baja notablemente porque la tensión sólo se aplica a cada linea 1/6 parte del tiempo. Por lógica he bajado el valor de las resistencias 1/6 parte (1000/6=166,66). Por seguridad mejor 180 que 150 ohmios.


DETALLES Y RESULTADOS DE LA CONSTRUCCIÓN.
Con la construcción no quería gastar demasiado dinero. Se trataba de un ejercicio. Así que he optado por cartulina y leds pequeños de 3mm. La circuitería está montada sobre un escudo casero del que ya he hablado en otra entrada. Hubiera sido más vistoso y luminoso usar leds grandes. Seguro que hay ideas mucho más trabajadas, pero esta es la mía...
En mi montaje, en el lugar del potenciometro para el volumen he puesto un puente, optando así por la potencia máxima, pero esto puede ser molesto para algunas personas así que indico la posibilidad de regular el volumen. Como altavoz uso un buzzer pasivo.
En la foto se puede ver, todavía sin acabar, mi idea original, con una pila de 9v. Pero, aunque válida, no ha resultado ser una opción demasiado buena. Esto requiere una interesante explicación que comentaré en los siguientes párrafos. De momento unas fotos para hacerse una idea:
 
LedTree assembly
Idea del proceso de montaje y detalles del PinoLed



PinoLed Photo
Una foto del PinoLed durante las pruebas




Y el video, un poco cutre (también en YouTube), da una pequeña muestra del resultado final de mi ejercicio, bautizado como PinoLed.



APUNTES SOBRE LA PROGRAMACIÓN.

También dejo por aquí a la vista mi programa del arduino. Probablemente muy simple y poco elegante para los muy iniciados. Pero espero que útil para aprender a los que, como yo, no lo son tanto.  Fue desarrollado y probado con un Arduino Uno R3 y con el IDE de programación 1.6.1.
Básicamente se trata de la función "Visualizar()" que está constantemente mostrando el brillo de cada led que la matriz "diodoFilCol[7][7]" guarda en sus índices (0 apagado, 255 brillo máximo).
Estos valores del brillo de cada led se van modificando según una "secuencia()" temporal para que coincida con la música también definida en esta función.
Para que no sea demasiado repetitivo, de forma medio chapucera (en honor al nombre del blog), he creado un par de posibles estados en donde, sin música, las luces hacen un par de efectos aleatorios: uno jugando con encender y apagar leds al azar; otro jugando con subir y bajar el brillo de diferentes colores. De manera más o menos accidental puede volverse al estado inicial de secuencia leds-música, y si no, al cabo de un tiempo mínimo,  de manera forzada (establecido ahora para las pruebas en 60 segundos) .
Otras funciones organizan de forma predefinida efectos simples de luces para simplificar la repetición en la programación.

Por si alguien se anima a probar, dejo aquí una copia del archivo .ino definitivo comprimido en zip.

Y para quien solo quiera curiosear y echar un vistazo rápido, aquí está el código.




////////////////////////////////////////////////
// Para hacer funcionar PinoLed               //
// Matriz de 36 leds y un buzzer              //
// http//:electronicaychapuzas.blogspot.com.es//
// by Sancos v1.1                             //
////////////////////////////////////////////////

byte diodoFilCol[7][7];   //definira estado diodos:0 apagado 255 encendido
// fila y columna define que pines uso.No usamos fila0 ni columna0
byte fila[7]={255,15,14,2,4,7,8};     //No usare fila0. 14=A0, 15=A1
byte columna[7]={255,3,5,6,9,10,11};  //No usare columna0
//Podría usar pwm por comodidad. Por eso utilizo estas salidas.
//Pero para poder usar Tone() sin problemas no usaré PWM
//Ademas, después de montado el circuito, he caido en que hubiera
//sido mas interesante el PWM en las filas.
int v=2; //Velocidad tempo
int i,c,n,f,luz;   //Contadores uso general
int estado; //Que estamos haciendo? 0-inicio; 1-secuencia;
unsigned long tiempo0,tiempo;   //Contadores tiempo
short Do2,Si2,Do3,Re3,Mi3,Fa3,Sol3,La3,Si3,Do4;  //Notas que vamos a usar
void setup()
{
  // Filas. Activas a nivel bajo. Reposo a nivel alto.
  for (f=1; f<7; f=f+1)
  {
    pinMode(fila[f],OUTPUT);
    digitalWrite(fila[f],HIGH);
  }
  
  // Audio implementado por pin 12.
  //señal de audio. Reposo a nivel bajo
  pinMode(12,OUTPUT);
  digitalWrite(12,LOW);
  
  // Notas
    Do2=130.813;
    Si2=246.942;
    Do3=261.626;
    Re3=293.665;
    Mi3=329.628;
    Fa3=349.228;
    Sol3=391.995;
    La3=440;
    Si3=493.883;
    Do4=523.251;
  
  //Columnas. Activas a nivel alto. Reposo a nivel bajo.
  for (c=1; c<7; c=c+1)
  {
    pinMode(columna[c],OUTPUT);
    digitalWrite(columna[c],LOW);
  }
  
  //Inicializamos el estado de cada led a apagado.
  //La matriz diodoFilCol define cada diodo
  // desde 0 (apagado) a 255 (brillo maximo)
  for (f=1; f<7; f=f+1)
  {
    for (c=0; c<7; c=c+1)
    {
      diodoFilCol[f][c]=0;
    }
    
 }
 
  tone(12,440,1000);   //Test al inicio
  delay(5000);  //Esperamos unos segundos antes de arrancar
  tiempo0=millis();   //Tomamos tiempo inicial para temporizaciones
  estado=1;
  
}



void loop()
{
  if (estado==0) Test();  //Test al inicio
  if (estado==1) Secuencia();  //Secuencia programada con musica
  if (estado==2) Luces();  //Luces al azar y silencio
  if (estado==3) Rgb();
  Visualizar();
}

// Visualizar la matriz diodoFilCol.
void Visualizar()
{
  for (c=1; c<7; c=c+1)  //Repetir con las seis columnas
  {
  digitalWrite(columna[c],HIGH);  //Activar columna
    for (n=1; n<255; n=n+4)   //Pasos lo mas cortos posible sin que parpadeen los leds
    {
      for (f=1; f<7; f=f+1)  //Activar  las filas según brillo en matriz
      {
        //Activamos o no más veces según numero en matriz mas alto
        if ((255-n)<(diodoFilCol[f][c])) digitalWrite(fila[f],LOW);   
        else digitalWrite(fila[f],HIGH);     
      }
    }
    digitalWrite(columna[c],LOW);  //Desactivar columna
    
    for (f=1; f<7; f=f+1)    //Desactivar filas
    {
      digitalWrite(fila[f],HIGH);
    } 
  }
}




/********************/
/* Efectos de luces */
/********************/


// Apagar todos los leds (0), o encender(255)
void Todos(int brillo)
{
  
   for (f=0; f<7; f=f+1)
  {
    for (c=0; c<7; c=c+1)
    {
      diodoFilCol[f][c]=brillo;
    }
    
  }
}


// Iluminar un led en concreto.
void Led(int fil,int col,int brillo)
{
  diodoFilCol[fil][col]=brillo;
  
}


// Iluminar una columna en concreto.
void Columna(int col, int brillo)
{
  for (c=0;c<7;c=c+1)
  {
    diodoFilCol[c][col]=brillo;
  }
}


//Iluminar una fila en concreto.
void Fila(int fil, int brillo)
{
  for (c=0;c<7;c=c+1)
  {
    diodoFilCol[fil][c]=brillo;
  }
}



//Iluminar todos rojos
void Rojos(int brillo)
{
  Led(1,1,brillo);
  Led(1,4,brillo);
  Led(2,3,brillo);
  Led(2,6,brillo);
  Led(3,2,brillo);
  Led(3,5,brillo);
  Led(4,2,brillo);
  Led(4,5,brillo);
  Led(5,2,brillo);
  Led(5,5,brillo);
  Led(6,2,brillo);
  Led(6,5,brillo);
}

//Iluminar todos verdes
void Verdes(int brillo)
{
  Led(1,2,brillo);
  Led(1,5,brillo);
  Led(2,2,brillo);
  Led(2,5,brillo);
  Led(3,3,brillo);
  Led(3,6,brillo);
  Led(4,3,brillo);
  Led(4,6,brillo);
  Led(5,3,brillo);
  Led(5,6,brillo);
  Led(6,3,brillo);
  Led(6,6,brillo);
}

//Iluminar todos amarillos
void Amarillos(int brillo)
{
  //Para amarillo 180 de rojo y 255 de verde
  //porque el led rojo alumbra más con menos tension
  int correccionbrillo;
  correccionbrillo=((brillo*180)/255);
  
  Led(1,1,correccionbrillo); Led(1,2,brillo); Led(1,3,0);
  Led(1,4,correccionbrillo); Led(1,5,brillo); Led(1,6,0);
  Led(2,1,brillo);
  Led(2,4,brillo);
  Led(3,1,brillo);
  Led(3,4,brillo);
  Led(4,1,brillo);
  Led(4,4,brillo);
  Led(5,1,brillo);
  Led(5,4,brillo);
  Led(6,1,brillo);
  Led(6,4,brillo);
}


void AzarLed(int cuantas, int pausa)  //Cuantas luces encendemos; cada una  pausa ms
{                                  // tiempo aprox. funcion = cuantas * pausa
  long int filaazar, columnaazar;
  Todos(0);
  randomSeed(analogRead(3)); //El pin A3 está al aire
  for (i=0; i<cuantas; i=i+1);
  {
    filaazar=random(1,7);
    columnaazar=random(1,7);
    Led(filaazar,columnaazar,255);
    delay(pausa);
  }
}

void AzarFila(int cuantas, int pausa) //Cuantas filas encendemos cada pausa ms
{                                     //tiempo aprox.funcion=cuantas*pausa
  long int filaazar;
  Todos(0);
  randomSeed(analogRead(3));  //A3 al aire
  for (i=0; i<cuantas; i=i+1);
  {
    filaazar=random(1,7);
    Fila(filaazar,255);
    delay(pausa);
  }
}

void AzarColumna(int cuantas, int pausa) //Cuantas columnas encendemos cada pausa ms
{                                     //tiempo aprox.funcion=cuantas*pausa
  long int columnaazar;
  Todos(0);
  randomSeed(analogRead(3));  //A3 al aire
  for (i=0; i<cuantas; i=i+1);
  {
    columnaazar=random(1,7);
    Columna(columnaazar,255);
    delay(pausa);
  }
}


/***********************/
/*     Secuencias      */
/***********************/

void Test()
{
  tiempo=(millis()-tiempo0);
  
    if (tiempo<1000)
      {
      Todos(255);
      }
    if ((tiempo>=1000)&&(tiempo<2000))
      {
      Todos(0);  
      Led(1,1,255); Led(1,4,255); //Rojo
      }
    if ((tiempo>=2000)&&(tiempo<3000))
      {
      Todos(0);
      Led(1,1,0); Led(1,4,0);  
      Led(1,2,255); Led(1,5,255);  //Verde
      }
    if ((tiempo>=3000)&&(tiempo<4000))
      {
      Todos(0);  
      Led(1,2,0); Led(1,5,0);  
      Led(1,3,255); Led(1,6,255);  //Azul
      }
    if ((tiempo>=4000)&&(tiempo<4500))
      {
      Todos(0);  
      Led(1,3,200); Led(1,6,200); //Apagando azul
      Led(1,5,5); //Encendiendo verde
      }
    if ((tiempo>=4500)&&(tiempo<5000))
      {
      Todos(0);  
      Led(1,3,100); Led(1,6,100); //Apagando azul
      Led(1,15,5); //Encendiendo verde
      }
    if ((tiempo>=5000)&&(tiempo<5500))
      {
      Todos(0);  
      Led(1,3,50); Led(1,6,50); //Apagando azul
      Led(1,5,50); //Encendiendo verde
      }
    if ((tiempo>=5500)&&(tiempo<6000))
      {
      Todos(0);  
      Led(1,3,25); Led(1,6,25); //Apagando azul
      Led(1,5,100); //Encendiendo verde
      }  
    if ((tiempo>=6000)&&(tiempo<6500))
      {
      Todos(0);  
      Led(1,3,15); Led(1,6,15); //Apagando azul
      Led(1,5,200); //Encendiendo verde
      }
    if ((tiempo>=6500)&&(tiempo<7000))
      {
      Todos(0);  
      Led(1,3,0); Led(1,6,0); //Apagando azul
      Led(1,5,255); //Encendiendo verde
      }
      
    if ((tiempo>=7000))
      {
      estado=1;
      tiempo0=millis();
      }
}


//Luces al azar para reposo
void Luces()
{
  long int quehacer,fil,col;
  randomSeed(analogRead(3));  //A3 al aire
  quehacer=random(1,100);
  fil=random(1,7);
  col=random(1,7);
  if (quehacer<3) Todos(0);
  if ((quehacer>=5)&&(quehacer<45)) Led(fil,col,255); 
  if ((quehacer>=45)&&(quehacer<85)) Led(fil,col,0);
  if (quehacer==85) estado=3; //ejecuta Rgb()
  if ((quehacer>85)&&(estado<89)) Fila(fil,255);
  if ((quehacer>89)&&(estado<94)) Columna(col,255);
  if ((quehacer>99)) { estado=1; tiempo0=millis(); }
  if ((millis()-tiempo0)>60000){ estado=1; tiempo0=millis(); } //aqui aseguro musica cada 60 segundos
}

//Led de colores
void Rgb()
{
  long int quehacer; //luz interesa global
  randomSeed(analogRead(3));  //A3 al aire
  quehacer=random(1,100);
  if (quehacer>60) luz=luz+1;
  if (luz>250) luz=luz-1;
  if ((luz>200)&&(quehacer>95)) luz=0;
  if (quehacer>98) {luz=0; estado=2;} //volver a luces()
  if (quehacer<33) Rojos(luz);
  if ((quehacer>33)&&(quehacer<66)) Verdes(luz);
  if ((quehacer>66)&&(quehacer<98)) Amarillos(luz);
  if ((quehacer>99)&&(luz>200)) { estado=1; tiempo0=millis(); }
}

//Programar secuencia a repetir

void Secuencia()

{
  tiempo=(millis()-tiempo0);
  
  /***************    MUSICA     ******************/
  //Compas1
    if (tiempo<(500/v))
      {
      if (tiempo<(400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(500/v))&&(tiempo<(1000/v)))
      {
      if (tiempo<(900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(1000/v))&&(tiempo<(2000/v)))
      {
      if (tiempo<(1750/v)) tone(12,Mi3,(100/v));
      }
      
      
    if ((tiempo>=(2000/v))&&(tiempo<(2500/v)))
      {
      if (tiempo<(2400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(2500/v))&&(tiempo<(3000/v)))
      {
      if (tiempo<(2900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(3000/v))&&(tiempo<(4000/v)))
      {
      if (tiempo<(3750/v)) tone(12,Mi3,(100/v));
      }
      
      
    //Compas2   
    if ((tiempo>=(4000/v))&&(tiempo<(4500/v)))
      {
      if (tiempo<(4400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(4500/v))&&(tiempo<(5000/v)))
      {
      if (tiempo<(4900/v)) tone(12,Sol3,(100/v));
      }
    if ((tiempo>=(5000/v))&&(tiempo<(5750/v)))
      {
      if (tiempo<(5600/v)) tone(12,Do3,(100/v));
      }
    if ((tiempo>=(5750/v))&&(tiempo<(6000/v)))
      {
      if (tiempo<(5900/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(6000/v))&&(tiempo<(8000/v)))
      {
      if (tiempo<(6500/v)) tone(12,Mi3,(100/v));
      }  
      
      
    //Compas3
    if ((tiempo>=(8000/v))&&(tiempo<(8500/v)))
      {
      if (tiempo<(8400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(8500/v))&&(tiempo<(9000/v)))
      {
      if (tiempo<(8900/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(9000/v))&&(tiempo<(9750/v)))
      {
      if (tiempo<(9700/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(9750/v))&&(tiempo<(10000/v)))
      {
      if (tiempo<(9900/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(10000/v))&&(tiempo<(10500/v)))
      {
      if (tiempo<(10400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(10500/v))&&(tiempo<(11000/v)))
      {
      if (tiempo<(10900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(11000/v))&&(tiempo<(11500/v)))
      {
      if (tiempo<(11400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(11500/v))&&(tiempo<(11750/v)))
      {
      if (tiempo<(11700/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(11750/v))&&(tiempo<(12000/v)))
      {
      if (tiempo<(11900/v)) tone(12,Mi3,(100/v));
      } 
     
     
   //Compas4
    if ((tiempo>=(12000/v))&&(tiempo<(12500/v)))
      {
      if (tiempo<(12400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(12500/v))&&(tiempo<(13000/v)))
      {
      if (tiempo<(12900/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(13000/v))&&(tiempo<(13500/v)))
      {
      if (tiempo<(13400/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(13500/v))&&(tiempo<(14000/v)))
      {
      if (tiempo<(13900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(14000/v))&&(tiempo<(15000/v)))
      {
      if (tiempo<(14900/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(15000/v))&&(tiempo<(15750/v)))
      {
      if (tiempo<(15700/v)) tone(12,Sol3,(100/v));
      }
    if ((tiempo>=(15750/v))&&(tiempo<(16000/v)))
      {
      if (tiempo<(15700/v)) tone(12,Do2,(100/v));
      }
     
     
    //Compas5
    if ((tiempo>=(16000/v))&&(tiempo<(16500/v)))
      {
      if (tiempo<(16400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(16500/v))&&(tiempo<(17000/v)))
      {
      if (tiempo<(16900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(17000/v))&&(tiempo<(18000/v)))
      {
      if (tiempo<(17750/v)) tone(12,Mi3,(100/v));
      }
      
      
    if ((tiempo>=(18000/v))&&(tiempo<(18500/v)))
      {
      if (tiempo<(18400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(18500/v))&&(tiempo<(19000/v)))
      {
      if (tiempo<(18900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(19000/v))&&(tiempo<(20000/v)))
      {
      if (tiempo<(19750/v)) tone(12,Mi3,(100/v));
      }
      
     
    
    //Compas6
     if ((tiempo>=(20000/v))&&(tiempo<(20500/v)))
      {
      if (tiempo<(20400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(20500/v))&&(tiempo<(21000/v)))
      {
      if (tiempo<(20900/v)) tone(12,Sol3,(100/v));
      }
    if ((tiempo>=(21000/v))&&(tiempo<(21750/v)))
      {
      if (tiempo<(21700/v)) tone(12,Do3,(100/v));
      }
    if ((tiempo>=(21750/v))&&(tiempo<(22000/v)))
      {
      if (tiempo<(21900/v)) tone(12,Re3,(100/v));
      }
    if ((tiempo>=(22000/v))&&(tiempo<(24000/v)))
      {
      if (tiempo<(22500/v)) tone(12,Mi3,(100/v));
      }  
     
      
    //Compas7
    if ((tiempo>=(24000/v))&&(tiempo<(24500/v)))
      {
      if (tiempo<(24400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(24500/v))&&(tiempo<(25000/v)))
      {
      if (tiempo<(24900/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(25000/v))&&(tiempo<(25750/v)))
      {
      if (tiempo<(25700/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(25750/v))&&(tiempo<(26000/v)))
      {
      if (tiempo<(25900/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(26000/v))&&(tiempo<(26500/v)))
      {
      if (tiempo<(26400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(26500/v))&&(tiempo<(27000/v)))
      {
      if (tiempo<(26900/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(27000/v))&&(tiempo<(27500/v)))
      {
      if (tiempo<(27400/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(27500/v))&&(tiempo<(27750/v)))
      {
      if (tiempo<(27700/v)) tone(12,Mi3,(100/v));
      }
    if ((tiempo>=(27750/v))&&(tiempo<(28000/v)))
      {
      if (tiempo<(27900/v)) tone(12,Mi3,(100/v));
      } 
      
      
    //Compas8
    if ((tiempo>=(28000/v))&&(tiempo<(28500/v)))
      {
      if (tiempo<(28400/v)) tone(12,La3,(100/v));
      } 
    if ((tiempo>=(28500/v))&&(tiempo<(29000/v)))
      {
      if (tiempo<(28900/v)) tone(12,Sol3,(100/v));
      }
    if ((tiempo>=(29000/v))&&(tiempo<(29500/v)))
      {
      if (tiempo<(29400/v)) tone(12,Fa3,(100/v));
      }
    if ((tiempo>=(29500/v))&&(tiempo<(30000/v)))
      {
      if (tiempo<(29900/v)) tone(12,Re3,(100/v));
      } 
    if ((tiempo>=(30000/v))&&(tiempo<(30500/v)))
      {
      if (tiempo<(30400/v)) tone(12,Do3,(100/v));
      }  
    if ((tiempo>=(30500/v))&&(tiempo<(32000/v)))
      {
      if (tiempo<(31900/v)) noTone(12);
      }
    
    
    
  /*****************   LUCES    *******************/
  //Compas1
    if (tiempo<(500/v))
      {
        //Mi3
        Todos(0);
        Rojos(255);
      }
    if ((tiempo>=(500/v))&&(tiempo<(1000/v)))
      {
        //Mi3
        Todos(0);
        Amarillos(255);
      }
    if ((tiempo>=(1000/v))&&(tiempo<(2000/v)))
      {
        //Mi3
        Todos(0);
        Verdes(255);
      }
      
      
    if ((tiempo>=(2000/v))&&(tiempo<(2500/v)))
      {
        //Mi3
        Todos(0);
        Rojos(255);
      }
    if ((tiempo>=(2500/v))&&(tiempo<(3000/v)))
      {
        //Mi3
        Amarillos(255);
      }
    if ((tiempo>=(3000/v))&&(tiempo<(4000/v)))
      {
        //Mi3
        Verdes(255);
      }
      
      
  //Compas2   
    if ((tiempo>=(4000/v))&&(tiempo<(4500/v)))
      {
        //Mi3
        Fila(1,0);
        Fila(2,0);
      }
    if ((tiempo>=(4500/v))&&(tiempo<(5000/v)))
      {
        //Sol3
        Fila(3,0);
      }
    if ((tiempo>=(5000/v))&&(tiempo<(5750/v)))
      {
        //Do3
        Fila(4,0);
      }
    if ((tiempo>=(5750/v))&&(tiempo<(6000/v)))
      {
        //Re3
        Fila(5,0);
      }
    if ((tiempo>=(6000/v))&&(tiempo<(6500/v)))
      {
        //Mi3
        Fila(6,0);
      }  
    if ((tiempo>=(6500/v))&&(tiempo<(7000/v)))
      {
        //Silencio
        Led(1,6,255);
      }
    if ((tiempo>=(7000/v))&&(tiempo<(7500/v)))
      {
        //Silencio  
      } 
    if ((tiempo>=(7500/v))&&(tiempo<(8000/v)))
      {
        //Silencio
        Led(1,6,0);
        Led(1,4,180); Led(1,5,255);
      }    
  
      
      
  //Compas3
    if ((tiempo>=(8000/v))&&(tiempo<(8500/v)))
      {
        //Fa3
        Todos(0);
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(8500/v))&&(tiempo<(9000/v)))
      {
        //Fa3
        Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
        Fila(1,0);Fila(2,0); 
      }
    if ((tiempo>=(9000/v))&&(tiempo<(9500/v)))  //Varia de nota
      {
        //Fa3
        Columna(1,0); Columna(2,30);Columna(3,255);Columna(4,30);Columna(5,0);Columna(6,125);
        Fila(1,0);Fila(2,0); 
      }
    if ((tiempo>=(9500/v))&&(tiempo<(10000/v)))  //Varia de nota
      {
        //Fa3
        Columna(1,125); Columna(2,0);Columna(3,30);Columna(4,255);Columna(5,30);Columna(6,0);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(10000/v))&&(tiempo<(10500/v)))
      {
        //Fa3
        Columna(1,0); Columna(2,125);Columna(3,0);Columna(4,30);Columna(5,255);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(10500/v))&&(tiempo<(11000/v)))
      {
        //Mi3
        Columna(1,30); Columna(2,0);Columna(3,125);Columna(4,0);Columna(5,30);Columna(6,255);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(11000/v))&&(tiempo<(11500/v)))
      {
        //Mi3
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(11500/v))&&(tiempo<(11750/v)))
      {
        //Mi3
        Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(11750/v))&&(tiempo<(12000/v)))
      {
        //Mi3
      
      } 
     
     
 //Compas4
    if ((tiempo>=(12000/v))&&(tiempo<(12500/v)))
      {
        //Mi3
        Columna(1,0); Columna(2,30);Columna(3,255);Columna(4,30);Columna(5,0);Columna(6,125);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(12500/v))&&(tiempo<(13000/v)))
      {
        //Re3
        Columna(1,125); Columna(2,0);Columna(3,30);Columna(4,255);Columna(5,30);Columna(6,0);
        Fila(1,0);Fila(2,0);  
      }
    if ((tiempo>=(13000/v))&&(tiempo<(13500/v)))
      {
        //Re3
        Columna(1,0); Columna(2,125);Columna(3,0);Columna(4,30);Columna(5,255);Columna(6,30);
        Fila(1,0);Fila(2,0); 
      }
    if ((tiempo>=(13500/v))&&(tiempo<(14000/v)))
      {
        //Mi3
        Columna(1,30); Columna(2,0);Columna(3,125);Columna(4,0);Columna(5,30);Columna(6,255);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(14000/v))&&(tiempo<(15000/v)))
      {
        //Re3
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
        Fila(1,0);Fila(2,0);
        
        
      }
    if ((tiempo>=(15000/v))&&(tiempo<(15750/v)))
      {
        //Sol3
        Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
        Fila(1,0);Fila(2,0);
        Led(1,1,255);
      }
    if ((tiempo>=(15750/v))&&(tiempo<(16000/v)))
      {
        //Do2
        Todos(0);
        Led(1,1,255);
      }
     
     
  //Compas5
    if ((tiempo>=(16000/v))&&(tiempo<(16500/v)))
      {
        //Mi3
        Todos(0);
        Rojos(255);
      }
    if ((tiempo>=(16500/v))&&(tiempo<(17000/v)))
      {
        //Mi3
         Todos(0);
        Amarillos(255);
      }
    if ((tiempo>=(17000/v))&&(tiempo<(18000/v)))
      {
        //Mi3
        Todos(0);
        Verdes(255);
      }
      
      
    if ((tiempo>=(18000/v))&&(tiempo<(18500/v)))
      {
        //Mi3
        Todos(0);
        Rojos(255);
      }
    if ((tiempo>=(18500/v))&&(tiempo<(19000/v)))
      {
        //Mi3
         Amarillos(255);
      }
    if ((tiempo>=(19000/v))&&(tiempo<(20000/v)))
      {
        //Mi3
        Verdes(255);
      }
      
     
    
    //Compas6
     if ((tiempo>=(20000/v))&&(tiempo<(20500/v)))
      {
        //Mi3
        Fila(6,0);
      }
    if ((tiempo>=(20500/v))&&(tiempo<(21000/v)))
      {
        //Sol3
        Fila(5,0);
      }
    if ((tiempo>=(21000/v))&&(tiempo<(21750/v)))
      {
        //Do3
        Fila(4,0);
      }
    if ((tiempo>=(21750/v))&&(tiempo<(22000/v)))
      {
        //Re3
        Fila(3,0);
      }
    if ((tiempo>=(22000/v))&&(tiempo<(24000/v)))
      {
        //Mi3
        Fila(2,0);
        Fila(1,0);
      }  
     
      
    //Compas7
    if ((tiempo>=(24000/v))&&(tiempo<(24500/v)))
      {
        //Fa3
        Todos(0);
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(24500/v))&&(tiempo<(25000/v)))
      {
        //Fa3
        Columna(1,30); Columna(2,0);Columna(3,125);Columna(4,0);Columna(5,30);Columna(6,255);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(25000/v))&&(tiempo<(25500/v)))  //No coincide con nota
      {
        //Fa3
        Columna(1,0); Columna(2,125);Columna(3,0);Columna(4,30);Columna(5,255);Columna(6,30);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(25500/v))&&(tiempo<(26000/v)))  //No coincide con nota
      {
        //Fa3
        Columna(1,125); Columna(2,0);Columna(3,30);Columna(4,255);Columna(5,30);Columna(6,0);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(26000/v))&&(tiempo<(26500/v)))
      {
        //Fa3
        Columna(1,0); Columna(2,30);Columna(3,255);Columna(4,30);Columna(5,0);Columna(6,125);
        Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(26500/v))&&(tiempo<(27000/v)))
      {
         //Mi3
         Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(27000/v))&&(tiempo<(27500/v)))
      {
        //Mi3
        Columna(1,255); Columna(2,30);Columna(3,0);Columna(4,125);Columna(5,0);Columna(6,30);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(27500/v))&&(tiempo<(27750/v)))
      {
        //Mi3
         Columna(1,30); Columna(2,0);Columna(3,125);Columna(4,0);Columna(5,30);Columna(6,255);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(27750/v))&&(tiempo<(28000/v)))
      {
        //Mi3
        Columna(1,0); Columna(2,125);Columna(3,0);Columna(4,30);Columna(5,255);Columna(6,30);
        Fila(1,0);Fila(2,0);
      } 
      
      
    //Compas8
    if ((tiempo>=(28000/v))&&(tiempo<(28500/v)))
      {
        //La3
        Columna(1,125); Columna(2,0);Columna(3,30);Columna(4,255);Columna(5,30);Columna(6,0);
        Fila(1,0);Fila(2,0);
      } 
    if ((tiempo>=(28500/v))&&(tiempo<(29000/v)))
      {
        //Sol3
        Columna(1,0); Columna(2,30);Columna(3,255);Columna(4,30);Columna(5,0);Columna(6,125);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(29000/v))&&(tiempo<(29500/v)))
      {
        //Fa3
        Columna(1,30); Columna(2,255);Columna(3,30);Columna(4,0);Columna(5,125);Columna(6,0);
         Fila(1,0);Fila(2,0);
      }
    if ((tiempo>=(29500/v))&&(tiempo<(30000/v)))
      {
        //Re3
        Todos(0);
      } 
    if ((tiempo>=(30000/v))&&(tiempo<(30500/v)))
      {
        //Do3
        Todos(255);
      }  
    if ((tiempo>=(30500/v))&&(tiempo<(32000/v)))
      {
        //Tres silencios corchea
      if (tiempo<(31900/v)) noTone(12);
      }
    if (tiempo>=(32000/v))
      {
        tiempo0=millis();
        estado=2; //ejecuta Luces()
      }
}



INTERESANTE PROBLEMILLA NO PREVISTO CON PILA DE 9V Y EXCESIVO CONSUMO.

Al final, casi siempre hay algo que podría haber estado mejor pensado. Y aquí he caído en la cuenta, cuando ya todo estaba demasiado montado.
Mi idea original era alimentar todo con una pila de 9v oculta debajo del pino de cartulina. Y en principio todo parece bien pensado, pero haciendo pruebas y test durante la programación me he encontrado con dos detalles:

Con la batería  todo va bien unos minutos, pero enseguida el arduino se apaga y enciende (el regulador de 5v necesita más de 7 en Vin). Cosa que si alimento con el usb (aunque usamos menos tensión), todo parece ir mejor.

Y originalmente las resistencias eran de menos potencia pero se calentaban mucho más de lo deseable. Tuve que montarlas de más watios. Así que tenía que haber exceso de consumo por algún sitio...

Repensando sobre el esquema del circuito ya montado me he dado cuenta que cuando los leds están apagados porque la fila está desactivada, la resistencia limitadora conduce de Vin a masa.  Así que todas las lineas conducen y consumen corriente. Incluso más en reposo.
Esto, aunque no llega a los límites del arduino porque es a través de los transistores, sí que agota la batería demasiado rápido. Las baterías de 9v tampoco son una maravilla en capacidad de carga.
Y si pensamos en una alimentación alta para que los leds alumbren mejor. Más allá de los 15v nos vamos a consumos peligrosos de más de 500mA ((15v/180ohm)*6 resistencias=0,5A).

Así que la idea de alimentarlo con baterías funciona, pero nos saldrá cara. Habrá que fijarse más en esta cuestión para otros proyectos.

Pero en este punto ha quedado éste.


24 junio 2011

Como hacer funcionar un display spi VFD (Vacuum Fluorescent Display) Pinguino (o arduino)+16LF01UA3






PARA REPASAR: Datasheet del display 16LF01UA3 (lo he consiguido en pdf buscando en la web por "16lf01ua3 untitle"); Pinguino Pic (lo mismo que arduino pero con microcontrolador de Microchip del que he comentado en una entrada anterior, software libre y hardware libre fácil de construir, que puedes ver si buscas en la web "pinguino pic hackinglab", fíjate expecialmente en el tutorial; conocimientos básicos de programación en C, tener un Manual de progamción Arduino que puedes también descargar en pdf desde la red para conocer que ordenes de c se pueden usar con arduino.



Esta vez se trata de hacer funcionar un display alfanumerico VFD (Display Fluorescente de Vacio). He conseguido alguno de despieces, y he encontrado documentación de éste en internet (16LF01UA3). Mi idea original es tratar de imitar un luminoso publicitario que pase continuamente un mensaje de derecha a izquierda.
Con una ojeada a la hoja de características del display Samsumg 16LF01UA3 nos hacemos con los pines y una idea de su funcionamiento.

Pin1 y pin2: Vcc(+5v).
Pin3,4,5,6,7: No conectados. El 3 falta como "llave" que impida conectar al revés.
Pin8: SCLK Activa por flanco de bajada el bit que señale DATA.
Pin9: DATA El bit (0 ó 1) se va añadiendo a un registro cada vez que SCLK baja de 1 a 0 hasta que forma un byte (8 bits) como código de control del display.
Pin10: /RST A nivel bajo activa el reset de todo el display.
Pin11 y pin12: GND


Así que tendremos que alimentar con 5 voltios al display (pines1-2, 11-12), y el "Pinguino" (placa similar a Arduino) que usaremos para manejar los tres pines: SCLK, DATA, y RESET.

Monto todo sobre una protoboard. En la programación del Pinguino "definiremos" como salidas: la 7 para el SCLK, la 6 para el RESET, y la 5 para el DATA.






Como mi experiencia con el pinguino es mínima, y aquí se trata de aprender, no me preocuparé de librerias previas, ni aprovecharé lo que hayan hecho otros. Programaremos "como los hombres", a pelo, usando las ordenes más elementales y genéricas para ir montando nuestras funciones... Creo que he descrito ampliamente con notas en el código pero aclararé como he ido planteando el asunto.
El display se maneja, basicamente, "emitiendo" secuencias de bits, que en grupos de 8 forman "palabras" que el display va entendiendo de determinada manera. Los códigos vienen en la hoja de características del display. El display alamacenará un bit cuando la señal SCLK pase de nivel alto (5v) a bajo (0v) y ese bit será el que en ese momento tenga el pin DATA. La señal /RESET deberá permanecer a nivel alto. A nivel bajo reseteará todo y tendremos que volver a empezar.
Lo primero ha sido la funcion sclkfall() que hace que la señal de SCLK pase a nivel bajo y por tanto el display "acepte" lo que hay en DATA. Y luego, usando ésta defino l() para meter un uno (5v), y o() para "meter" un cero (0v). OJO: l() parece un uno pero es una "L"minúscula, y o() parece un cero pero es una "O"minúscula. Uso el parecido porque no puedo hacer una función que empiece por un número y de esta forma , a posteriori es muy cómodo, porque es fácil ver el byte que queiro enviar en cada momento. Luego todo es enviar bytes. ¿Cúal?, se sabe mirando la hoja de características, aunque voy explicando en el código.


// Probando display vfd 16LF01UA3

// Usaremos un pinguino con pic18f2550
// Anadir la linea siguiente si es con pic18f4550
// #define PIC18F4550


#define SCLK 7 //manejaremos SCLK por pin7
#define RST 6 // /RST por pin6
#define DATA 5 // DATA por pin5

int colocarPuntero(int En);//Prototipado porque pasa valor
////////////////////////////////////////////////////////////
// TEXTO PARA EL DISPLAY en modo scroll AQUI
char Mensaje[100]="Jueves, 16 de junio ";// 3" "al final
int posicionUltLetra=22;
// TAMAÑO ARRAY Mensaje[x] DEBE SER MAYOR QUE EL NUMERO DE CARACTERES
// posicionUltLetra INDICARA EL NUMERO DE CARACTERES.
////////////////////////////////////////////////////////////
int scroll=1;// 1=>Desplazamiento continuo del texto izq-der
// 0=>No se controla el desplazamiento.
// 2=>Prueba (poniendo codigo en su sitio) cosas nuevas
////////////////////////////////////////////////////////////

char digDisplay[17]=" ";// 17 espacios
//Representa los caracteres en cada
// posicion del display, usaremos [1...16]
// uno por cada digito. El [0] no lo usamos
int posicionMensaje,i;
//No.1er.caracter escribir en display
// i contador que no puedo definir en loop()


/////////////////////////////////////////////
void setup()
{
pinMode(SCLK,OUTPUT); //Todas seran salidas hacia
pinMode(RST,OUTPUT); //el display
pinMode(DATA,OUTPUT);

resetdisplay(); // La primera vez reseteamos display
numdigit(); // configuramos el numero de digitos
brillo(); // la intensidad de los digitos
BorrarDisplay(); // Borrar
posicionMensaje=0;//Por donde empezaremos a leer el Mensaje[]
}

void loop()
{

if(scroll==1)
{
// Guardamos en cada digDisplay[i] el caracter que le toca a ese digito
// Pretendemos que el texto corra de derecha a izquierda continuamente
for(i=1;i<16;i++)
{
digDisplay[i]=digDisplay[i+1];
//Cada digito corre de derecha a izquierda
}
digDisplay[16]=Mensaje[posicionMensaje];
//El ultimo digito de la derecha es el nuevo que leemos de Mensaje[]

if (posicionMensaje==posicionUltLetra)
// Si era el ultimo caracter hay que volver a leer por el principio
{
posicionMensaje=0;
}
else
// Si todavia no llegamos preparamos para leer el siguiente
{
posicionMensaje=posicionMensaje+1;
//La siguiente vez leeremos el caracter siguiente de Mensaje[]
}



// Representamos cada caracter en su digDisplay
for(i=1;i<17;i++)
{
colocarPuntero(i);
switch(digDisplay[i])
{
case '@':Arroba();break;
case 'A':case 'a':A();break;
case 'B':case 'b':B();break;
case 'C':case 'c':C();break;
case 'D':case 'd':D();break;
case 'E':case 'e':E();break;
case 'F':case 'f':F();break;
case 'G':case 'g':G();break;
case 'H':case 'h':H();break;
case 'I':case 'i':I();break;
case 'J':case 'j':J();break;
case 'K':case 'k':K();break;
case 'L':case 'l':L();break;
case 'M':case 'm':M();break;
case 'N':case 'n':N();break;
case 'O':case 'o':O();break;
case 'P':case 'p':P();break;
case 'Q':case 'q':Q();break;
case 'R':case 'r':R();break;
case 'S':case 's':S();break;
case 'T':case 't':T();break;
case 'U':case 'u':U();break;
case 'V':case 'v':V();break;
case 'W':case 'w':W();break;
case 'X':case 'x':X();break;
case 'Y':case 'y':Y();break;
case 'Z':case 'z':Z();break;
case '(':AbreParentesis();break;
case '\\':BarraIzqDer();break;
case '/':BarraDerIzq();break;
case ')':CierraParentesis();break;
case '_':GuionBajo();break;
case '\"':Comillas();break;
case '#':Almohadilla();break;
case '$':Dolar();break;
case '%':Porcentaje();break;
case '&':Andpersand();break;
case '\'':Apostrofe();break;
case '<':MenorQue();break;
case '>':MayorQue();break;
case '*':Asterisco();break;
case '+':Mas();break;
case ',':case ';':Espacio();PuntoyComa();break;// El ; y el .
case '.':Espacio();Punto();break;//se asocian al ultimo
case '0':Cero();break; //caracter. No hay coma pero queda
case '1':Uno();break; //parecido el punto y coma.
case '2':Dos();break;
case '3':Tres();break;
case '4':Cuatro();break;
case '5':Cinco();break;
case '6':Seis();break;
case '7':Siete();break;
case '8':Ocho();break;
case '9':Nueve();break;
case '?':Interrogacion();break;
case '=':Igual();break;
case ' ':Espacio();break;
default:Espacio();
}
}
delay(400); //Retraso para poder visualizar
BorrarDisplay();
}


if (scroll==0)
{
// Poniendo al principio la constante scroll a 0
// cada letra (llamada a su funcion) ira despues
// de la anterior y al acabar empezara linea nueva.
BorrarDisplay();
H();O();L();A();Espacio();
M();U();N();D();O();Punto();Espacio();
delay(1000);BorrarDisplay();
Q();U();E();Espacio();
T();A();L();Espacio();
E();S();T();A();M();O();S();Interrogacion();
delay(1500);
}
if (scroll==2)
{
// Poniendo la constante scroll a 2 aquí puedes
// hacer pruebas si arriesgar demasiado...
}
}//////////////////////////////////////////////////////////


// Funciones para controlar el display
// Ver datasheet 16lf01ua3.
void sclkfall() // SCLK activa por flanco
{ // de bajada lo que marque DATA
digitalWrite(SCLK,HIGH);
digitalWrite(SCLK,LOW); // y preparando la siguiente bajada
digitalWrite(SCLK,HIGH);// vuelve a quedar a nivel alto
}


void o() // La funcion o() ("o" minuscula) marca un 0
{
digitalWrite(DATA,LOW);
sclkfall();
}
void l() // La funcion l() ("L" minuscula) marca un 1
{
digitalWrite(DATA,HIGH);
sclkfall();
}



void resetdisplay() // resetea el display
{ // activando a nivel bajo RST mas
digitalWrite(RST,LOW); // de 1ms y volvieno a nivel alto
delay(2); // para funcionamiento normal
digitalWrite(RST,HIGH);
delay(2);
}


void numdigit() // Define cuantos digitos usaremos
{ // los 16 en este caso "11000000"
l();l();o();o();o();o();o();o();
}


void brillo() // Para configurar la intensidad del display
{ // Maxima 11111111
l();l();l();l();l();l();l();l();
}


int colocarPuntero(int En)// Elege el digito que se va a escribir
{
l();o();l();o();// Primer cuarteto ordena control de puntero 1010

switch(En) // Segundo cuarteto indica dígito. De izq. a der.
{ // El primero 1111 y siguientes 0000,0001,...1110
case 1:l();l();l();l();break;
case 2:o();o();o();o();break;
case 3:o();o();o();l();break;
case 4:o();o();l();o();break;
case 5:o();o();l();l();break;
case 6:o();l();o();o();break;
case 7:o();l();o();l();break;
case 8:o();l();l();o();break;
case 9:o();l();l();l();break;
case 10:l();o();o();o();break;
case 11:l();o();o();l();break;
case 12:l();o();l();o();break;
case 13:l();o();l();l();break;
case 14:l();l();o();o();break;
case 15:l();l();o();l();break;
case 16:l();l();l();o();break;
default:l();l();l();l();
}
return(0);
}


void BorrarDisplay() // Borra escribiendo 16 espacios
{
int c;
colocarPuntero(1);
for (c=1;c<17;c=c+1)
{
Espacio();
}
}

void Retraso()
{
delay(0); // Ajustar restraso para poder leer
}



////////// Definiciones de letras //////////////

void Arroba() // Codigo para escribir @ 00000000
{o();o();o();o();o();o();o();o();Retraso();}
void A() // Codigo para escribir A 00000001
{o();o();o();o();o();o();o();l();Retraso();}
void B() // Codigo para B 00000010
{o();o();o();o();o();o();l();o();Retraso();}
void C() // Codigo para C 00000011
{o();o();o();o();o();o();l();l();Retraso();}
void D() // Codigo para escribir D 00000100
{o();o();o();o();o();l();o();o();Retraso();}
void E() // Codigo para escribir E 00000101
{o();o();o();o();o();l();o();l();Retraso();}
void F() // Codigo para escribir F 00000110
{o();o();o();o();o();l();l();o();Retraso();}
void G() // Codigo para escribir G 00000111
{o();o();o();o();o();l();l();l();Retraso();}
void H() // Codigo para escribir H 00001000
{o();o();o();o();l();o();o();o();Retraso();}
void I() // Codigo para escribir I 00001001
{o();o();o();o();l();o();o();l();Retraso();}
void J() // Codigo para escribir J 00001010
{o();o();o();o();l();o();l();o();Retraso();}
void K() // Codigo para escribir K 00001011
{o();o();o();o();l();o();l();l();Retraso();}
void L() // Codigo para escribir L 00001100
{o();o();o();o();l();l();o();o();Retraso();}
void M() // Codigo para escribir M 00001101
{o();o();o();o();l();l();o();l();Retraso();}
void N() // Codigo para escribir N 00001110
{o();o();o();o();l();l();l();o();Retraso();}
void O() // Codigo para escribir O 00001111
{o();o();o();o();l();l();l();l();Retraso();}
void P() // Codigo para escribir P 00010000
{o();o();o();l();o();o();o();o();Retraso();}
void Q() // Codigo para escribir Q 00010001
{o();o();o();l();o();o();o();l();Retraso();}
void R() // Codigo para escribir R 00010010
{o();o();o();l();o();o();l();o();Retraso();}
void S() // Codigo para escribir S 00010011
{o();o();o();l();o();o();l();l();Retraso();}
void T() // Codigo para escribir T 00010100
{o();o();o();l();o();l();o();o();Retraso();}
void U() // Codigo para escribir U 00010101
{o();o();o();l();o();l();o();l();Retraso();}
void V() // Codigo para escribir V 00010110
{o();o();o();l();o();l();l();o();Retraso();}
void W() // Codigo para escribir W 00010111
{o();o();o();l();o();l();l();l();Retraso();}
void X() // Codigo para escribir X 00011000
{o();o();o();l();l();o();o();o();Retraso();}
void Y() // Codigo para escribir Y 00011001
{o();o();o();l();l();o();o();l();Retraso();}
void Z() // Codigo para escribir Z 00011010
{o();o();o();l();l();o();l();o();Retraso();}
void AbreParentesis() // Codigo para escribir ( 00011011
{o();o();o();l();l();o();l();l();Retraso();}
void BarraIzqDer() // Codigo para escribir \ 00011100
{o();o();o();l();l();l();o();o();Retraso();}
void BarraDerIzq() // Codigo para escribir / 00101111
{o();o();l();o();l();l();l();l();Retraso();}
void CierraParentesis() // Codigo para escribir ) 00011101
{o();o();o();l();l();l();o();l();Retraso();}
void GuionBajo() // Codigo para escribir _ 00011111
{o();o();o();l();l();l();l();l();Retraso();}
void Comillas() // Codigo para escribir " 00100010
{o();o();l();o();o();o();l();o();Retraso();}
void Almohadilla() // Codigo para escribir # 00100011
{o();o();l();o();o();o();l();l();Retraso();}
void Dolar() // Codigo para escirbir $ 00100100
{o();o();l();o();o();l();o();o();Retraso();}
void Porcentaje() // Codigo para escribir % 00100101
{o();o();l();o();o();l();o();l();Retraso();}
void Andpersand() // Codigo para escribir & 00100110
{o();o();l();o();o();l();l();o();Retraso();}
void Apostrofe() // Codigo para escribir ' 00100111
{o();o();l();o();o();l();l();l();Retraso();}
void MenorQue() // Codigo para escribir < 00101000
{o();o();l();o();l();o();o();o();Retraso();}
void MayorQue() // Codigo para escribir > 00101001
{o();o();l();o();l();o();o();l();Retraso();}
void Asterisco() // Codigo para escribir * 00101010
{o();o();l();o();l();o();l();o();Retraso();}
void Mas() // Codigo para escribir + 00101011
{o();o();l();o();l();o();l();l();Retraso();}
void PuntoyComa() // Codigo para escribir ; 00101100
{o();o();l();o();l();l();o();o();Retraso();}
void Menos() // Codigo para escribir - 00101101
{o();o();l();o();l();l();o();l();Retraso();}
void Punto() // Codigo para escribir . 00101110
{o();o();l();o();l();l();l();o();Retraso();}
void Cero() // Codigo para escribir 0 00110000
{o();o();l();l();o();o();o();o();Retraso();}
void Uno() // Codigo para escribir 1 00110001
{o();o();l();l();o();o();o();l();Retraso();}
void Dos() // Codigo para escribir 2 00110010
{o();o();l();l();o();o();l();o();Retraso();}
void Tres() // Codigo para escribir 3 00110011
{o();o();l();l();o();o();l();l();Retraso();}
void Cuatro() // Codigo para escribir 4 00110100
{o();o();l();l();o();l();o();o();Retraso();}
void Cinco() // Codigo para escribir 5 00110101
{o();o();l();l();o();l();o();l();Retraso();}
void Seis() // Codigo para escribir 6 00110110
{o();o();l();l();o();l();l();o();Retraso();}
void Siete() // Codigo para escribir 7 00110111
{o();o();l();l();o();l();l();l();Retraso();}
void Ocho() // Codigo para escribir 8 00111000
{o();o();l();l();l();o();o();o();Retraso();}
void Nueve() // Codigo para escribir 9 00111001
{o();o();l();l();l();o();o();l();Retraso();}
void Interrogacion() // Codigo para escribir ? 00111111
{o();o();l();l();l();l();l();l();Retraso();}
void Igual() // Codigo para escribir = 00111101
{o();o();l();l();l();l();o();l();Retraso();}
void Espacio() // Codigo para el espacio 00100000
{o();o();l();o();o();o();o();o();}

/////////////////////////////////////Sancos 16-6-2011
////////////////////////////////////

El código sale aquí un poco enmarañado porque si copio y pego pierdo los tabulados. Pero para seguir un poco las explicaciones sirve.
Si quieres acceder al "archivo.pde" que puede leer directamente el IDE del pinguino, puedes descargarlo desde aquí.
Actualmente yo estoy usando la versión "Pinguino beta 9.04" y es con la que he probado todo esto.
También me ha parecido útil el que puedas descargar el programa como "documento.doc" para que sea más legible con colores y sangrados. Pero recuerda que esto no correrá en el editor del Pinguino. Tendrías que pasarlo escribiendo a pelo.

También añado aquí (a Diciembre del 2014) si quieres descargar una versión revisada para arduino ("archivo.ino"). Probado con arduino uno y el ide 1.0.4 a raiz de un comentario anónimo del  6 de diciembre de 2014 (léelo para observaciones) advirtiendo de que el código para pinguino no compilaba en arduino.

Al final he usado un if para, según el valor de la constante scroll, escoger un código que hace pasar de derecha a izquierda lo que pongamos en Mensaje[x] (video que pone una fecha); o no controlar la posición de cada letra y que sea el display el que automaticamente ponga una tras otra (video "Hola mundo, como estamos?"); o en una tercera opción, podemos hacer pruebas sin estropear lo que sabemos que funciona. En esta última parte no he puesto código.

Supongo que se entiende facilmente que las funciones l(), y o() envían, respectivamente, un 1 y un 0 al display.

Para cada letra se llama a su función correspondiente. Esta envía ceros y unos con el código correcto, según la hoja de características. Observa que el código que corresponde a escribir una letra tiene como bit más significativos (los primeros que se emiten) dos ceros, y los seis restantes indican la letra. Los que no empiecen por dos ceros son códigos de control: el brillo (yo sólo uso el maximo- brillo() -),número de dígitos (yo uso los 16- numdigit() -),la posición del siguiente caracter (aquí la función no es tan simple porque para conseguir que parezca que se desplacen tengo que posicionar cada letra- colocarPuntero() -y uso todos los códigos).
Algunas funciones especiales son: resetdisplay() en la que activo la linea de reset, o BorrarDisplay() que escribe 16 espacios. La función retraso() ,que aquí parecerá inútil, permite hacer pruebas con diferentes velocidades. Si escribes todo sin perder tiempo no verás nada.

Si al principio del código pones scroll=0 el asunto solo se tratará de ir "llamando a letras" estas irán apareciendo en la posición 1, luego 2, luego 3,...luego 16, luego 1, luego 2... Esto lo hace automaticamente el display 16LF01UA3. Al poner una letra suma la siguiente posición un paso, excepto para la coma y el punto.

Si el scroll=1, se complica un poco la cosa. Porque intento que el texto (el que quieras poner en Mensaje[x]) se desplace continuamente como en un luminoso publicitario. Mensaje[x] es un array. Necesitas que el texto quepa dentro, así que x debe de ser al menos el número de caracteres que quieras representar. El primer carácter será Mensaje[0] y el último Mensaje[x-1]. Observese que Mensaje[x] debería ser un carácter especial que marque el final de la cadena. He tenido problemas con esto. Así que en vez de "calcular" el final de la cadena, he optado por utilizar una constante posicionUltLetra que me marca cual es la última letra.
Luego el truco será en usar la cadena puente digDisplay[17] que se corresponderá a cada caracter real del display (1...16). Será 17, porque la posición 0 y 17 no se usan. Al principio esta cadena tiene espacios. Luego iremos pasando cada letra un puesto a la izquierda. ¿Y que pasa con la última? Pues que será la nueva, que vamos controlando con posicionMensaje teniendo en cuenta que después del final, vendrá el principio.
Una vez que tenemos la cadena digDisplay[17] llena de forma apropiada, escribimos cada elemento en su posición, llamando a las función más apropiada.
Y repetimos...



Espero no haberme liado mucho.
Me gustaría que hicieseis comentarios, aunque no sean demasiado buenos. Si son constructivos prenderé más. Y también me gustaría saber si alguien lo lee...


Bueno os dejo los videos.

Con desplazamiento, tipo luminoso publicitario:

Y sin desplacamiento:

POR FAVOR, valoraría comentarios... y siento la posible publicidad en mis enlaces pero es lo que tienen los alojamientos gratuitos...
Saludos.