Ayuda Panel 2416 Sure

Hola, he comprado el panel "2416 Red LED 5mm Dot Matrix Display Information Board" de Sure y estoy intenandolo controlar con esta libreria https://github.com/gauravmm/HT1632-for-Arduino , pero no hay manera, solo consigo que me funcione la parte de arriba, la de abajo no salen mas que cosas raras" he cambiado los valores de la libreria pero na, me vuelvo loco!!

otra cosa es que la placa por debajo pone 15115-112v110

Alguien tiene una similar?

https://github.com/gauravmm/HT1632-for-Arduino

este es el panel en cuestion

http://www.sure-electronics.net/mcu,display/DE-DP11212_4_b.jpg

http://cgi.ebay.es/ws/eBayISAPI.dll?ViewItem&item=350458902543&ssPageName=STRK:MEWNX:IT#ht_4135wt_957

Yo tengo una 3216 y uso la misma libreria.

Has cargado el ejemplo que viene por defecto o tienes algun codigo creado?

he usado los del ejemplo y funciona, pero solo de la mitad para arriba, y la parte de abajo me salen cosas raras, por la tarde subo unas fotos. me podrías mandar tu código? es el mismo panel? gracias

Yo tengo una 3216 y uso la misma libreria.

yoPERO, es curioso el artilugio, pero cuando quieres insertar un icono como lo pasas al panel.?

Saludo.s

La verdad es que aun no experimentado con iconos, he hecho cosas simples como crueces, lineas etc. Tiene mucho curro la verdad, en este post encontraras muchos ejemplos:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1225239439 La programación seria algo como:

// Column1 Column2 Column3 Y coordinate number // |00000000||00000000||00000000|0 // |00000000||00000000||00000000|1 // |00000000||00000000||00000000|2 // |00000000||00000000||00000000|3 // |00000000||00000000||00000000|4 // |00000000||00000000||00000000|5 // |00000000||00000000||00000000|6 // |00000000||00000000||00000000|7 //Top X coodinate [6543210-1][14 to 7][22 to 15] // Top left middle right 8x8's // |00000000||00000000||00000000|8 // |00000000||00000000||00000000|9 // |00000000||00000000||00000000|10 // |00000000||00000000||00000000|11 // |00000000||00000000||00000000|12 // |00000000||00000000||00000000|13 // |00000000||00000000||00000000|14 // |00000000||00000000||00000000|15 //Bottom X coodinate [6543210-1][14 to 7 ][22 to 15 ] // Bottom left middle right 8x8's

Tambien se puede cargar directamente un bitmap, pero debes saber exactamente las dimensiones y el nombre

http://canosso.wordpress.com/2011/01/06/using-bitmaps-and-scroll-them-for-bicolor-matrix/

Pero el que se lleva la palma es el de este link:

http://www.youtube.com/watch?v=efrn9bTX0nQ&playnext=1&list=PLA74209AB2C67D7EC

Gracias por la info.

nada, no hay manera, para mi que la memoria de este panel esta direccionada de otra forma

@ varikap

Prueba con la libreria de MilesBurton:

http://www.milesburton.com/HT1632_Arduino_%22Matrix_Display%22_Library_for_the_Sure_2416_and_0832

@varikap

Acabo de probar con esta otra library y tambien funciona, pruebalo y me dices.

http://code.google.com/p/wiseclock2/downloads/detail?name=HT1632.zip&can=2&q=

