sábado, 5 de marzo de 2011

Graficos en display HD44780 (1602)

Existen en el mercado montones de displays compatibles con HD44780. Yo tengo este: http://www.yerobot.com/arduino-lcd-shield.html.

El display es de 16 columnas por dos filas y puede mostrar una amplia variedad de caracteres, pero tiene un inconveniente: no puede mostrar gráficos... pero podemos hacer alguna trampa para conseguir algo útil.

Para empezar, manejamos bien el display con la biblioteca standard incluída con el entorno Arduino: http://www.arduino.cc/en/Reference/LiquidCrystal. No hace falta utilizar la que provee el fabricante. De hecho, es poco aconsejable usarla porque es más lenta y no funciona bien con las versiones nuevas.
El controlador HD44780 muestra caracteres en una matriz de puntos, así que nada impediría que pudiera dibujar cualquier cosa sobre ella. Sin embargo no está pensado para eso. No tiene comandos que permitan enviar pixels individuales sino sólo caracteres alfanuméricos.

El truco que usaré para hacer algunos gráficos consiste en aprovechar la capacidad del HD44780 para mostrar hasta ocho caracteres definidos por el usuario. De esa forma, en principio, en la zona que ocuparían ocho letras podemos poner lo que nos de la gana. Simplemente escribiríamos el carácter 0, el 1, el 2 y así hasta el 8 y luego los redefinimos para que tengan los pixels que nos haga falta.

Sólo hay un inconveniente: que el display LCD no tiene todos los pixels sino que deja una separación entre los caracteres. La cosa se ve más o menos así:





Físicamente no existen los pixels necesarios para dibujar un gráfico completo. Así que tendremos que conformarnos con otro tipo de gráficos. Tenemos 8 cuadros de 8x5 pixels separados por 1 pixel, para hacer lo que queramos. Así que voy a hacer una rutina para dibujar histogramas.

Podemos suponer que cada carácter tiene seix pixels, uno de los cuales siempre tendrá que estar apagado. Así que tendremos que adaptarnos a eso y tratar de que no se note. Para eso dibujaremos barras verticales pero separadas por un píxel entre ellas. De esa forma parecerá que la separación fue dejada a propósito. 


De esta forma, hay 24 columnas disponibles para mostrar información. La rutina utilizará, entonces, un vector con 24 bytes. En ellos se usarán valores entre 0 y 8 para indicar la altura de la barra a mostrar. 

byte grafica[24];

La gráfica se podrá ubicar en cualquier parte de la pantalla, simplemente escribiendo los caracteres del 0 al 7, que son los que se pueden redefinir. Por ejemplo una manera sencilla de hacerlo sería esta:

void setup(){
  lcd.begin(16, 2);

  lcd.clear();

  lcd.setCursor(8,0);
  lcd.print('\0');
  lcd.print('\1');
  lcd.print('\2');
  lcd.print('\3');
  lcd.print('\4');
  lcd.print('\5');
  lcd.print('\6');
  lcd.print('\7');
  }

Por supuesto, también hace falta información para mostrar. Esta deberá ser generada por cualquier programa que hagamos. Después daré un ejemplo práctico. Por ahora llenemos el vector con algo de información para probar.

for (a=0;a<9;a++) grafica[a]=a;

for (a=8;a<16;a++) grafica[a]=8;
Y ahora la rutina que convierte el contenido del vector "gráfica" componiendo los caracteres que se ven en la pantalla.
void mostrar_grafica(){
int d,dp;
byte bm[8];
int c,columna,fila,posicion,x;
int i;
for ( c=0;c<8;c++){ 
    memset(bm,0,8); //llenamos el caracter con ceros
    
    // cada caracter representa 3 datos
    
    //poner puntitos en la primera columna (entre 0 y 8)
    x=0;
    dp=c * 3;

    for (columna=0;columna<5;columna+=2){
        i=dp + 2-( columna/2 );
        d=grafica[i];
                   
        for (fila=0;fila<8;fila++){
            
            if (d>8) // dato invalido
              bm[fila] |= (fila & 1) <<columna;
            else 
              if (fila>=9-d) bm[fila] |= 1<<columna;
                 
            
            }//for fila
        x++;
        }//for columna
    lcd.createChar(c,bm);  
    }

}

Nótese que no es necesario volver a escribir los caracteres en la pantalla cuando se modifica la gráfica. El controlador se encargará de irlos modificando cuando se hace el lcd.createChar.

Finalmente, al usar la rutina el resultado queda así:


En el siguiente artículo, un ejemplo práctico de uso de esta rutina.