Diferencia entre revisiones de «ST7920»

De Proyectos
Ir a la navegación Ir a la búsqueda
 
(No se muestran 42 ediciones intermedias del mismo usuario)
Línea 10: Línea 10:


Explicación gráfica:
Explicación gráfica:
[[File:HCGROM.jpg]]
 
 
[[File:HCGROM.jpg|400px]]




Línea 25: Línea 27:




[[File:HCGROM01.jpg]]
[[File:HCGROM01.jpg|400px]]




Línea 32: Línea 34:




[[File:HCGROM02.jpg]]
[[File:HCGROM02.jpg|400px]]




*Como vemos en la imagen de arriba la AC 0x00 se ha dividido en dos y la letra ''A'' se ha escrito en la primera ''subcasilla''. Esta ''subcasilla'' es el espacio que ocupa un caracter de la HCGROM (como hemos visto más arriba)
*Como vemos en la imagen de arriba la AC 0x00 se ha dividido en dos y la letra ''A'' se ha escrito en la primera ''subcasilla''. Esta ''subcasilla'' es el espacio que ocupa un caracter de la HCGROM (como hemos visto más arriba)
*Ahora queremos dejar dos espacios en blanco y escribir ''B'', por lo tanto queremos escribirla en la ''subcasilla'' 4. No se puede acceder a esta ''subcasilla'' porque la AC 0x01 empieza con la ''subcasilla'' 3 y no hay manera de acceder la ''subcasilla'' 4 directamente
*Ahora queremos dejar dos espacios en blanco y escribir ''B'', por lo tanto queremos escribirla en la ''subcasilla'' 4. No se puede acceder a esta ''subcasilla'' porque la AC 0x01 empieza con la ''subcasilla'' 3 y no hay manera de acceder la ''subcasilla'' 4 directamente
*Para poder escribir en la ''subcasilla'' 4 tendré que enviar el comando Set CGRAM con la dirección de AC 0x01 y luego enviar el comando Write Ram con el hexadecimal del ''espacio'' y otra vez el comando Write Ram con el hexadecimal de ''B''
*Para poder escribir en la ''subcasilla'' 4 tendré que enviar el comando Set CGRAM con la dirección de AC 0x01 y luego enviar el comando Write Ram con el hexadecimal del ''espacio'' y otra vez el comando Write Ram con el hexadecimal de ''B''. El código quedaria asi:
 
 
<pre>
  /*La función send envia la instrucción/dato y especifica si es un comando (CMD) o es un dato (DATA). El comando tiene el RS a 0 y el dato tiene el RS a 1*/
 
  send(0x81, CMD);  //Seleccionamos la AC 0x01 con la instrucción básica Set DDRAM Address
 
  send(0x20, DATA);  //Envio un espacio (en ascci 0x20 es un espacio)
  send(0x42, DATA);  //Envio la letra B
</pre>