nada, tampoco me van esas librerias, como mucho he conseguido esto, la parte de abajo se ve mal, no debería de salir nada :(

por fin!!! este si funciona!! por que puede ser?

http://123led.wordpress.com/about/

Excelente, me alegra que ahora funcione.

Madre mia con el “pong clock” 1642 lineas de codigo.

de porque no funciona… no lo se, quiza sea bueno que de las anteriores librerias aisles solo una funcion:

es decir que el void loop quede algo como esto:

void loop ()
{
demo_chars();

}

Las librerias traen muchos ejemplos juntos y depnde del tamño de la matrix para que cosas complejas funcionen correctamente.
Si introduces codigos que pasan el limite( en tu caso 24 para x) entonces la señal baja en Y y segun yo por eso es que la segunda linea horizontal de tu matrix muestra cosas raras.

imagen mas grande:

Hola,

yo estuve haciendo unas pruebas con la misma que tu utilizas y con la que es bicolor. Descubrí dos cosas, que muchos de los ejemplos que corren por Internet no funcionan y que algunas de estas matrices tienen problemas de calidad, yo compré dos como la tuya y en una me salió una zona que no funcionaba.

El schetck que me funcónó es el del ping, en la matriz biciolor no lo conseguí sin eliminar la parte del shadow ram, de todas formas hay dos versiones del chip que se llaman casi igual y las librerías no son las mismas.

Las fuentes que me encontré eran bastante feas, pero utilicé un programa The dot factory que permite generar los ficheros a partir de las fuentes del windows, también para hacer iconos.

El resultado final de lo que hice está en este link: http://www.zigbe.net/?p=806 no me acuerdo si publiqué mucha información técnica en los posts anteriores porque al final el panel es funcional, pero me queda pulir algunos detalles y publicar la info.

http://www.zigbe.net

Nada, no hay manera, no encuentro ninguna libreria que funcione, solo este :(

http://123led.wordpress.com/about/

ayuda!!!!

y se te funciona porque quieres otra?

Hola!! podrias pasarme la libreria que te funciono con el panel que es igual que el mio? Gracias!!!

zigbe31416: Hola,

yo estuve haciendo unas pruebas con la misma que tu utilizas y con la que es bicolor. Descubrí dos cosas, que muchos de los ejemplos que corren por Internet no funcionan y que algunas de estas matrices tienen problemas de calidad, yo compré dos como la tuya y en una me salió una zona que no funcionaba.

El schetck que me funcónó es el del ping, en la matriz biciolor no lo conseguí sin eliminar la parte del shadow ram, de todas formas hay dos versiones del chip que se llaman casi igual y las librerías no son las mismas.

Las fuentes que me encontré eran bastante feas, pero utilicé un programa The dot factory que permite generar los ficheros a partir de las fuentes del windows, también para hacer iconos.

El resultado final de lo que hice está en este link: http://www.zigbe.net/?p=806 no me acuerdo si publiqué mucha información técnica en los posts anteriores porque al final el panel es funcional, pero me queda pulir algunos detalles y publicar la info.

http://www.zigbe.net

Hola,

la verdad es que después de muchas pruebas (y de llegar a la conclusión de que uno de los paneles estaba estropeado porque me hacía una línea blanca), conseguí que me funcionara con la librería que venía en el ejemplo del pong.

#include <ht1632c.h> // Holtek LED driver by WestFW - updated to HT1632C by Nick Hall

Este es el encabezado del scketch

/***********************************************************************

  • LED Pong Clock by Nick Hall
  • v2.27 Dec 2010

Ojo, porque hay varias versiones de estos paneles controlados por un chip diferente (unos llevan la C y los otros no). he estado buscando los ficheros, la verdad es que núnca acabé este proyecto con los paneles monocromos, porque cuando vi que funcionaba continué con dos paneles de color, que utilizan otra librería diferente.

Tuve muchos problemas con la primera y con la segunda librería, complicados con la poca fiabilidad de estos paneles que a veces provocan que lo que piensas que es un error de programación sea un error del panel.

inserto a continuación una copia de mi scketch, es una versión bastante primitiva, poco documentada, desordenada, etc. aunque funciona. Lo siento

#include <ht1632c.h>                     // Holtek LED driver by WestFW - updated to HT1632C by Nick Hall
#include <avr/pgmspace.h>                // Enable data to be stored in Flash Mem as well as SRAM              
#include <Font.h>                        // Font library

#define ASSERT(condition)                // Nothing
#define X_MAX 47                         // Matrix X max LED coordinate (for 2 displays placed next to each other)
#define Y_MAX 15                         // Matrix Y max LED coordinate (for 2 displays placed next to each other)
#define NUM_DISPLAYS 2                   // Num displays for shadow ram data allocation
#define FADEDELAY 40                     // Time to fade display to black
#define plot(x,y,v)  ht1632_plot(x,y,v)  // Plot LED
#define cls          ht1632_clear        // Clear display

static const byte ht1632_data = 10;      // Data pin for sure module
static const byte ht1632_wrclk = 11;     // Write clock pin for sure module
static const byte ht1632_cs[2] = {4,5};  // Chip_selects one for each sure module. Remember to set the DIP switches on the modules too.

String ProductId;
//____________________NEWSOFTSERIAL (SOLO PARA DEPURAR)_____________________________
#include <NewSoftSerial.h>
uint8_t ssRX = 9; // Connect Arduino pin 9 to TX of usb-serial device
uint8_t ssTX = 8;// Connect Arduino pin 8 to RX of usb-serial device
NewSoftSerial nss(ssRX, ssTX);
int cuentas =0;
int cuentasZB =0;
//__________________________________XBEE____________________________________________
#include <XBee.h> //Librería Xbeee
#define MAX_PAYLOAD_SIZE 72
XBee xbee = XBee(); //Crea el objeto de Xbee
uint8_t payload[MAX_PAYLOAD_SIZE]; //Crea el puntero para el payload
XBeeAddress64 addr64 = XBeeAddress64(0x00000000, 0x00000000);
ZBTxRequest zbTx = ZBTxRequest(addr64, payload, sizeof(payload));
ZBTxStatusResponse txStatus = ZBTxStatusResponse();
// create reusable response objects for responses we expect to handle 
ZBRxResponse rx = ZBRxResponse();
ModemStatusResponse msr = ModemStatusResponse();
String pzb; //Contiene la cadena que se va a enviar en el payload

int statusLed = 13;
int errorLed=13;
int puntos=0;
long milesimas=0;



void setup()
{
  xbee.begin(9600);
// Solo depuración
  nss.begin(9600);
  nss.println("Se ha reiniciado el micro!!");
 // nss.println (cf.ProductId);


//********************************PANEL**************************************************
  ht1632_setup();                        // Setup display (uses flow chart from page 17 of sure datasheet)
  ProductId="BMOTES PANEL";
    ht1632_clear();
    for (int n=0; n<=ProductId.length(); n++){
      ht1632_puttinychar( n*4, 1, ProductId[n]);
    }
delay (2000);
fade_down();
ht1632_clear();
fade_up();
}

//_________________________________BUCLE PRINCIPAL DEL PROGRAMA________________________________________
void loop()
{ 
  xbee.readPacket(); //lee el paquete
//*****DEBUG*****
   cuentasZB += 1;
   if (cuentasZB>2000){
     cuentasZB=0;
   nss.print ("#");
  }
//****************

  if (xbee.getResponse().isAvailable()) //Si se ha recibido.....
    {
    nss.println ("Se ha recibido un paquete");

      if (xbee.getResponse().getApiId() == ZB_RX_RESPONSE) 
        { //Comprueba que se trate de un paquete ZB RX
        xbee.getResponse().getZBRxResponse(rx); //rellena la clase ZB RX

        borrarPayload();
        String comando;
        for (int n=0; n<=rx.getDataLength(); n++)
            {
            payload[n] = rx.getData()[n];
            comando += rx.getData()[n];
            }
    
         if (comando.substring (0, 4)=="INF:")//Se ha enviado un comando al panel
            {
            procesarInfo (comando);
            nss.println ("Despues de procesar info");
            }
         if (comando.substring (0, 4)=="HOR:") //Se ha enviado un comando de hora
            {
              procesarHora (comando);
              nss.println ("Despues de procesar hora");
            }
      }
    }
    if (millis()-milesimas >1000)
    {
      int pos=22;
       if (puntos==0)
       {puntos=1;
             ht1632_plot (pos, 6, 1);
             ht1632_plot (pos, 7, 1);
             ht1632_plot (pos+1, 6, 1);
             ht1632_plot (pos+1, 7, 1);
             ht1632_plot (pos, 10, 1);
             ht1632_plot (pos, 11, 1);
             ht1632_plot (pos+1, 10, 1);
             ht1632_plot (pos+1, 11, 1);
       }       
       else
       {puntos=0;
             ht1632_plot (pos, 6, 0);
             ht1632_plot (pos, 7, 0);
             ht1632_plot (pos+1, 6, 0);
             ht1632_plot (pos+1, 7, 0);
             ht1632_plot (pos, 10, 0);
             ht1632_plot (pos, 11, 0);
             ht1632_plot (pos+1, 10, 0);
             ht1632_plot (pos+1, 11, 0);
       }
      milesimas = millis();   
    }
} //Fin del loop  

//_______________________PROCESAR INFO____________________________________________________
void procesarInfo (String cmd){
  
int a, b, c;
String nodo, canal, valor, unidad;
int pos;
char car;

fade_down();
        nss.println ("Despues de fade_down()");
ht1632_clear();
        nss.println ("Despues de ht1632_clear()");
fade_up();
        nss.println ("Despues de fadeup");

// Separa los valores en diferentes variables
a= cmd.indexOf(';');
if (a== -1)
  {nodo=' ';
  canal=' ';
  valor=' ';}
else
  {
    nodo = cmd.substring (4,a);
    b= cmd.indexOf(';', a+1);
    if (b== -1)
      {canal=' ';
      valor=' ';}
    else
      {canal = cmd.substring (a+1,b);
       c= cmd.indexOf(';', b+1);
       if (c== -1)
          {valor=' ';}
        else
          {valor = cmd.substring (b+1,c);}
    }
  }

unidad = cmd.substring (c+1);

if (nodo.length()>8) {nodo=nodo.substring(0,8);}
if (canal.length()>8) {canal=canal.substring(0,8);}
if (valor.length()>8) {valor=valor.substring(0,8);}

//Muestra en pantalla el nodo y el canal
pos= 0;
  for (int n=0; n<nodo.length(); n++)
      {
      ht1632_putchar(pos, 0, nodo[n]);
      pos +=6;
      nss.print (nodo[n]);
      }
pos= 0;
  for (int n=0; n<canal.length(); n++)
      {
      ht1632_putchar( pos, 8, canal[n]);
      pos +=6;
      nss.print (canal[n]);
      }
//espera 3 segundos
delay (3000);
fade_down();
        nss.println ("Despues de fade_down()");
ht1632_clear();
        nss.println ("Despues de ht1632_clear()");
fade_up();
        nss.println ("Despues de fadeup");

pos= 0;
  for (int n=0; n<valor.length(); n++)
      {
      car= valor[n];
        switch (car)
        {
         case '.':
           ht1632_plot (pos, 13, 1);
           ht1632_plot (pos, 14, 1);
           ht1632_plot (pos+1, 13, 1);
           ht1632_plot (pos+1, 14, 1);
           pos +=3;
           break;
         case ',':
           ht1632_plot (pos, 13, 1);
           ht1632_plot (pos, 14, 1);
           ht1632_plot (pos+1, 13, 1);
           ht1632_plot (pos+1, 14, 1);
           pos +=3;
           break;
         case '1':
           ht1632_putbigchar( pos, 1, valor[n]);
           pos +=9;
           break;
         default:
           ht1632_putbigchar( pos, 1, valor[n]);
           pos +=11;
        } 
      nss.print (canal[n]);
      }
delay (3000);
}

//_______________________PROCESAR HORA____________________________________________________
void procesarHora (String cmd)
{
  int pos=0;
  ht1632_clear();
    for (int n=4; n<6; n++)
        {
         ht1632_putbigchar( pos, 1, cmd[n]);
        pos +=11;
        }
             ht1632_plot (pos, 6, 1);
             ht1632_plot (pos, 7, 1);
             ht1632_plot (pos+1, 6, 1);
             ht1632_plot (pos+1, 7, 1);
             ht1632_plot (pos, 10, 1);
             ht1632_plot (pos, 11, 1);
             ht1632_plot (pos+1, 10, 1);
             ht1632_plot (pos+1, 11, 1);
             pos +=3;
  
    for (int n=7; n<9; n++)
        {
         ht1632_putbigchar( pos, 1, cmd[n]);
        pos +=11;
        }
  
}

http://www.zigbe.net/

La parte que faltaba, no me ha cabido en un solo mensaje.

Por cierto… he leído en este post que alguien utiliza el panel 1632 con la misma librería, OJO!!! no utiliza la misma y no funciona, una utiliza el chip holteck XXXXXc y la otra sin c, la información que hay es muy confusa e induce al error, te aseguro que son librerías diferentes.

//____________________________ENVIAR PAQUETE DEL HARDWARE_______________________________________
void enviarPaqueteHW ()
{
/*  
  pzb= "PID=";
    pzb += cf.ProductId;
    pzb += ";HW=";
    pzb += cf.HardwareVersion;
    pzb += ";SW=";
    pzb += cf.SoftwareVersion;
    pzb += ";SN=";
    pzb += cf.SerialNumber;
    pzb += ";";
    borrarPayload();
    for (int i=0; i<=pzb.length(); i++){payload[i] = pzb[i];} //Copia la String en el Payload
    xbee.send(zbTx);
    //DEBUG
    nss.println ("enviarPaqueteHW->ha enviado el paquete");
    nss.println(pzb);
    //
*/
}


//____________________________BORRAR PAYLOAD_______________________________________
void borrarPayload()
{
      for ( int n=0; n<=MAX_PAYLOAD_SIZE; n++){payload[n]= ' ';} //borra el contenido de la valiable del payload
}

//_____________CONVERTIR LAS VARIABLES DOUBLE EN TEXTO______________________________
String floatToString(double number, uint8_t digits) 
{ 
  String resultString = "";
  if (number < 0.0){  // Handle negative numbers
     resultString += "-";
     number = -number;
  }
  double rounding = 0.5;// Redondea correctamente (1.999, 2) se imprimem como "2.00"
  for (uint8_t i=0; i<digits; ++i)
    rounding /= 10.0;
    number += rounding;
  unsigned long int_part = (unsigned long)number;// Extrae la parte entera del número y la imprime
  double remainder = number - (double)int_part;
  resultString += int_part;
  if (digits > 0) {resultString += ".";}   // Imprime el punto digital si hay decimales
  while (digits-- > 0){  // Estrae los dígitos que quedan de uno en uno
    remainder *= 10.0;
    int toPrint = int(remainder);
    resultString += toPrint;
    remainder -= toPrint; 
  } 
  return resultString;
}

//_________________MUESTRA LOS CÓDIGOS DE ERROR EN EL LED____________________________
void flashLed(int pin, int times, int wait) {
    for (int i = 0; i < times; i++) {
      digitalWrite(pin, HIGH);
      delay(wait);
      digitalWrite(pin, LOW);
      if (i + 1 < times) {delay(wait);}
    }
}


/*
 * ht1632_chipselect / ht1632_chipfree
 * Select or de-select a particular ht1632 chip. De-selecting a chip ends the commands being sent to a chip.
 * CD pins are active-low; writing 0 to the pin selects the chip.
 */

void ht1632_chipselect(byte chipno)
{
  DEBUGPRINT("\nHT1632(%d) ", chipno);
  digitalWrite(chipno, 0);
}

void ht1632_chipfree(byte chipno)
{
  DEBUGPRINT(" [done %d]", chipno);
  digitalWrite(chipno, 1);
}


/*
 * ht1632_writebits
 * Write bits (up to 8) to h1632 on pins ht1632_data, ht1632_wrclk Chip is assumed to already be chip-selected
 * Bits are shifted out from MSB to LSB, with the first bit sent being (bits & firstbit), shifted till firsbit is zero.
 */
void ht1632_writebits (byte bits, byte firstbit)
{
  DEBUGPRINT(" ");
  while (firstbit) {
    DEBUGPRINT((bits&firstbit ? "1" : "0"));
    digitalWrite(ht1632_wrclk, LOW);
    if (bits & firstbit) {
      digitalWrite(ht1632_data, HIGH);
    } 
    else {
      digitalWrite(ht1632_data, LOW);
    }
    digitalWrite(ht1632_wrclk, HIGH);
    firstbit >>= 1;
  }
}


/*
 * ht1632_sendcmd
 * Send a command to the ht1632 chip. A command consists of a 3-bit "CMD" ID, an 8bit command, and one "don't care bit".
 *   Select 1 0 0 c7 c6 c5 c4 c3 c2 c1 c0 xx Free
 */
static void ht1632_sendcmd (byte d, byte command)
{
  ht1632_chipselect(ht1632_cs[d]);        // Select chip
  ht1632_writebits(HT1632_ID_CMD, 1<<2);  // send 3 bits of id: COMMMAND
  ht1632_writebits(command, 1<<7);        // send the actual command
  ht1632_writebits(0, 1);         	  // one extra dont-care bit in commands.
  ht1632_chipfree(ht1632_cs[d]);          //done
}


/*
 * ht1632_senddata
 * send a nibble (4 bits) of data to a particular memory location of the
 * ht1632.  The command has 3 bit ID, 7 bits of address, and 4 bits of data.
 *    Select 1 0 1 A6 A5 A4 A3 A2 A1 A0 D0 D1 D2 D3 Free
 * Note that the address is sent MSB first, while the data is sent LSB first!
 * This means that somewhere a bit reversal will have to be done to get
 * zero-based addressing of words and dots within words.
 */
static void ht1632_senddata (byte d, byte address, byte data)
{
  ht1632_chipselect(ht1632_cs[d]);      // Select chip
  ht1632_writebits(HT1632_ID_WR, 1<<2); // Send ID: WRITE to RAM
  ht1632_writebits(address, 1<<6);      // Send address
  ht1632_writebits(data, 1<<3);         // Send 4 bits of data
  ht1632_chipfree(ht1632_cs[d]);        // Done.
}


/*
 * ht1632_setup
 * setup the ht1632 chips
 */
void ht1632_setup()
{
  for (byte d=0; d<NUM_DISPLAYS; d++) {
    pinMode(ht1632_cs[d], OUTPUT);

    digitalWrite(ht1632_cs[d], HIGH);  // Unselect (active low)
     
    pinMode(ht1632_wrclk, OUTPUT);
    pinMode(ht1632_data, OUTPUT);
    
    ht1632_sendcmd(d, HT1632_CMD_SYSON);    // System on 
    ht1632_sendcmd(d, HT1632_CMD_LEDON);    // LEDs on 
    ht1632_sendcmd(d, HT1632_CMD_COMS01);   // NMOS Output 24 row x 24 Com mode
    
    for (byte i=0; i<128; i++)
      ht1632_senddata(d, i, 0);  // clear the display!
  }
}


/*
 * we keep a copy of the display controller contents so that we can know which bits are on without having to (slowly) read the device.
 * Note that we only use the low four bits of the shadow ram, since we're shadowing 4-bit memory.  This makes things faster, and we
 * use the other half for a "snapshot" when we want to plot new data based on older data...
 */
byte ht1632_shadowram[NUM_DISPLAYS * 96];  // our copy of the display's RAM


/*
 * plot a point on the display, with the upper left hand corner being (0,0).
 * Note that Y increases going "downward" in contrast with most mathematical coordiate systems, but in common with many displays
 * No error checking; bad things may happen if arguments are out of bounds!  (The ASSERTS compile to nothing by default
 */
void ht1632_plot (char x, char y, char val)
{

  char addr, bitval;

  ASSERT(x >= 0);
  ASSERT(x <= X_MAX);
  ASSERT(y >= 0);
  ASSERT(y <= y_MAX);

  byte d;
  //select display depending on plot values passed in
  if (x >= 0 && x <=23 ) {
    d = 0;
  }  
  if (x >=24 && x <=47) {
    d = 1;
    x = x-24; 
  }   

  /*
   * The 4 bits in a single memory word go DOWN, with the LSB (first transmitted) bit being on top.  However, writebits()
   * sends the MSB first, so we have to do a sort of bit-reversal somewhere.  Here, this is done by shifting the single bit in
   * the opposite direction from what you might expect.
   */

  bitval = 8>>(y&3);  // compute which bit will need set

  addr = (x<<2) + (y>>2);  // compute which memory word this is in 

  if (val) {  // Modify the shadow memory
    ht1632_shadowram[(d * 96)  + addr] |= bitval;
  } 
  else {
    ht1632_shadowram[(d * 96) + addr] &= ~bitval;
  }
  // Now copy the new memory value to the display
  ht1632_senddata(d, addr, ht1632_shadowram[(d * 96) + addr]);
}


/*
 * get_shadowram
 * return the value of a pixel from the shadow ram.
 */
byte get_shadowram(byte x, byte y)
{
  byte addr, bitval, d;

  //select display depending on plot values passed in
  if (x >= 0 && x <=23 ) {
    d = 0;
  }  
  if (x >=24 && x <=47) {
    d = 1;
    x = x-24; 
  }  

  bitval = 8>>(y&3);  // compute which bit will need set
  addr = (x<<2) + (y>>2);       // compute which memory word this is in 
  return (0 != (ht1632_shadowram[(d * 96) + addr] & bitval));
}

http://www.zigbe.net/

tercer y último trozo…

/*
 * snapshot_shadowram
 * Copy the shadow ram into the snapshot ram (the upper bits)
 * This gives us a separate copy so we can plot new data while
 * still having a copy of the old data.  snapshotram is NOT
 * updated by the plot functions (except "clear")
 */
void snapshot_shadowram()
{
  for (byte i=0; i< sizeof ht1632_shadowram; i++) {
    ht1632_shadowram[i] = (ht1632_shadowram[i] & 0x0F) | ht1632_shadowram[i] << 4;  // Use the upper bits
  }

}

/*
 * get_snapshotram
 * get a pixel value from the snapshot ram (instead of
 * the actual displayed (shadow) memory
 */
byte get_snapshotram(byte x, byte y)
{

  byte addr, bitval;
  byte d = 0;

  //select display depending on plot values passed in 
  if (x >=24 && x <=47) {
    d = 1;
    x = x-24; 
  }  

  bitval = 128>>(y&3);  // user upper bits!
  addr = (x<<2) + (y>>2);   // compute which memory word this is in 
  if (ht1632_shadowram[(d * 96) + addr] & bitval)
    return 1;
  return 0;
}


/*
 * ht1632_clear
 * clear the display, and the shadow memory, and the snapshot
 * memory.  This uses the "write multiple words" capability of
 * the chipset by writing all 96 words of memory without raising
 * the chipselect signal.
 */
void ht1632_clear()
{
  char i;
  for(byte d=0; d<NUM_DISPLAYS; d++)
  {
    ht1632_chipselect(ht1632_cs[d]);  // Select chip
    ht1632_writebits(HT1632_ID_WR, 1<<2);  // send ID: WRITE to RAM
    ht1632_writebits(0, 1<<6); // Send address
    for (i = 0; i < 96/2; i++) // Clear entire display
      ht1632_writebits(0, 1<<7); // send 8 bits of data
    ht1632_chipfree(ht1632_cs[d]); // done
    for (i=0; i < 96; i++)
      ht1632_shadowram[96*d + i] = 0;
  }
}


/* ht1632_putchar
 * Copy a 5x7 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate
 * This is unoptimized and simply uses plot() to draw each dot.
 */
void ht1632_putchar(byte x, byte y, char c)
{
  byte dots;
  if (c >= 'A' && c <= 'Z' || (c >= 'a' && c <= 'z') ) {
    c &= 0x1F;   // A-Z maps to 1-26
  } 
  else if (c >= '0' && c <= '9') {
    c = (c - '0') + 31;
  } 
  else if (c == ' ') {
    c = 0; // space
  }
  else if (c == '.') {
    c = 27; // full stop
  }
  else if (c == '\'') {
    c = 28; // single quote mark
  }  
  else if (c == ':') {
    c = 29; // clock_mode selector arrow
  }
  else if (c == '>') {
    c = 30; // clock_mode selector arrow
  }

  for (char col=0; col< 5; col++) {
    dots = pgm_read_byte_near(&myfont[c][col]);
    for (char row=0; row < 7; row++) {
      if (dots & (64>>row))   	     // only 7 rows.
        plot(x+col, y+row, 1);
      else 
        plot(x+col, y+row, 0);
    }
  }
}


/* ht1632_putbigchar
 * Copy a 10x14 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate
 * This is unoptimized and simply uses plot() to draw each dot.
 */
void ht1632_putbigchar(byte x, byte y, char c)
{
  byte dots;
  if (c >= 'A' && c <= 'Z' || (c >= 'a' && c <= 'z') ) {
    return;   //return, as the 10x14 font contains only numeric characters 
  } 
  if (c >= '0' && c <= '9') {
    c = (c - '0');
    c &= 0x1F;
  } 

  for (char col=0; col< 10; col++) {
    dots = pgm_read_byte_near(&mybigfont[c][col]);
    for (char row=0; row < 8; row++) {
      if (dots & (128>>row))   	   
        plot(x+col, y+row, 1);
      else 
        plot(x+col, y+row, 0);
    }

    dots = pgm_read_byte_near(&mybigfont[c][col+10]);
    for (char row=0; row < 8; row++) {
      if (dots & (128>>row))   	   
        plot(x+col, y+row+8, 1);
      else 
        plot(x+col, y+row+8, 0);
    } 
  }  
}


/* ht1632_puttinychar
 * Copy a 3x5 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate
 * This is unoptimized and simply uses plot() to draw each dot.
 */
void ht1632_puttinychar(byte x, byte y, char c)
{
  byte dots;
  if (c >= 'A' && c <= 'Z' || (c >= 'a' && c <= 'z') ) {
    c &= 0x1F;   // A-Z maps to 1-26
  } 
  else if (c >= '0' && c <= '9') {
    c = (c - '0') + 31;
  } 
  else if (c == ' ') {
    c = 0; // space
  }
  else if (c == '.') {
    c = 27; // full stop
  }
  else if (c == '\'') {
    c = 28; // single quote mark
  } else if (c == '!') {
    c = 29; // single quote mark
  }  else if (c == '?') {
    c = 30; // single quote mark
  }

  for (char col=0; col< 3; col++) {
    dots = pgm_read_byte_near(&mytinyfont[c][col]);
    for (char row=0; row < 5; row++) {
      if (dots & (16>>row))   	   
        plot(x+col, y+row, 1);
      else 
        plot(x+col, y+row, 0);
    }
  }  
}




/*
 * flashing_cursor
 * print a flashing_cursor at xpos, ypos and flash it repeats times 
 */
void flashing_cursor(byte xpos, byte ypos, byte cursor_width, byte cursor_height, byte repeats)
{
  for (byte r = 0; r <= repeats; r++) {    
    for (byte x = 0; x <= cursor_width; x++) {
      for (byte y = 0; y <= cursor_height; y++) {
        plot(x+xpos, y+ypos, 1);
      }
    }
    
    if (repeats > 0) {
      delay(400);
    } else {
      delay(70);
    }
        
    for (byte x = 0; x <= cursor_width; x++) {
      for (byte y = 0; y <= cursor_height; y++) {
        plot(x+xpos, y+ypos, 0);
      }
    }   
    //if cursor set to repeat, wait a while
    if (repeats > 0) {
     delay(400); 
    }
  }
}

/*
 * fade_down
 * fade the display to black
 */
void fade_down() {
  char intensity;
  for (intensity=14; intensity >= 0; intensity--) {
    ht1632_sendcmd(0, HT1632_CMD_PWM + intensity); //send intensity commands using CS0 for display 0
    ht1632_sendcmd(1, HT1632_CMD_PWM + intensity); //send intensity commands using CS0 for display 1
    delay(FADEDELAY);
  }
  //clear the display and set it to full brightness again so we're ready to plot new stuff
  cls();
  ht1632_sendcmd(0, HT1632_CMD_PWM + 15);
  ht1632_sendcmd(1, HT1632_CMD_PWM + 15);
}


/*
 * fade_up
 * fade the display up to full brightness
 */
void fade_up() {
  char intensity;
  for ( intensity=0; intensity < 15; intensity++) {
    ht1632_sendcmd(0, HT1632_CMD_PWM + intensity); //send intensity commands using CS0 for display 0
    ht1632_sendcmd(1, HT1632_CMD_PWM + intensity); //send intensity commands using CS0 for display 1
    delay(FADEDELAY);
  }
}


//display software version number
void printver(){

  byte i = 0;
  char ver[16] = "Clock V2.27";
  
  while(ver[i]) {
    ht1632_puttinychar((i*4) + 1, 1, ver[i]);
    i++;
  }
  delay(2000);
  cls();
}