Control de GLCD de 128x64 puntos

Aunque son un poco mas costosos que los LCD de caracteres, pueden ser útiles a la hora de mostrar gráficamente algún parámetro, por ejemplo barras, animaciones, figuras geométricas, texto y también imágenes o fotos. 
Para manejar una imagen o foto por ejemplo tenemos que tener en cuenta que necesitamos de un microcontrolador con buena memoria y velocidad, ya que el renderisado se realiza de forma secuencial, por ejemplo el microcontrolador utilizado para este ejemplo no es suficiente para mostrar una imagen.
En este ejemplo se mostrara un voltimetro y amperimetro, dentro de unos recuadros, con texto informativo y demás.
Para este ejemplo estoy utilizando las librerías para CCS, HDM64GS12.c y graphics.c
La librería HDM64GS12, posee el control del GLCD propiamente dicho y dentro de la misma en su encabezado encontraremos el pinout del mismo para poder conectarlo. El GLCD debe tener el controlador KS0108, debemos tener en cuenta que en el pinout debe tener los pines de selección de chip CS1 y CS2.
La librería Graphics es la librería que nos permitirá dibujar y escribir en el mismo, que detallare las funciones principales de la misma (de todas formas pueden verlo en el encabezado de la librería).

Funciones para controlar el GLCD:

glcd_line(x1, y1, x2, y2, color)
Dibuja una linea desde la coordenada (x1,y1) hasta (x2,y2), la variable color puede ser ON o OFF, esta es la que habilita la linea o no (para mostrarla o no).

glcd_rect(x1, y1, x2, y2, fill, color)
Dibuja un rectángulo desde la coordenada (x1,y1) hasta (x2,y2), la variable fill es para rellenar el rectángulo si esta en YES, o solo dibuja el contorno con NO. la variable color al igual que en el resto de las funciones es la que habilita o no el rectángulo.

glcd_bar(x1, y1, x2, y2, width, color)
Dibuja una barra desde la coordenada (x1,y1) hasta (x2,y2), la variable width es el número de pixel de ancho de la barra, la variable color al igual que en el resto de las funciones es la que habilita o no la barra.

glcd_circle(x, y, radius, fill, color)
Dibuja un circulo en la coordenada (x,y), la variable radius es el numero de pixeles de radio de del circulo, a variable fill es para rellenar el circulo si esta en YES, o solo dibuja el contorno con NO. la variable color al igual que en el resto de las funciones es la que habilita o no el circulo.

glcd_text57(x, y, textptr, size, color)
Escribe texto en la coordenada (x,y), la variable textptr es la variable puntero donde esta alojado el carácter o string, a variable size es el tamaño del carácter, la variable color al igual que en el resto de las funciones es la que habilita o no el texto.

glcd_pixel(x, y, color)
Dibuja un punto o pixel en la dirección de la coordenada (x,y), la variable color al igual que en el resto de las funciones es la que habilita o no el punto o pixel.


A continuación mostrare el pinout del GLCD:



En este programa realizaremos un voltimetro y amperimetro que utilizara 2 canales ADC.
Se implementa el PIC16F887 por la cantidad de pines y puertos del mismo, recordemos que este GLCD requiere de una cantidad considerable de pines.

  1. #include <16F887.h>
  2. #device adc=10
  3. #fuses HS,NOWDT,NOPROTECT,NOLVP
  4. #use delay(clock=20000000)
  5. #include <HDM64GS12.c>
  6. #include <graphics.c>
  7. void main(){
  8.    int16 voltajeADC, voltajeADC_ant,
  9.          corrienteADC, corrienteADC_ant;
  10.    setup_adc_ports(sAN0|sAN1|VSS_VDD);
  11.    setup_adc(ADC_CLOCK_DIV_2);
  12.    char nombre[]="Electgpl";
  13.    char parametro[]="   [V]       [A]   ";
  14.    char voltaje[9];
  15.    char corriente[9];
  16.    glcd_init(ON);
  17.    glcd_rect(1, 1, 127, 63, NO, ON);
  18.    glcd_rect(3, 3, 125, 13, NO, ON);
  19.    glcd_text57(41, 5, nombre, 1, ON);
  20.    glcd_rect(3, 15, 125, 25, NO, ON);
  21.    glcd_text57(8, 17, parametro, 1, ON);
  22.    glcd_rect(3, 27, 125, 61, NO, ON);
  23.    while(true){
  24.       set_adc_channel(0);
  25.       delay_us(10);
  26.       voltajeADC=read_adc();
  27.       if(voltajeADC_ant != voltajeADC){
  28.          glcd_text57(13, 38, voltaje, 2, OFF);
  29.          sprintf(voltaje, "%1.2f", (float)voltajeADC*5/1023.0);
  30.          voltaje[4] = '\0';
  31.          glcd_text57(13, 38, voltaje, 2, ON);  
  32.          voltajeADC_ant=voltajeADC;
  33.       }
  34.       set_adc_channel(1);
  35.       delay_us(10);
  36.       corrienteADC=read_adc();
  37.       if(corrienteADC_ant != corrienteADC){
  38.          glcd_text57(70, 38, corriente, 2, OFF);
  39.          sprintf(corriente, "%1.2f", (float)corrienteADC*5/1023.0);
  40.          corriente[4] = '\0';
  41.          glcd_text57(70, 38, corriente, 2, ON);  
  42.          corrienteADC_ant=corrienteADC;
  43.       }  
  44.    }
  45. }