== DDRAM ==
== DDRAM ==
Línea 91: Línea 103:
== Set Graphic Display Ram Address ==
== Set Graphic Display Ram Address ==
Esta instrucción es la que se usa para pasar una dirección de memoria a la AC (que recordemos que es la dirección que necesitamos para saber donde dibujar un pixel, es decir las cordenadas X e Y).<br/>
Esta instrucción es la que se usa para pasar una dirección de memoria a la AC (que recordemos que es la dirección que necesitamos para saber donde dibujar un pixel, es decir las cordenadas X e Y).<br/>
En la imagen se ven dos filas porque se tiene que enviar dos veces esta instrucción, una para el eje X y la otra para el eje Y. La primera instrucción se envia para determinar la X y la segunda para determinar la Y.<br/>
En la imagen se ven dos filas porque se tiene que enviar dos veces esta instrucción, una para el eje X y la otra para el eje Y. La primera instrucción se envia para determinar la Y y la segunda para determinar la X.<br/>
En el apartado [[ST7920#Relación AC con GDRAM|Relación AC con GDRAM]] hay una imagen explicativa de la relación entre la AC y la GDRAM
En el apartado [[ST7920#Relación AC con GDRAM|Relación AC con GDRAM]] hay una imagen explicativa de la relación entre la AC y la GDRAM


= Como funciona la comunicación Serial (SPI) =
= Como funciona la comunicación Serial (SPI) =
Línea 146: Línea 157:


== Modo serie (SPI) ==
== Modo serie (SPI) ==
La inicialización del lcd en modo serie no está especificada en el datasheet, este solo especifica la inicialización en paralelo 4 bits y 8 bits. Por lo que he visto en los códigos (del propio vendedor y otros) la inicialización en modo serie es igual que en 8 bits.<br/>
<br/>
Las dos primeras instrucciones que se envian (Function Set) en el modo paralelo 8 bits colocan el bit DB2 en 0 para indicar que se inicializa en modo 8bits. Por las pruebas que yo he hecho da igual si se coloca este bit a 0 o a 1 cuando utilizamos el modo serie. En los ejmeplos que he ido poniendo en toda esta wiki he puesto este bit a 0.
<br/><br/>


= Modo Gráfico (GLCD) =
== Funciones Modo Gráfico ==
En el modo gráfico interviene varias instrucciones, tanto básicas como extendidas. Las que conozco yo porque las he utilizado son las que explicaré:
*[[ST7920#Set Graphic Display Ram Address|Set Graphic Display Ram Address]]
*[[ST7920#Funciones Básicas|Write to RAM]]
**Esta instrucción básica sirve para "dibujar" pixeles cuando el lcd está en modo gráfico.
**Cada instrucción Write to RAM puede "dibujar" 8 pixeles, la siguiente imagen ilustra como se codifican los pixeles:
<br/>
[[File:Esquema_Pixeles.jpg|600px]]
<br/><br/>
*Function Set
**Tal como dije en el apartado [[ST7920#Function Set|anterior]] esta instrucción nos sirve para acceder al modo extendido y así poder utilizar las instrucciones que afectan al modo gráfico.
**Cada vez que se quiera acceder a un pixel, antes se ha de poner en modo extendido
**La forma de poner en modo extendido es enviando el comando 0x34
*Function Set con G activado
**Hay otra instrucción Function Set en el modo extendido
**Esta instrucción enciende o apaga el display con el bit G (se puede ver en la imagen del apartado [[ST7920#Funciones Extendidas|Funciones Extendidas]])
**La instrucción con G activado equivale al comando 0x36
<br/>


= Modo Gráfico (GLCD) =
El modo gráfico es en el que tenemos control total sobre todos los pixeles del lcd. con esto podemos dibujar todo tipos de formas, dibujos o incluso tener fuentes de diferentes tamaños y estilos.
== Relación AC con GDRAM ==
== Relación AC con GDRAM ==
Antes he explicado una peculiaridad de la AC en el apartado [[ST7920#Peculiaridad al escribir un caracter de la HCGROM|Peculiaridad al escribir un caracter de la HCGROM]]. En el modo gráfico también se tiene que tener en cuenta porque cuando se quiere dibujar un pixel se tiene que seleccionar una AC. Esto se verá en más profundidad en el siguiente apartado, [[ST7920#Proceso al dibujar uno o varios pixeles|Proceso al dibujar uno o varios pixeles]]<br/>
=== Explicación Coordenadas ===
Dibujo de las coordenadas del lcd:
 
 
[[File:Coordenadas.jpg]]
 
 
Imaginemos que quiero dibujar el pixel (0,0) (el que es de color negro de la imagen). Tengo que decirle al lcd donde esta el pixel que quiero dibujar y por eso le tengo que pasar unas "coordenadas".<br/>
Estas coordenadas se las pasaré con la instrucción extendida [[ST7920#Set Graphic Display Ram Address|Set Graphic Display RAM Addres]].<br/>
Primero tengo que enviar la instrucción para determinar la Y, que es la fila. Como en este caso queremos la fila 0 le enviaremos la instrucción:
send(0x80)  //0x80 (instrucción Set Graphic Display Ram Address ) + 0x00 (fila 0) = 0x80
Luego tengo que enviar la instrucción para determinar la X, que es la AC. Hay 8 AC, por tanto la X va desde la 0 a la 7.<br/>
La AC agrupa dos "subcasillas" de 8 bits cada una (cada bit representa un pixel). Estas "subcasillas" representan instrucciones de Write to RAM y esta instrucción puede dibujar 8 pixeles diferentes. Para poder dibujar la AC entera necesito enviar dos instrucciones Write to RAM, una para dibujar cada subcasilla.<br/>
Por lo tanto para este ejemplo la AC que tengo que "seleccionar" es la 0x00
send(0x80)  //0x80 (instrucción Set Graphic Display Ram Address ) + 0x00 (AC 0) = 0x80
Finalmente pasaré a dibujar el pixel con la instrucción Write to RAM, pero eso ya se verá como se hace en la sección [[ST7920#Proceso al dibujar uno o varios pixeles|Proceso al dibujar uno o varios pixeles]]
<br/><br/>
=== Peculiaridad AC ===
<br/>
Esta peculiaridad tiene que ver con la necesidad de enviar dos instrucciones Write to RAM para poder dibujar un AC completo.Me explico.<br/>
Si yo quiero dibujar un pixel en la subcasilla 01, por ejemplo el pixel número 8 de la fila 0 tengo que enviar dos instrucciones Write to RAM, una para escribir la subcasilla 00 con todos los bits a 0 (para que los pixeles esten apagados) y otra con solo el bit 8 a 1 y los demas a 0.<br/>
Asi que cada vez que queramos escribir un pixel tenemos que escribir un AC completo, porque no se puede acceder a la segunda subcasilla de un AC directamente, se han de escribir las dos subcasillas a la vez. (Esto no es del todo cierto porqué si que hay un caso donde se puede escribir cada subcasilla, se verá en el apartado [[ST7920#Dibujar todos los pixeles|Dibujar todos los pixeles]])
<br/><br/>
=== Peculiaridad Fila ===
<br/>
Esta peculiaridad se presenta cuando queremos dibujar un pixel a partir de la fila 32.<br/>
En el dibujo anterior se puede ver que cuando la fila va desde la 0 hasta la 31 la numeración en hexadecimal sigue esa correlación, es decir que para la fila 0 tenemos que enviar al lcd el número 0x00, para la 1 enviamos 0x01 y asi hasta la 31 que enviamos 0x1F (que es 31 en hexadecimal).<br/>
Se puede ver en el dibujo que cuando llegamos a la fila 32 tenemos que enviar 0x00 al lcd para decir que es la 32, la 33 sera 0x01 hasta la 63 que será 0x1F. Cuando llegamos a la "segunda mitad" de la pantalla se "resetea" la numeración de las filas.<br/>
La AC también se comporta de forma peculiar. La pantalla tiene 8 AC (que van desde la 0x00 hasta la 0x07) en cada fila. Mientras estamos en la "mitad superior de la pantalla" (es decir de la fila 0 a la 31) la AC 0 se accede a través de 0x00, la AC 1 a través de la 0x01 y asi hasta la AC 7 a través de la 0x07. Cuando pasamos la fila 31 la AC 0 se accede a traves de 0x08, la AC 1 sera la 0x09 y así hasta la AC 7 que será a través de la 0x0F.<br/>
<br/>
<br/>
A parte de esta peculiaridad también nos encontramos con otra. Esta tiene que ver con las filas y columnas.<br/>
== Proceso al dibujar uno o varios pixeles ==
=== Dibujar un pixel concreto ===
A través de 3 ejemplos explicaré como dibujar un pixel y como intervienen las Peculiaridades AC y Fila
 
*Ejemplo 1: Código dibujar pixel 2,3 (azul)
**La fila es la 2 (0x02)
**La AC (columna) es la 0 (0x00)
**A la función Write to RAM se le pasará 0x10. Si nos fijamos en el punto Write to RAM que hay en la sección [[ST7920#Funciones Modo Gráfico|Funciones Modo Gráfico]], vemos que el pixel 3 equivale a 0x10
<br/>
<br/>
Para poder explicarla primero se ha de explicar como se divide en coordenadas la pantalla:<br/>
<pre>
/*La función send envia la instrucción que le paso por parámetro al lcd y especifica mediante CMD o DATA si RS vale 1 o 0*/
 
                    //Primero se envia el comando Function Set con RE a 1
  send(0x34, CMD);
 
                    //Luego se envia la fila (Y)
  send(0x82, CMD);  //0x80 equivale a la instrucción extendida Set Graphic Display RAM Address (0x80) + Y (0x02)
 
                    //Después se envia la AC (X)
  send(0x80, CMD);  //0x82 equivale a la instrucción extendida Set Graphic Display RAM Addres (0x80) + AC (0x00)
 
  /*Ahora se dibuja los pixeles con la instrucción básica Write to RAM*/
 
                    //Como Write to RAM es una instrucción básica, antes de poder utilizarla hemos de cambiar a instrucciones básicas
  send(0x30, CMD);  //0x30 es la instrucción básica Function Set con RE = 0
 
  send(0x10, DATA); //Se dibuja el pixel (2,3)
 


  send(0x34, CMD);  //Volvemos al modo extendido


[[File:Coordenadas.jpg]]
  send(0x36, CMD);  //Instruccion extendida Extended Function Set con G en 1 (display on)
</pre>
 
 
*Ejemplo 2: Teniendo en cuenta Peculiaridad AC - dibujar pixel (2,8) (rojo)
**Quiero dibujar el pixel (2,8) (el rojo) pero sin que se borre el pixel (2,3) dibujado en el anterior Ejemplo
**La fila será la misma que en el ejemplo anterior 0x02
**La AC seguirá siendo la 0
**La "subcasilla" será la segunda porqué la primera "subcasilla" va desde el pixel 0 al 7 y yo quiero encender el 8
**En este ejemplo se ve que se tiene que tener en cuenta que pixeles hay dibujados en la primera "subcasilla" antes de dibujar pixeles de la segunda "subcasilla"
<br/>
<pre>
  send(0x34, CMD);
 
  send(0x82, CMD);  //Seleccionamos la fila 2
  send(0x80, CMD);  //Seleccionamos la AC 0
 
  send(0x30, CMD);  //Si solo enviara la instrucción Write to RAM que dibuja el pixel 8 entonces el que tenia
  send(0x10, DATA); //dibujado (el pixel 3) se borraria. Por eso he de enviar primero la instrucción Write to RAM
  send(0x80, DATA); //que dibuja la primera "subcasilla" y luego la que dibuja la segunda "subcasilla"
 
  send(0x34, CMD);
  send(0x36, CMD);
</pre>
 
 
*Ejemplo 3: Teniendo en cuenta la Peculiaridad Fila - dibujar pixel (32,0) (amarillo)
**La fila será la 32, que según el dibujo de las [[ST7920#Explicación Coordenadas|coordenadas]] equivale a 0x00
**La AC será la primera de la "segunda mitad" de la pantalla, por tanto equivale a 0x08
**Aunque este pixel pertenezca a la primera AC, no será la 0x00 sino la 0x08. Si pusiese la 0x00 se dibujaria el pixel (0,0)
<br/>
<pre>
  send(0x34, CMD);
 
  send(0x80, CMD);  //Seleccionamos la fila 32
  send(0x88, CMD);  //Seleccionamos la AC0 que equivale a la 0x08
 
  send(0x30, CMD);
  send(0x80, DATA); //Escribimos el pixel 0 de la primera "subcasilla"


  send(0x34, CMD);
  send(0x36, CMD);
</pre>


Esto es una representación gráfica de la pantalla. Esta indicado que hay 128 columnas y 64 filas. Este dibujo también me servirá para explicar como se dibuja un pixel.<br/>
=== Dibujar todos los pixeles ===
Aqui voy a explicar como dibujar toda la pantalla en una sola función. Esto es útil para limiarla (dibujamos todo 0x00 y asi apagamos todos los pixeles) o para dibujar una imagen cargada en memoria (por ejemplo un logo o algo similar).<br/>
Al dibujar la pantalla entera se ha de tener en cuenta la [[ST7920#Peculiaridad Fila|Peculiaridad Fila]], pero no es necesario tener en cuenta la [[ST7920#Peculiaridad AC|Peculiaridad AC]], esto se verá en el ejemplo.<br/>
<br/>
<br/>
Para poder dibujar en el pixel 0 de la fila 0, tengo que indicar unas coordenadas X e Y. La coordenada Y son las filas y la coordenada X es la AC. Si quiero acceder al pixel 0,0 tengo que seleccionar la AC 0x00 y luego dibujar los 16 pixeles que contiene esa AC. Si solo quiero dibujar el primer pixel entonces el primero lo pondré a 1 y los demas a 0.<br/>
Excribo el código de ejemplo:
<pre>
<pre>
instrucción para seleccionar la AC 0x00
 
instruccion para seleccionar la fila 0
for(int j=0; j<32; j++){
instruccion para escribir los 8 primeros bits    |  Esto funciona como la HCGROM
  send(0x34, CMD);
instruccion para escribir los 8 siguientes bits  |
 
En la primera instruccion de escritura de 8 bits el bit mas alto estara puesto en 1
  send(0x80+j, CMD);
  send(0x80, CMD);
 
  send(0x30, CMD);
  for(int i=0; i<16; i++){
    send(0x00, DATA);  //A la función send le puedo pasar 0x00 para limpiar la pantalla o una matriz donde esta cargada la imagen o logo que quiera dibujar
  }
}
for(g=0; g<32; g++){
  send(0x34, CMD);
 
  send(0x80+g, CMD);
  send(0x88, CMD);
 
  send(0x30, CMD);
  for(int h=0; h<16; h++){
    send(0x00, DATA);
  }
}
send(0x34, CMD);
send(0x36, CMD);
 
</pre>
</pre>
Con este código lo que tendremos es el pixel 0,0 encendido.<br/>
 
Esta es una forma muy básica de dibujar la pantalla entera, es meramente didáctica. Los dos bulces, por ejemplo, no son necesarios, pero así queda mejor explicado.<br/>
<br/>
 
Tenemos dos bucles porque cada uno dibuja una "mitad" de la pantalla. Según lo explicado anteriormente en [[ST7920#Peculiaridad Fila|Peculiaridad Fila]], cuando pasamos de la fila 31 la numeració de la AC cambia. Por tanto hago dos bucles para cada "tipo de numeración" de la AC.<br/>
Los dos bucles son iguales menos en el comando que indicamos en que AC estamos, así que comentaré solo uno y el otro es lo mismo pero empezando por la AC 0x08 hasta la 0x0F en vez de empezar por la AC 0x00 hasta la AC 0x07.<br/>
<br/>
 
El bucle es de 32 iteraciones porqué recorremos desde la fila 0 hasta la 31.<br/>
La primera instrucción que envio es la de Function Set con RE = 1 (0x34). Ahora que ya estamos en modo extendido pasamos a enviar las [[ST7920#Explicación Coordenadas|coordenadas]].<br/>
En la siguiente instrucción se envia la primera coordenada con la instrucción 0x80+j. Esto es así porque a medida que aumenta la j, se va cambiando de fila.<br/>
La segunda coordenada es la AC. En este caso no hace falta ir incrementandola porque una vez dibujada una AC entera la AC se incrementa automáticamente.<br/>
<br/>
 
Luego volvemos a enviar Function Set pero con RE = 0 (0x30) para poder enviar Write to RAM.<br/>
El siguiente bucle es el que dibuja una fila entera. El bucle va desde 0 hasta 15 (es decir 16 posiciones) porqué dentro de él hay una instrucción Write to RAM que escribe una "subcasilla" entera. Debido a que la "subcasilla" se compone de 8 bits y el bucle se repite 16 veces, tenemos un total de 128 bits/pixeles (que es la anchura del lcd).<br/>
<br/>
<br/>
Ahora si queremos por ejemplo encender el pixel 1,0 lo que haremos es lo mismo, pero en vez de poner la fila 0 pondremos la fila 1 <--en vez de filas poner el valor que tiene que ir o sino meterlo dentro de un pre
 
Hay que destacar que la [[ST7920#Peculiaridad AC|Peculiaridad AC]]  no es necesaria tenerla en cuenta porque con este método se escribe la pantalla entera, no se volverá a una posición anterior para redibujar un pixel, asi que no hará falta tener en cuenta el tema de sobreescribir las "subcasillas".<br/>
<br/>
<br/>
El problema viene cuando llegamos a la fila 32. Desde la fila 0 a la fila 32 los valores para seleccionar la fila van del hasta el. En la fila 32<---no se si son los valores de la fila o los valores de las columnas los que cambian en la fila 32. mirar en el codigo de los chinos.


== Proceso al dibujar uno o varios pixeles ==
Una vez acabados los dos bucles principales se entra en modo extendido (0x34) para encender el lcd (0x36) y así ver el resultado.
Aqui voy a poner el proceso a seguir, tanto con código y con dibujos, para dibujar pixeles en este lcd.
<br/>
<br/>


= Fuentes personalizadas =
= Fuentes personalizadas =

Revisión actual - 13:01 6 abr 2013

Características

Address Counter (AC)

La AC es una dirección que sirve para poder acceder a la DDRAM, CGRAM y GDRAM. Cuando yo quiero acceder a una posicion de la DDRAM, CGRAM o GDRAM lo hago a traves de una dirección. Mediante el siguiente dibujo explico la relación entre la AC y la DDRAM. La relación con la CGRAM no la explicaré porque no la encuentro necesaria y no me he parado a "jugar" con ella, y la relación con la GDRAM la explico en el apartado de Relación AC con GDRAM.

CGROM

Esta CGROM tiene la particularidad que tiene caracteres chinos de 16x16 puntos. Según el manual tiene 8192 caracteres.

HCGROM

La HCGROM contiene 128 caracteres alfanumericos de 8x16 puntos. Lo que quiere decir que son los caracteres que representan nuestro abecedario. Cada caracter de estos ocupa la mitad que los de la CGROM y necesitas escribir dos caracteres de la HCGROM para completar una dirección AC.


Explicación gráfica:



  • Como se puede ver en el dibujo, en este lcd se pueden mostrar 8 caracteres de la CGROM y 16 de la HCGROM. La CGROM son kanjis chinos y la HCGROM son caracteres alfanumericos (nuestro abecedario mas los signos de puntuación, etc)
  • Cada caracter HCGROM consta de 8bits, y un caracter de CGROM necesita 16bits para ser representado
  • La AC tiene solo 8 posiciones, lo que significa que tengo que escribir los caracteres alfanuméricos de dos en dos porque dos caracteres de la HCGROM equivalen a 1 AC


Peculiaridad al escribir un caracter de la HCGROM

Aqui intento explicar el punto anterior de la AC.


  • Enviamos la instrucción 0x40 que es enviar la AC 0x00 con la instrucción Set CGRAM (0x40)



  • En la imagen se ve la AC 0x00 subrayada de azul, ahora estamos en esa AC
  • Ahora vamos a poner RS a 1 para poder enviar el caracter A (que en hexa es 0x41). Lo que hacemos es escribir la letra A en la primera posición



  • Como vemos en la imagen de arriba la AC 0x00 se ha dividido en dos y la letra A se ha escrito en la primera subcasilla. Esta subcasilla es el espacio que ocupa un caracter de la HCGROM (como hemos visto más arriba)
  • Ahora queremos dejar dos espacios en blanco y escribir B, por lo tanto queremos escribirla en la subcasilla 4. No se puede acceder a esta subcasilla porque la AC 0x01 empieza con la subcasilla 3 y no hay manera de acceder la subcasilla 4 directamente
  • Para poder escribir en la subcasilla 4 tendré que enviar el comando Set CGRAM con la dirección de AC 0x01 y luego enviar el comando Write Ram con el hexadecimal del espacio y otra vez el comando Write Ram con el hexadecimal de B. El código quedaria asi:


  /*La función send envia la instrucción/dato y especifica si es un comando (CMD) o es un dato (DATA). El comando tiene el RS a 0 y el dato tiene el RS a 1*/
  
  send(0x81, CMD);  //Seleccionamos la AC 0x01 con la instrucción básica Set DDRAM Address

  send(0x20, DATA);  //Envio un espacio (en ascci 0x20 es un espacio)
  send(0x42, DATA);  //Envio la letra B

DDRAM

Hay 64x2bytes de espacios para la DDRAM. Son 4 lienas para 16 caracteres de 16x16 puntos o 32 caracteres de 8x16 puntos.
La explicación de que es la DDRAM ya se puede leer en el apartado DDRAM del HD44780U

CGRAM

Esta es la RAM que nos permite crear caracteres personalizados, pero debido a que este lcd es un GLCD lo encuentro poco útil. En la sección de Modo Gráfico (GLCD) hay funciones mucho más interesantes.


Descripción Pines

  • XRESET: Es el pin de reset. Para resetear el lcd desde el hardware se hace con este pin. Tiene que pasar de LOW a HIGH para hacer el reset. Yo no lo utilizo.
  • PSB: Es un pin para seleccionar si usamos la comunicación serie o paralela. LOW es serie y HIGH paralela
  • Vo: Es para el contraste (creo)
  • VSS: GND
  • VDD: +5V

SPI

En el modo serie no se puede leer de la pantalla. Solo se pueden hacer operaciones de escritura.

  • RS: es el pin SS del protocolo SPI
  • RW: es el pin MOSI del protocolo SPI
  • E: es el pin clock del protocolo SPI

Paralelo 8 bits

  • RS: Es el pin Register Select. LOW es para seleccionar las instrucciones de registro (IR) y HIGH es para seleccionar los registros de datos (DR).
  • RW: Es el pin de lectura/escritura. LOW es para indicar escritura y HIGH para indicar lectura.
  • E: Es el pin utilizado para indicar el paso de datos del microcontrolador al st7920. Se ve mejor su utilidad en la sección Como funciona la comunicación paralela 8bits
  • D0-D7: Son los pines de datos.

Funciones Básicas


  • Todas las instrucciones básicas excepto Function Set son muy parecidas o iguales a las de HD44780U


Function Set

  • El bit DL indica si estamos en comunicación paralela de 8 bits(HIGH) o comunicación paralela de 4 bits (LOW)
  • El bit RE es para indicar si estamos en modo extendido(HIGH) o en modo normal(LOW). El modo extendido es en modo GLCD


Funciones Extendidas


De todas las funciones extendidas, de momento, yo solo he "trasteado" con Extended Function Set y Set Graphic Display Ram Address

Extended Function Set

  • DL indica lo mismo que en el apartado anterior, HIGH para comunicacion paralelo 8 bits y LOW para 4 bits.
  • RE indica modo extendido (HIGH) o normal (LOW)
  • G indica lcd encendido (HIGH) o apagado (LOW). Este es el bit importante, porque cada vez que pasemos de modo normal a modo GLCD tenemos que asegurarnos de enviar esta instrucción con este bit en HIGH, sino no se verá nada. Esto lo explico en el apartado de pasos a seguir al dibujar un pixel

Set Graphic Display Ram Address

Esta instrucción es la que se usa para pasar una dirección de memoria a la AC (que recordemos que es la dirección que necesitamos para saber donde dibujar un pixel, es decir las cordenadas X e Y).
En la imagen se ven dos filas porque se tiene que enviar dos veces esta instrucción, una para el eje X y la otra para el eje Y. La primera instrucción se envia para determinar la Y y la segunda para determinar la X.
En el apartado Relación AC con GDRAM hay una imagen explicativa de la relación entre la AC y la GDRAM

Como funciona la comunicación Serial (SPI)

Como funciona la comunicación paralela 8bits

  • El bit RS controla si enviamos operaciones DR o IR (Paralelo 8 bits). El bit lo tenemos que colocar a 1 o 0 antes de activar Enable
  • El bit RW controla si son operaciones de lectura o escritura (Paralelo 8 bits). Igual que con RS se tiene que establecer en 0 o 1 antes de acrtivar Enable
  • Los bits DB0-DB7 son los que llevan los datos. Se tienen que establecer antes de activar Enable
  • El bit E (Enable) es el que utilizamos para transmitir las instrucciones al lcd


Ejemplo:

/*RS, RW, DB0-DB7 y E son pines digitales de Arduino*/

  digitalWrite(RS, LOW);
  digitalWrite(RW, LOW);
  digitalWrite(DB7, LOW);
  digitalWrite(DB6, LOW);
  digitalWrite(DB5, LOW);
  digitalWrite(DB4, LOW);
  digitalWrite(DB3, LOW);
  digitalWrite(DB2, LOW);
  digitalWrite(DB1, LOW);
  digitalWrite(DB0, HIGH);

  digitalWrite(E, HIGH);
  delayMicroseconds(1);
  digitalWrite(E, LOW);
  • En este ejemplo vamos a enviar el comando Display Clear 0x01
  • Como se puede comprobar en la tabla de Funciones Básicas estamos transmitiendo la instrucción IR Display Clear
  • Esta es una forma muy básica de hacerlo, pero es para que quede claro
  • Cuando establecemos todos los pines con su bit correspondiente (0 o 1) pasamos a enviar la instrucción al lcd
    • En el flanco de bajada de Enable es cuando se transmite la instrucción al lcd


Inicialización por software

Modo paralelo 8 bits

Aqui voy a poner el esquema de la inicialización con interfaz de 8 bits. La de 4 bits no la pondré porque trabajar este LCD con 4 bits da errores que no se de donde vienen exactamente pero que con la interfaz de 8 bits o la de serie no pasa.

Al hacer las pruebas he visto como siguiendo este esquema se hace una buena inicialización y el lcd funciona correctamente, pero también he visto en códigos encontrados por internet que no siguen todo el esquema y también se le inicializa correctamente el lcd.

Supongo que de la segunda manera ahorras tiempo en inicializar, pero no se si hay más ventajas o no, ni puedo decir con certeza cual es mejor utilizar.


Modo serie (SPI)

La inicialización del lcd en modo serie no está especificada en el datasheet, este solo especifica la inicialización en paralelo 4 bits y 8 bits. Por lo que he visto en los códigos (del propio vendedor y otros) la inicialización en modo serie es igual que en 8 bits.

Las dos primeras instrucciones que se envian (Function Set) en el modo paralelo 8 bits colocan el bit DB2 en 0 para indicar que se inicializa en modo 8bits. Por las pruebas que yo he hecho da igual si se coloca este bit a 0 o a 1 cuando utilizamos el modo serie. En los ejmeplos que he ido poniendo en toda esta wiki he puesto este bit a 0.

Modo Gráfico (GLCD)

Funciones Modo Gráfico

En el modo gráfico interviene varias instrucciones, tanto básicas como extendidas. Las que conozco yo porque las he utilizado son las que explicaré:

  • Write to RAM
    • Esta instrucción básica sirve para "dibujar" pixeles cuando el lcd está en modo gráfico.
    • Cada instrucción Write to RAM puede "dibujar" 8 pixeles, la siguiente imagen ilustra como se codifican los pixeles:




  • Function Set
    • Tal como dije en el apartado anterior esta instrucción nos sirve para acceder al modo extendido y así poder utilizar las instrucciones que afectan al modo gráfico.
    • Cada vez que se quiera acceder a un pixel, antes se ha de poner en modo extendido
    • La forma de poner en modo extendido es enviando el comando 0x34
  • Function Set con G activado
    • Hay otra instrucción Function Set en el modo extendido
    • Esta instrucción enciende o apaga el display con el bit G (se puede ver en la imagen del apartado Funciones Extendidas)
    • La instrucción con G activado equivale al comando 0x36


Relación AC con GDRAM

Explicación Coordenadas

Dibujo de las coordenadas del lcd:



Imaginemos que quiero dibujar el pixel (0,0) (el que es de color negro de la imagen). Tengo que decirle al lcd donde esta el pixel que quiero dibujar y por eso le tengo que pasar unas "coordenadas".
Estas coordenadas se las pasaré con la instrucción extendida Set Graphic Display RAM Addres.
Primero tengo que enviar la instrucción para determinar la Y, que es la fila. Como en este caso queremos la fila 0 le enviaremos la instrucción:

send(0x80)  //0x80 (instrucción Set Graphic Display Ram Address ) + 0x00 (fila 0) = 0x80

Luego tengo que enviar la instrucción para determinar la X, que es la AC. Hay 8 AC, por tanto la X va desde la 0 a la 7.
La AC agrupa dos "subcasillas" de 8 bits cada una (cada bit representa un pixel). Estas "subcasillas" representan instrucciones de Write to RAM y esta instrucción puede dibujar 8 pixeles diferentes. Para poder dibujar la AC entera necesito enviar dos instrucciones Write to RAM, una para dibujar cada subcasilla.
Por lo tanto para este ejemplo la AC que tengo que "seleccionar" es la 0x00

send(0x80)  //0x80 (instrucción Set Graphic Display Ram Address ) + 0x00 (AC 0) = 0x80

Finalmente pasaré a dibujar el pixel con la instrucción Write to RAM, pero eso ya se verá como se hace en la sección Proceso al dibujar uno o varios pixeles

Peculiaridad AC


Esta peculiaridad tiene que ver con la necesidad de enviar dos instrucciones Write to RAM para poder dibujar un AC completo.Me explico.
Si yo quiero dibujar un pixel en la subcasilla 01, por ejemplo el pixel número 8 de la fila 0 tengo que enviar dos instrucciones Write to RAM, una para escribir la subcasilla 00 con todos los bits a 0 (para que los pixeles esten apagados) y otra con solo el bit 8 a 1 y los demas a 0.
Asi que cada vez que queramos escribir un pixel tenemos que escribir un AC completo, porque no se puede acceder a la segunda subcasilla de un AC directamente, se han de escribir las dos subcasillas a la vez. (Esto no es del todo cierto porqué si que hay un caso donde se puede escribir cada subcasilla, se verá en el apartado Dibujar todos los pixeles)

Peculiaridad Fila


Esta peculiaridad se presenta cuando queremos dibujar un pixel a partir de la fila 32.
En el dibujo anterior se puede ver que cuando la fila va desde la 0 hasta la 31 la numeración en hexadecimal sigue esa correlación, es decir que para la fila 0 tenemos que enviar al lcd el número 0x00, para la 1 enviamos 0x01 y asi hasta la 31 que enviamos 0x1F (que es 31 en hexadecimal).
Se puede ver en el dibujo que cuando llegamos a la fila 32 tenemos que enviar 0x00 al lcd para decir que es la 32, la 33 sera 0x01 hasta la 63 que será 0x1F. Cuando llegamos a la "segunda mitad" de la pantalla se "resetea" la numeración de las filas.
La AC también se comporta de forma peculiar. La pantalla tiene 8 AC (que van desde la 0x00 hasta la 0x07) en cada fila. Mientras estamos en la "mitad superior de la pantalla" (es decir de la fila 0 a la 31) la AC 0 se accede a través de 0x00, la AC 1 a través de la 0x01 y asi hasta la AC 7 a través de la 0x07. Cuando pasamos la fila 31 la AC 0 se accede a traves de 0x08, la AC 1 sera la 0x09 y así hasta la AC 7 que será a través de la 0x0F.

Proceso al dibujar uno o varios pixeles

Dibujar un pixel concreto

A través de 3 ejemplos explicaré como dibujar un pixel y como intervienen las Peculiaridades AC y Fila

  • Ejemplo 1: Código dibujar pixel 2,3 (azul)
    • La fila es la 2 (0x02)
    • La AC (columna) es la 0 (0x00)
    • A la función Write to RAM se le pasará 0x10. Si nos fijamos en el punto Write to RAM que hay en la sección Funciones Modo Gráfico, vemos que el pixel 3 equivale a 0x10


/*La función send envia la instrucción que le paso por parámetro al lcd y especifica mediante CMD o DATA si RS vale 1 o 0*/

                    //Primero se envia el comando Function Set con RE a 1
  send(0x34, CMD);
  
                    //Luego se envia la fila (Y)
  send(0x82, CMD);  //0x80 equivale a la instrucción extendida Set Graphic Display RAM Address (0x80) + Y (0x02)

                    //Después se envia la AC (X)
  send(0x80, CMD);  //0x82 equivale a la instrucción extendida Set Graphic Display RAM Addres (0x80) + AC (0x00)

  /*Ahora se dibuja los pixeles con la instrucción básica Write to RAM*/

                    //Como Write to RAM es una instrucción básica, antes de poder utilizarla hemos de cambiar a instrucciones básicas
  send(0x30, CMD);  //0x30 es la instrucción básica Function Set con RE = 0

  send(0x10, DATA); //Se dibuja el pixel (2,3)


  send(0x34, CMD);  //Volvemos al modo extendido

  send(0x36, CMD);  //Instruccion extendida Extended Function Set con G en 1 (display on)


  • Ejemplo 2: Teniendo en cuenta Peculiaridad AC - dibujar pixel (2,8) (rojo)
    • Quiero dibujar el pixel (2,8) (el rojo) pero sin que se borre el pixel (2,3) dibujado en el anterior Ejemplo
    • La fila será la misma que en el ejemplo anterior 0x02
    • La AC seguirá siendo la 0
    • La "subcasilla" será la segunda porqué la primera "subcasilla" va desde el pixel 0 al 7 y yo quiero encender el 8
    • En este ejemplo se ve que se tiene que tener en cuenta que pixeles hay dibujados en la primera "subcasilla" antes de dibujar pixeles de la segunda "subcasilla"


  send(0x34, CMD);

  send(0x82, CMD);  //Seleccionamos la fila 2
  send(0x80, CMD);  //Seleccionamos la AC 0

  send(0x30, CMD);  //Si solo enviara la instrucción Write to RAM que dibuja el pixel 8 entonces el que tenia
  send(0x10, DATA); //dibujado (el pixel 3) se borraria. Por eso he de enviar primero la instrucción Write to RAM
  send(0x80, DATA); //que dibuja la primera "subcasilla" y luego la que dibuja la segunda "subcasilla"

  send(0x34, CMD);
  send(0x36, CMD);


  • Ejemplo 3: Teniendo en cuenta la Peculiaridad Fila - dibujar pixel (32,0) (amarillo)
    • La fila será la 32, que según el dibujo de las coordenadas equivale a 0x00
    • La AC será la primera de la "segunda mitad" de la pantalla, por tanto equivale a 0x08
    • Aunque este pixel pertenezca a la primera AC, no será la 0x00 sino la 0x08. Si pusiese la 0x00 se dibujaria el pixel (0,0)


  send(0x34, CMD);

  send(0x80, CMD);  //Seleccionamos la fila 32
  send(0x88, CMD);  //Seleccionamos la AC0 que equivale a la 0x08

  send(0x30, CMD);
  send(0x80, DATA); //Escribimos el pixel 0 de la primera "subcasilla"

  send(0x34, CMD);
  send(0x36, CMD);

Dibujar todos los pixeles

Aqui voy a explicar como dibujar toda la pantalla en una sola función. Esto es útil para limiarla (dibujamos todo 0x00 y asi apagamos todos los pixeles) o para dibujar una imagen cargada en memoria (por ejemplo un logo o algo similar).
Al dibujar la pantalla entera se ha de tener en cuenta la Peculiaridad Fila, pero no es necesario tener en cuenta la Peculiaridad AC, esto se verá en el ejemplo.


for(int j=0; j<32; j++){
  send(0x34, CMD);
  
  send(0x80+j, CMD);
  send(0x80, CMD);

  send(0x30, CMD);
  for(int i=0; i<16; i++){
    send(0x00, DATA);  //A la función send le puedo pasar 0x00 para limpiar la pantalla o una matriz donde esta cargada la imagen o logo que quiera dibujar
  }
}
for(g=0; g<32; g++){
  send(0x34, CMD);

  send(0x80+g, CMD);
  send(0x88, CMD);

  send(0x30, CMD);
  for(int h=0; h<16; h++){
    send(0x00, DATA);
  }
}
send(0x34, CMD);
send(0x36, CMD);

Esta es una forma muy básica de dibujar la pantalla entera, es meramente didáctica. Los dos bulces, por ejemplo, no son necesarios, pero así queda mejor explicado.

Tenemos dos bucles porque cada uno dibuja una "mitad" de la pantalla. Según lo explicado anteriormente en Peculiaridad Fila, cuando pasamos de la fila 31 la numeració de la AC cambia. Por tanto hago dos bucles para cada "tipo de numeración" de la AC.
Los dos bucles son iguales menos en el comando que indicamos en que AC estamos, así que comentaré solo uno y el otro es lo mismo pero empezando por la AC 0x08 hasta la 0x0F en vez de empezar por la AC 0x00 hasta la AC 0x07.

El bucle es de 32 iteraciones porqué recorremos desde la fila 0 hasta la 31.
La primera instrucción que envio es la de Function Set con RE = 1 (0x34). Ahora que ya estamos en modo extendido pasamos a enviar las coordenadas.
En la siguiente instrucción se envia la primera coordenada con la instrucción 0x80+j. Esto es así porque a medida que aumenta la j, se va cambiando de fila.
La segunda coordenada es la AC. En este caso no hace falta ir incrementandola porque una vez dibujada una AC entera la AC se incrementa automáticamente.

Luego volvemos a enviar Function Set pero con RE = 0 (0x30) para poder enviar Write to RAM.
El siguiente bucle es el que dibuja una fila entera. El bucle va desde 0 hasta 15 (es decir 16 posiciones) porqué dentro de él hay una instrucción Write to RAM que escribe una "subcasilla" entera. Debido a que la "subcasilla" se compone de 8 bits y el bucle se repite 16 veces, tenemos un total de 128 bits/pixeles (que es la anchura del lcd).

Hay que destacar que la Peculiaridad AC no es necesaria tenerla en cuenta porque con este método se escribe la pantalla entera, no se volverá a una posición anterior para redibujar un pixel, asi que no hará falta tener en cuenta el tema de sobreescribir las "subcasillas".

Una vez acabados los dos bucles principales se entra en modo extendido (0x34) para encender el lcd (0x36) y así ver el resultado.

Fuentes personalizadas

Documentación

Datasheet ST7920 Cytronix

Notas

  • Hacer una representación gráfica de la disposicion de las filas y columnas, lo del 0x80 y 0x88
    • Explicar como lo soluciona dc42
  • Explicar como soluciona dc42 el problema de que cada AC son 2 bytes y que cada byte representa 8 pixeles (cada pixel corresponde a un bit)
  • Explicar uint8_t *ptr = image + ((16 * r) + (2 * startColNum)); y también la parte (2*startColNum)
  • Explicar sendLcdData(*ptr++);
  • Creo que cada vez que si se hace esto:
setPixel(1,1);
flush();
setPixel(2,1);
flush();

Esta dibujando la pantalla entera para cada flush(), pero no lo entiendo porque la función flush() esta preparada para renderizar un AC de una fila concreto