Como podemos ver en el código fuente, incluimos las dos librerías necesarias para el GLCD, luego definimos las variables para el ADC y las variables character donde alojaremos los strings a mostrar y las dos variables dinámicas de tensión y corriente.
Luego podemos encontrar la configuración de los dos canales de ADC y seguida de la misma la primera instrucción del GLCD que sera la inicialización del mismo que lo borrara cuando el argumento de la función sea ON.
Ahora pasaremos a la función principal de loop donde se aloja el programa.
Ya que la medición de corriente como la de tensión poseen el mismo código, solo explicare una y la otra es igual.
Como podemos ver, seteamos el canal ADC a leer, esperamos los 10us para conversión segura y luego volcamos el contenido del ADC (de 0 a 1023 por estar seteado a 10bit) a la variable voltajeADC.
Por ahora voy a saltear el if y el intercambio de variables, ya que quiero demostrar de forma sencilla el funcionamiento.
La primer función para escribir de forma dinámica, necesitamos la función sprintf se utiliza para escribir strings por eso la letra s antes de printf, y aquí ingresaremos el string (en este caso voltaje[], luego le sigue el formato de dato "%1.2f" que nos dará un entero y dos decimales y por ultimo el valor que vamos a volcar al string (en este caso realizamos todo el acondicionamiento de la entrada en el mismo lugar, que es multiplicar el valor del ADC por 5V que es el fondo de escala y dividirlo por los 1023 pasos de resolución, así nos mostrara un valor de tensión de 0 a 5V).
Luego podemos encontrar el string voltaje[4] que se iguala a '\0', que se encarga de delimitar el máximo de caracteres a 4.
Ahora debemos enviar el valor que hemos convertido a string en la función sprintf a la función del GLCD glcd_text57() donde damos origen al texto en la posición (13,38), luego viene la variable string, el tamaño de la letra que le he puesto valor 2 (es un valor mediano como para que entren las dos variables) y por ultimo ON que sera el que habilita esta función para que se muestre el valor.
Ahora si nosotros cambiamos un valor por otro, que es lo que va a pasar al medir con el ADC, se sobre escribirán uno sobre el otro y no se entenderá lo que se lee, para ello es el uso del if y el intercambio de variables, como pueden ver el if es verdadero cuando el valor del ADC es distinto al ultimo valor leido por el ADC, esto es para ver si el valor cambio o no.
Una vez que es verdadero porque el valor cambia, se procede a la función glcd_text57 pero con el ultimo valor del argumento OFF ya que esto apaga e texto en ese momento y da lugar a la actualización del mismo, y luego lo pone ON y lo deja ahi hasta que se vuelva a actualizar el valor del ADC y entre al if.
De esta forma se logra actualizar en la pantalla de forma dinámica el valor.



51 comentarios:

  1. tengo una consulta si quiero gregar una medición mas repito esta parte del codigo
    set_adc_channel(0);


    25. delay_us(10);


    26. voltajeADC=read_adc();


    27. if(voltajeADC_ant != voltajeADC){


    28. glcd_text57(13, 38, voltaje, 2, OFF);


    29. sprintf(voltaje, "%1.2f", (float)voltajeADC*5/1023.0);


    30. voltaje[4] = '\0';


    31. glcd_text57(13, 38, voltaje, 2, ON);


    32. voltajeADC_ant=voltajeADC;
    y porque se ven muy oscuros los dígitos cuando cambio el valor de tencion

    ResponderEliminar
    Respuestas
    1. Hola, si lo que queres es tener tres mediciones, es decir leer tres canales ADC y mostrar tres valores en el GLCD, tenes que clonar las lineas de la 24 a 33, y setear set_adc_channel(2); ya que el 0 y el 1 ya los estas usando. Aunque no es solo eso, ya en el mapa de la pantalla no entran otros dos números, tendrías que jugar un poco con los tamaños de los dígitos (achicarlos un poco para que entren dos números mas), tal vez redistribuirlos de otra forma, etc.. Todo lo que es escribir en pantalla se maneja las funciones comentadas al principio para escribir mediante text57, para hacer los rectangulos, etc... La realidad es que posiblemente te cambien todas las funciones del GLCD para que te logren entrar los 3 valores en la misma pantalla y eso.
      Te recomendaría que primero juegues un poco con las funciones de rectángulo, texto, etc y lo simules en el Proteus para familiarizarte con estas funciones y luego ya vas a darte cuenta de esto de como mostrar los valores y como ordenarlos en pantalla a tu propio gusto.
      Saludos!

      Eliminar
  2. si lo pude armar, gracias , ahora cuando tomo la medición los números se van poniendo como una mancha negra en el proteus, tendría que probarlo en la realidad para ver si se ven bien o me hace lo mismo, que puede ser?

    aparte felicitarte muy buena pagina¡¡

    ResponderEliminar
    Respuestas
    1. Es raro que en el proteus se vea como una mancha negra, no te quedo ningún número superpuesto? proba comentando cada bloque (bloque ejemplo de 24 a 32), de esa forma si tenes los tres bloques de las tres mediciones, podes comentar 2 de ellos y ver el que queda como se ve en la pantalla sin que se te pisen los números y demás. lo podes ir probando asi para los tres, una vez que tenes eso ahí des-comentas todo y lo corroboras, pero es raro que te haga esa mancha.
      A veces pasa que en la realidad funciona diferente pero no se si sea el caso, antes de que compres el GLCD proba ir comentando el código para que veas si te anda bien de a un numero por vez.
      Bueno Gracias!!
      Cualquier cosa avísame!

      Saludos.

      Eliminar
  3. te consulto porque la línea 38 es la misma que la 41 con la difencia del on of ?

    ResponderEliminar
    Respuestas
    1. Es la misma linea pero en una enciende y en la otra apaga, eso lo hace a modo "refresco" de pantalla para que la variación numérica, sin ese refresco (si dejaríamos siempre habilitado el valor) se pisaría el valor actual con el próximo que lee el ADC. De esta forma se genera el refresco de pantalla en el sector del numero solamente y no en el resto, asi no se ve el parpadeo.
      Saludos!

      Eliminar
  4. si cuando muevo el "pote" me muestra los valores pixelados y cada ves que vas moviendo se pone como una mancha negra te subo el código si queres para verlo

    ResponderEliminar
    Respuestas
    1. Claro, eso debe ser porque se están pisando, porque no se esta borrando el display en cada cambio de dígito para que no se distorsione. Si queres pasame el código asi lo pruebo acá.
      Saludos!

      Eliminar

  5. #include <18f4550.h>
    #device adc=10
    #fuses HS,NOWDT,NOPROTECT,NOLVP
    #use delay(clock=20000000)
    #include
    #include
    #use fast_io(b)
    #use fast_io(c)


    char cps[]="CPS";
    char volt[]="V";
    char amper[]="A";

    char voltaje[9];
    char minuto[9];
    char corriente[9];

    int16 voltajeADC, voltajeADC_ant,
    cpsADC,cpsADC_ant,
    corrienteADC, corrienteADC_ant;



    void main(){





    setup_adc_ports(AN0_to_an2|VSS_VDD);
    setup_adc(ADC_CLOCK_DIV_32);





    glcd_init(ON);


    glcd_line(0, 25, 125, 25, on);


    while(true){

    set_adc_channel(0);
    delay_us(62);
    voltajeADC=read_adc();
    if(voltajeADC_ant != voltajeADC){
    glcd_text57(5, 3, voltaje, 1, OFF);
    sprintf(voltaje, "%1.1f", (float)voltajeADC*5/1023.0);
    voltaje[4] = '\0';
    glcd_text57(5, 3, voltaje, 2, ON);
    voltajeADC_ant=voltajeADC;
    glcd_text57(45,12,volt,1,on);
    }


    set_adc_channel(1);
    delay_us(62);
    corrienteADC=read_adc();
    if(corrienteADC_ant != corrienteADC){
    glcd_text57(65, 3, corriente, 1, OFF);
    sprintf(corriente, "%1.1f", (float)corrienteADC*5/1023.0);
    corriente[4] = '\0';
    glcd_text57(65, 3, corriente, 2, ON);
    corrienteADC_ant=corrienteADC;
    glcd_text57(105,12,amper,1,on);

    }

    set_adc_channel(2);
    delay_us(62);
    cpsADC=read_adc();
    if(cpsADC_ant != cpsADC){
    glcd_text57(5, 3, minuto, 1, OFF);
    sprintf(minuto, "%02.2f", (float)cpsADC*5/1023.0);
    minuto[4] = '\0';
    glcd_text57(30, 40, minuto, 3, ON);
    cpsADC_ant=cpsADC;
    glcd_text57(105,55,cps,1,on);
    }
    }

    }

    ResponderEliminar
  6. en la ultima medición estoy tratando de medir frecuencia tengo que cambiar los decimales por la medición de 3 cifras sin coma

    ResponderEliminar
    Respuestas
    1. Ok, en un rato cuando llego a casa lo miro y te aviso por este medio que resultados me dio!
      Saludos!

      Eliminar
  7. Respuestas
    1. Hola, ya lo tengo andando, proba este código. http://pastebin.com/JvWnMiej
      Saludos!

      Eliminar
  8. Si lo voy a probar estube modificando y logre que ande y le agregue una imagen voy mejorando, gracias saludos

    ResponderEliminar
    Respuestas
    1. Por nada!, ahí en el código que te pase ya estaría andando al menos lo de que se superponían los dígitos y el mapeo.
      Saludos!

      Eliminar
  9. como estas no te quiero molestar, pero te consulto tengo un parpadeo que no puedo sacar como hago para que queden bien quietos los números, se me desvanecen de un lado al otro siempre es raro, saludos

    ResponderEliminar
    Respuestas
    1. Hola, el parpadeo supongo que se debe al refresco de pantalla, ya que para lograr de que no se vea ese cumulo de números en el mismo dígito hay que habilitar, mostrar, deshabilitar y consultar si el numero cambio para ver si hay que actualizarlo o no, en ese ciclo hay un pequeño parpadeo, te recomendaría bajar el delay de ese proceso, o bien corregir la histeresis para que el numero no cambie tanto (de esa forma no actualice tan seguido), para ello podrías demorar el muestreo o bien sacar promedio.
      Saludos.

      Eliminar
  10. Si pensaba en sacar promedios con un bucle for y después mostrar el resultado
    Voy a ver como lo hago, saludos

    ResponderEliminar
    Respuestas
    1. Si, es la forma mas fácil, podes tomar valores, alojarlos en un vector y después sumar cada valor del vector y dividirlo por el indice máximo., va hay muchas formas de hacerlo.
      Saludos.

      Eliminar
  11. Lo hago y te cuento como me va, gracias saludos

    ResponderEliminar
  12. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  13. set_adc_channel(2);
    delay_us(20);
    cpsadc=0;
    for(var1=0;var1<500;var1++)
    {
    cpsadc=cpsadc+ read_adc();
    delay_us(62);
    }

    glcd_text57(30, 40, minuto, 3, ON);

    cpsadc = cpsadc * 50 / 1023;
    sprintf(minuto, "%1.1f", (float)cpsADC);
    minuto[5] = '\0';


    que me falta para lograr el promedio y mostrarlo en el glcd

    ResponderEliminar
    Respuestas
    1. Esta bien, haces un acumulador sumando el actual mas el anterior con la linea cpsadc=cpsadc+read_adc(); por 500 veces, y luego lo ideal seria tomar ese valor y dividirlo en 500 para que se complete el promedio, por ahí 500 son muchos valores, pensa que los números crecen y podría darte un overflow según el tipo de variable.

      Eliminar
  14. Me esta volviendo loco y no logro hacerlo andar

    ResponderEliminar
  15. Me esta volviendo loco y no logro hacerlo andar

    ResponderEliminar

  16. int16 cpsADC,cpsADC_ant;

    int var1;

    setup_adc_ports(san0|san1|san2|VSS_VDD);

    setup_adc(ADC_CLOCK_DIV_2);




    char minuto[9];




    glcd_init(ON);



    while(true)
    {




    set_adc_channel(2);
    delay_us(20);
    cpsadc=0;
    for(var1=0;var1<500;var1++)
    {
    cpsadc=cpsadc+ read_adc();
    delay_us(62);
    }
    cpsADC_ant=cpsadc/500;
    glcd_text57(30, 40, minuto, 3, ON);

    sprintf(minuto, "%1.1f", (float)cpsADC_ant* 50 / 1023);
    minuto[5] = '\0';





    }


    }

    sabes que no me funciona con el for

    ResponderEliminar
  17. Me pone que la condición no es verdadera y me marca la línea del for

    ResponderEliminar
  18. Me pone que la condición no es verdadera y me marca la línea del for

    ResponderEliminar
    Respuestas
    1. Que raro, que no te compile un for, tal vez tengas un overflow, podes probar con un while y las variables de control dentro del mismo.

      Eliminar
  19. Respuestas
    1. Me pone una advertencia y no muestra nada en pantalla

      Eliminar
  20. Respuestas
    1. En el For le das la configuracion con i=0, i<500 y i++, en el while no podes poner todas esas condiciones, pero si podes hacer que sea verdadero o no. Por ejemplo, para que el while se cumpla tiene que ser verdadero, ahi podes poner que i menor a 500, quedando while(i<500){ }, de esta forma el while se cumple siempre que i<500 sea verdadero. Para que i se incremente en uno cada vez que se repite el ciclo tenes que poner dentro del while un i++; y i=0 tenes que ponerlo fuera del mismo.
      i=0;
      while(i<500){
      codigo
      codigo
      codigo
      ...
      i++;
      }

      Eliminar
  21. Puedo colocar varios while uno por cada medición?

    ResponderEliminar
    Respuestas
    1. Si claro, el while es solo un bucle condicional como el For que ejecuta el código en su interior si la condición es verdadera. Al manejar menos parámetros que el for tenes que poner el incremento y eso adentro, pero el uso es el mismo.

      Eliminar
  22. Puedo colocar varios while uno por cada medición?

    ResponderEliminar
  23. lo hice funcionar mejor estaba usando un cristal de 4mhz lo cambie y funciono mucho mejor ahora los números se mueven muy rápido le tengo que poner un capacitor de 10uf para que quede un poco estable estará mucho ruido , creo que igual voy a tener que hacer promedios para que quede mas estable la medición, con if no puedo promediar?
    voy a probar con el while

    ResponderEliminar
    Respuestas
    1. Claro, me olvide de ese detalle, yo en el código del post lo tengo con 20MHz, creo que ese era un tema también por eso lo use asi.
      El capacitor en la entrada es clave para que no fluctué tanto.
      Podes hacer ese promedio, o podes poner un delay, para que tome muestras mas lentas.
      El promedio podes hacerlo con muchas funciones diferentes, la lógica que necesites depende de como pienses tu promedio, pero si podes proba primero dándole un delay entre cada medición del ADC para que actualice mas lento.
      Saludos.!

      Eliminar
  24. Si pude hacer andar el for pero no funciona bien tengo que ver como implementar el for y que haga efecto en el código, no se si ponerlo después de if o antes y después que el if haga la comparación de la variable.

    ResponderEliminar
    Respuestas
    1. Posteame el código asi puedo verlo mejor, asi te puedo ayudar mas fácil.

      Saludos.

      Eliminar
  25. while(true){

    set_adc_channel(0);
    delay_us(20);
    volt1=0;
    if(volt1 != voltajeADC){

    for(var1=0;var1<5;var1++)
    {
    volt1 = volt1+read_adc();
    delay_us(62);
    }

    glcd_text57(5, 3, voltaje, 2, OFF);

    voltajeadc=volt1/5;

    voltajeadc=voltajeadc*50/1023;

    sprintf(voltaje, "%4.1f", (float)voltajeADC);
    voltaje[4] = '\0';
    glcd_text57(5, 3, voltaje, 2, ON);
    volt1=voltajeADC;






    }


    }
    }

    ResponderEliminar
    Respuestas
    1. Lo que veo a simple vista es que esta algo desordenado el codigo, lo que yo haria es primero leer el ADC (set adc channel, y read adc(), lo alojas en una varibale, luego el delay de conversion de 20us).
      Una vez que tenes el valor, recien ahi lo metes en un for de mas de 5 valores para que fluctue menos, una vez que sale del for, tomas esa variable y la dividis por la cantidad de ocurrencias que le pusiste al for.
      y recien despues de eso aplicas el calculo para pasarlo a tension
      y despues lo mostras en el GLCD.
      Para que sea mas estructurado y sencillo.

      Eliminar
  26. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  27. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  28. Lo probé y no funciona no cambia que tildado en 2 volt y baja a cero pero borroso, no se me ocurre como hacer que cambie dígito a dígito y de una forma más lenta, voy a ver si con el if puedo lograr algo, gracias saludos

    ResponderEliminar
  29. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  30. Bueno logre que queden fijos los dígitos con capacitores pero ahora no logro promediar y cuando toma la lectura adc se mueve muy rápido alguna idea como hacer un promedio, gracias

    ResponderEliminar
  31. Este comentario ha sido eliminado por el autor.

    ResponderEliminar