Está en la página 1de 32

Manejo LCD Grfico

DSM

Christopher Orihuela Sosa

10/09/07

ndice
Introduccin..........................................................................................................................................1
Hardware..............................................................................................................................................2
LCD.................................................................................................................................................3
patillaje........................................................................................................................................4
tabla de instrucciones..................................................................................................................5
Timings.......................................................................................................................................6
Organizacin de la pantalla.........................................................................................................8
Circuito completo.................................................................................................................................9
Cdigo comentado..............................................................................................................................10
Bibliografa.........................................................................................................................................28

Introduccin:
El objetivo de este trabajo era aprender a controlar con un PIC un display LCD grfico mediante la
creacin de un pequeo juego rompebloques que lo utilice.

Hardware:
El montaje se realiz utilizando un PIC 16F84A de 18 patillas a 12 mhz que controla un LCD
grfico. El juego se maneja con 2 pulsadores que comparten las lneas de datos del LCD
aprovechndose del margen de niveles de la lgica TTL.
A continuacin se entrar en detalles sobre cada una de estas partes.

LCD:
El LCD utilizado es un LCD Grfico CFAG12864B-TMI-V de la casa Crystalfontz.

Caractersticas ms importantes
Ancho (pxeles/mm)

128 / 60

Alto (pxeles/mm)

64/ 32.6

Color

Pxeles blancos sobre azul

Inversor de tensin

Incluido

Chipset

2 x S6B0108 y 1 x S6B0107

El CFAG12864B-TMI-V est basado en los chips Samsung S6B0108 / S6B0107 (antes llamados
KS6B108 / KS0107B).

Cada S6B0108 solo admite 64 lneas as que el LCD est dividido en 2 mitades, cada una de ellas
controlada por uno de estos 2 chips tal y como ilustra el grfico.

Externamente, el patillaje del LCD es el siguiente:

Patillaje
1

Vdd (+5v)

Vss (0v)

Vo Entrada de contraste. Necesita al menos una diferencia de tensin de


8v para funcionar, de ah que se necesite un inversor de tensin.

4-11 DB0-DB7 - Datos


12

CS1 (Active low) - Selecciona la primera mitad del LCD.

13

CS2 (Active low) - Selecciona la segunda mitad del LCD.

14

RESET (Active low) - Poner a VSS para resetear.

15

R/ W (Active low) - Selecciona operacin de lectura/escritura.

16

D/ I (Active low) - Indica si la operacin es de datos o instrucciones. *

17

E - Seal de "reloj" para el chipset del LCD.

18

Vee - Salida del inversor de tensin integrado (-5v).

19

LED+ nodo para la luz del LCD (backlight).

20

LED- Ctodo para la luz del LCD (backlight).

* D/I tambin es llamado a veces RS en la documentacin del chipset.


El montaje para el contraste usando el inversor de corriente integrado es este:

Se recomienda una resistencia de al menos 25 Ohmios para el nodo del backlight. Una resistencia
de 10000 Ohmios para RESET suele ser tpica tambin en estos montajes.
4

sta es la tabla de instrucciones del S6B0108:


En la documentacin se invierten los ejes normales, es decir, se nombra a las columnas como Y y a
las pginas verticales (grupos de 8 pxeles verticales) como X (ver organizacin de pantalla ms
adelante).
Cuando se escribe o lee un dato en/de la (RAM de) pantalla, la Y se aumenta en 1.
Tabla de instrucciones del S6B0108
Instruccin RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
Display L
ON/OFF

Set Y
Address

Set X Page L

Set
L
Display
Start Line

Display Start Line (0-63)

B
U
S
Y

ON R
/
E
OFF S
E
T

Status
Read

Funcin

H/L Encender/apagar la pantalla.


L: Off
H: On

Y Address (0-63)

Establece la Direccin Y,
es decir, la columna activa.

X Page (0-7)

Establece la Pgina X, es
decir, la pgina vertical
activa.
Indica qu lnea debe ser
tomada como lnea de inicio
a la hora de mostrar la
imgen en el display.

Lee el estado del chip:


BUSY Bit:
H: Ocupado
L: Disponible
DISPLAY Status Bit:
L : Display Encendido
H:
Display Apagado
RESET Bit:
L:
Normal
H: Reseteando

Write
Display
Data

H L

Display Data (8 bits, es decir, una pgina


vertical entera)

Escribe el byte en a RAM


en la posicin de pantalla
activa e incrementa la
posicin Y activa en 1.

Read
Display
Data

H H

Display Data (8 bits, es decir, una pgina


vertical entera)

Pone en DB0:7 el contenido


de la RAM en la posicin
de pantalla activa actual.

Hay que hacer notar que cada mitad del LCD se controla independientemente. Cada mitad se activa
poniendo CS1 o CS2 a nivel bajo antes de cada instruccin, respectivamente.
La seal E (pin 17) acta como reloj. Las instrucciones se procesan en el flanco de subida. Si
estamos haciendo una lectura del estado (Status Read) ste es devuelto mientras E est alta.
Hay que esperar a que la BUSY Flag del LCD est desactivada antes de intentar cualquiera de las 6
primeras instrucciones (las 5 primeras se consideran escrituras de registros del LCD, y Status Read
una lectura de los mismos).

Las lecturas de RAM de pantalla son algo ms complejas que las dems instrucciones, pues
necesitan adems de una primera lectura en la que en realidad no lee nada, pues la lectura efectiva
se hace en el segundo intento.

Organizacin de la pantalla:
La pantalla est dividida en 2 mitades de 64x64 pxeles, cada una organizada en 64 columnas
individualmente sealables (Y Addresses) y en 8 pginas verticales (X Pages) que agrupan 8 pxeles
monocromticos cada una (1 byte cada X page).

Cada mitad individual del LCD se activa poniendo ~CS1 o ~CS2 a nivel bajo.

Circuito completo:

No se pintaron MCLR, VSS, VDD y OSC1 y OSC2 para ahorrar espacio. Estos pines estn
conectados como en cualquier circuito genrico de PICs.
Hay dos puntos que aclarar respecto a este circuito:
1) RA4/~CS1 tiene una resistencia Pull-up de 2K2 Ohmios porque este pin es Open Drain
Output en el PIC.
2) La lectura de los pulsadores (RB0 y RB1) utiliza los niveles lgicos TTL compatibles con el
PIC y se realiza de la siguiente forma:
1. Cuando el LCD esta usando ese puerto, al no haber resistencias entre el pin del PIC y el
del LCD siempre prevalece el valor puesto por el PIC/LCD.
2. Cuando el LCD esta desconectado (~CS1 y ~CS2 altos) se pueden leer los pulsadores. Si
stos estn sin pulsar, estarn puestos a 5V por las resistencias pull-up de 47K (TTL
input high mnimo 2V y -0.04mA) y detectar un 1 lgico.
Si estn pulsados, la corriente fluir desde los 5V por las resistencias pull-up de 47K y
las de 2K2 hasta GND, lo que significa que en dichos pines habr
5V/(47000+2200)=0.1mA y (0.0001*2200)=0.22V (TTL input low mximo 0.8V y
-1.6mA). En este caso la corriente no llega al mximo (-1.6mA) que podra exigir un
chip TTL, pero el PIC detectaba el 0 lgico sin problemas.

Cdigo comentado:
El juego consiste en romper todas los bloques sin que la pelota se caiga. Si se consigue se pasa a
otra fase en la que la velocidad de movimiento aumenta.
Al principio el LCD est apagado, para iniciar una fase hay que pulsar un botn.

LIST
__FUSES
include

P=16F84A, R=DEC
_HS_OSC & _WDT_OFF & _CP_OFF & _PWRTE_ON
"P16F84A.inc"

;------------------------------------------------------------------------; Constantes
;------------------------------------------------------------------------; Indican que pin corresponde a que seal de control
LCD_CS1
EQU 4
LCD_CS2
EQU 3
LCD_RW
EQU 2
LCD_DI
EQU 1
LCD_RS
EQU 1 ; (RS=DI)
LCD_E
EQU 0

;------------------------------------------------------------------------; Variables
;------------------------------------------------------------------------Ram

EQU

h'0C'

; Inicio de la RAM

; Variables usadas para controlar el LCD


; (los ejes X e Y siguen la convencion del LCD)
LCD_Page
EQU
Ram+0 ; LCD_Set_Page_X
LCD_Address
EQU
Ram+1 ; LCD_Set_Address_Y
LCD_Data
EQU
Ram+2 ; LCD_Write_Display_Data
;
LCD_Pixel_X
EQU
Ram+3 ; LCD_Put_Pixel/LCD_Set_Pixel
LCD_Pixel_Y
EQU
Ram+4 ;
; Variables temporales
Temp_Count
EQU
;
Temp_1
EQU
Temp_X
EQU
Temp_2
EQU
Temp_Y
EQU
Temp_3
EQU

Ram+5
Ram+6
Ram+6
Ram+7
Ram+7
Ram+8

10

Temp_Z

EQU

Ram+8

; Variables usadas para el juego


; (los ejes X e Y siguen la convencion del
Inc_X
EQU
Ram+9 ;
Inc_Y
EQU
Ram+10
;
Pelota_X
EQU
Ram+11
Pelota_Y
EQU
Ram+12
;
Barra_Y
EQU
Ram+13
Barra_Y_Inc
EQU
Ram+14
;
Espera
EQU
Ram+15
;
Bloques_Quedan
EQU
Ram+16
Bloques
EQU
Ram+17
entero)

LCD)
; Direcciones de la pelota
;
; Posicion de la pelota
; Posicion de la barra
; Incremento de la barra
; Espera entre frame y frame
;
;
;
;

Cuantos bloques quedan


Bloques (2 filas x 16 bytes)
Valores (0: derribado,
1: marcado para borrar, 2:

;------------------------------------------------------------------------; Programa
;------------------------------------------------------------------------Start

ORG
goto

0
Inicio

;
;

Direccion de inicio
Iniciar programa

;
; Aqui van todas las tablas del programa

Esta tabla es necesaria, porque querremos iluminar solo un pixel de toda la pgina vertical de 8
pxeles (la pelota).
; Tabla que devuelve el valor para una pagina
; del LCD segun el pixel que queramos poner
Pixel_Value
addwf PCL, F
retlw b'00000001'
retlw b'00000010'
retlw b'00000100'
retlw b'00001000'
retlw b'00010000'
retlw b'00100000'
retlw b'01000000'
retlw b'10000000'
;

11

Inicializar puertos. Apagamos el LCD hasta que se pulse algo.


Inicio
; Inicializar puertos
clrf
PORTA
clrf
PORTB
;
bsf
STATUS, RP0
clrf
TRISA
clrf
TRISB
bcf
STATUS, RP0
;
bcf
PORTA, LCD_E
inicialmente baja

; poner B a salida

; debemos poner E

;
;
; Apagamos el LCD
bcf
PORTA,
bsf
PORTA,
call LCD_Off
;
bsf
PORTA,
bcf
PORTA,
call LCD_Off
;
bsf
PORTA,

hasta que se pulse un boton


LCD_CS1
LCD_CS2
LCD_CS1
LCD_CS2
LCD_CS1

; desactivar las 2

mitades

bsf
PORTA, LCD_CS2 ; del lcd para leer
;
bsf
STATUS, RP0 ; puerto B a leer
movlw H'FF'
movwf
TRISB
bcf
STATUS, RP0
;
; esperamos unos ms
movlw 100
call
WaitNms
;
Esperar_Pulsacion_Inicial
movfw PORTB
; esperamos a que se pulse algo
andlw b'00000011'
addlw -3
btfsc STATUS, Z
goto Esperar_Pulsacion_Inicial
;
; volvemos a dejar el puerto B a salida
bsf
STATUS, RP0
clrf TRISB
bcf
STATUS, RP0
;
;
;
; inicialmente ponemos un cierto retardo (velocidad

12

juego)

movlw 30
movwf Espera
;
;
;
; aqui es donde se inicializan los elementos de

cada partida

; esta parte es comun para todas las partidas

Esta parte inicia una partida, independientemente de la velocidad. Se inicializan las posiciones de
la pelota, los bloques (matriz 2x16 en variable bloques), etc.
Iniciar_Partida

; Inicializar primera mitad del LCD


bcf
PORTA, LCD_CS1
bsf
PORTA, LCD_CS2
call LCD_On
call LCD_Clear
;
; Inicializar segunda mitad
bsf
PORTA, LCD_CS1
bcf
PORTA, LCD_CS2
call LCD_On
call LCD_Clear
;
;
;
; posicion inicial de la pelota
movlw 30
movwf Pelota_X
movwf Pelota_Y
movlw 1
movwf Inc_X
movwf Inc_Y
;
;
;
; posicion inicial de la barra
movlw 44
movwf Barra_Y
;
;

derribar

;
; inicializar los bloques
movlw
32
movwf
Bloques_Quedan

; quedan 32 bloques por

; ahora los marcamos con un 2 en la matriz (2=por

13

derribar)

Ini_bloques_L1

movwf Temp_X
movlw Bloques
movwf FSR
movlw
2
movwf INDF
incf FSR
decfsz
Temp_X
goto Ini_bloques_L1
; fin de inicializar los bloques
;
;

El loop principal consiste en pintar la pantalla, actualizar las posiciones de barra y pelota,
comprobar las colisiones con bloques y bordes, leer los controles, comprueba si se gana o se
pierde... Realiza una espera fija entre frame y frame. No hubo necesidad de usar el timer, puesto
que el nmero de instrucciones que se ejecuta es casi igual entre frames y es el lcd el que limita en
gran medida la velocidad (esperando al busy flag).

;Loop principal
Loop

;
; pintar los bloques
; apuntamos a la primera hilera de bloques
movlw Bloques
movwf FSR
;
bcf
PORTA, LCD_CS1 ; primera mitad LCD
bsf
PORTA, LCD_CS2
call Pintar_Bloques
; apuntamos a la segunda hilera bloques
movlw (Bloques+8)
movwf FSR
;
bsf
PORTA, LCD_CS1
bcf
PORTA, LCD_CS2 ; segunda mitad LCD
call Pintar_Bloques
; fin de pintar los bloques
;
;
;
; pintar la pelota
movfw Pelota_X
movwf LCD_Pixel_X
movfw Pelota_Y
movwf LCD_Pixel_Y
call LCD_Put_Pixel
;
;

14

;
; pintamos la barra
call Pintar_Barra
; fin de pintar la barra
;
;
; Leemos los controles y realizamos
; una espera entre frames
Mandos

unids. derecha

bsf
PORTA, LCD_CS1 ; desactivar
bsf
PORTA, LCD_CS2 ; las 2 mitades del lcd
;
; espera
movfw Espera
call
WaitNms
;
bsf
STATUS, RP0 ; puerto B a leer
movlw H'FF'
movwf
TRISB
bcf
STATUS, RP0
;
clrf Barra_Y_Inc ; movimiento neutro (cero)
; Si B.0 es 0 y no esta al tope de derecha, mover 2
btfsc
PORTB, 0
goto Mandos_L1
movfw Barra_Y
addlw 152
btfsc STATUS, C
goto Mandos_L1
movlw 2
movwf Barra_Y_Inc

Mandos_L1
2 unids. izquierda

Mandos_L2

; Si B.1 es 0 y no esta al tope de izquierda, mover


btfsc
PORTB, 1
goto Mandos_L2
movfw Barra_Y
btfsc STATUS, Z
goto Mandos_L2
movlw 254
; -2
movwf Barra_Y_Inc
; volvemos a dejar el puerto B a salida
bsf
STATUS, RP0
clrf TRISB
bcf
STATUS, RP0
;
; otra espera
movfw Espera
call
WaitNms
;
;

15

Loop_Sig_1

Loop_Sig_2

Loop_Sig_3

Loop_Sig_4

;
; hacer rebotar la pelota en las paredes
; colision arriba
movfw Pelota_X
btfss STATUS, Z
goto Loop_Sig_1
movlw 1
movwf Inc_X
; colision izquierda
movfw Pelota_Y
btfss STATUS, Z
goto Loop_Sig_2
movlw 1
movwf Inc_Y
; colision abajo
movfw Pelota_X
xorlw 60
btfss STATUS, Z
goto Loop_Sig_3
call Wait1Second
goto Start
; colision derecha
movfw Pelota_Y
xorlw 127
btfss STATUS, Z
goto Loop_Sig_4
movlw 255
; -1
movwf Inc_Y
; final de rebotes en paredes
;
;
;
;
; hacer rebotar la pelota si choca con la barra

dentro de los limites


Rebotes_Barra
X=56
colisiones

movlw 56

; la barra empieza a partir de

xorwf Pelota_X, W

; solo entonces miramos

btfss STATUS, Z
goto Rebotes_Barra_Salir
;
movfw Pelota_Y
;
addlw 1
subwf Barra_Y, W
btfsc STATUS, C ; Si Pelota_Y>=Barra_Y
goto Rebotes_Barra_Salir
;
movfw Barra_Y
;
addlw 25
subwf Pelota_Y, W

16

Rebotes_Barra_Salir

btfsc STATUS, C ; Y Pelota_Y<=(Barra_Y+24)


goto Rebotes_Barra_Salir
;
movlw 255
; -1
movwf Inc_X
;
;
;
; borrar la pelota
call LCD_Clear_Pixel
;
;
; borrar la barra
call Borrar_Barra
;
;
; mover la pelota a la siguiente posicion
movfw Inc_X
addwf Pelota_X, F
movfw Inc_Y
addwf Pelota_Y, F
;
;
;
;
; mover la barra hacia la siguiente posicion
movfw Barra_Y_Inc
addwf Barra_Y, F
;

;
;
; Comprobar la colision con los bloques
Colision_Bloques

sale
pelota
pelota

movlw b'11110000'
andwf Pelota_X, W
btfss STATUS, Z ; comprobar si X es <=15, si no,
goto Fin_Colision_Bloques
; comprobar si hay bloques en la posicion de la
; extraemos la pos. del bloque a partir de la
movfw Pelota_Y
movwf Temp_1
rrf
Temp_1, F
rrf
Temp_1, F
rrf
Temp_1, F
movlw b'00001111'

17

; bloques 8x8

Colision_Bloques_L1
bloque

borrar)
siguiente nivel

andwf Temp_1, F
btfss Pelota_X, 3
; comprobar si fila 1 o 2
goto Colision_Bloques_L1
bsf
Temp_1, 4 ; sumar 16
; accedemos al byte de ese
movlw Bloques
addwf Temp_1, W
movwf FSR
movfw INDF
btfsc STATUS, Z ; comprobamos si 0 (bloque borrado)
goto Fin_Colision_Bloques
;
; no es un cero, efectuamos rebotes
decf INDF
; si no decrementamos (marcar para
decfsz

Bloques_Quedan ; si ultimo bloque,

goto Colision_Bloques_L2
;
;
Partida_Ganada ; cuando se gana una partida
; restar 10 al retardo si no es ya 10
movlw -10
addwf Espera, W
btfsc STATUS, Z
goto Partida_Ganada_L1
movwf Espera
Partida_Ganada_L1
; Esperamos 1 segundo antes de pasar de fase
call
Wait1Second
; iniciamos una nueva partida con mas velocidad
goto Iniciar_Partida
;
;
Colision_Bloques_L2
; comprobamos si entro por la derecha, izquierda, o
medio
Colision_Bloques_Comp_Izquierda
movlw b'00000111'
andwf Pelota_Y, W
; nos interesan los 3 ultimos
bits
btfss STATUS, Z ; si desplazamiento dentro bloque es
cero
goto Colision_Bloques_Comp_Derecha
movlw 255
; -1
movwf Inc_Y
goto Colision_Bloques_Comp_Arriba
Colision_Bloques_Comp_Derecha
movfw Pelota_Y
movwf Temp_3
movlw b'00000111'
andwf Temp_3, F ; solo 3 ultimos bits
xorwf Temp_3, F
btfss STATUS, Z
goto Colision_Bloques_Comp_Arriba

18

movlw 1
movwf Inc_Y
Colision_Bloques_Comp_Arriba
movlw b'00000111'
andwf Pelota_X, W
btfss STATUS, Z ; si desplazamiento dentro bloque es
cero
goto Colision_Bloques_Comp_Abajo
movlw 255
; -1
movwf Inc_X
goto Fin_Colision_Bloques
Colision_Bloques_Comp_Abajo
movfw Pelota_X
movwf Temp_3
movlw b'00000111'
andwf Temp_3, F
xorwf Temp_3, F
btfss STATUS, Z
goto Fin_Colision_Bloques
movlw 1
movwf Inc_X
; fin de rebotes
;
Fin_Colision_Bloques
; Fin de comprobar la colision con los bloques
;
goto

Loop

sta es la parte de las subrutinas, se divide en dos. Las que solo sirven para el juego y las ms
generales que controlan el lcd.

;------------------------------------------------------------------------; Subrutinas
;------------------------------------------------------------------------;
; Borra la barra
;
Borrar_Barra
;
;

clrw
goto Pintar_Barra_C

;
; Pinta la barra
;

19

Pintar_Barra
Pintar_Barra_C

Pintar_Barra_CS1

movlw b'00111111'
movwf LCD_Data
movlw b'11000000'
andwf Barra_Y, W
btfsc STATUS, Z ; que mitad LCD empezamos a pintar
goto Pintar_Barra_CS1
bsf
PORTA, LCD_CS1
bcf
PORTA, LCD_CS2 ; segunda mitad LCD
goto Pintar_Barra_Mover
bcf
bsf

Pintar_Barra_Mover

PORTA, LCD_CS1 ; primera mitad LCD


PORTA, LCD_CS2

; movernos donde deberia estar la barra


movfw Barra_Y
movwf LCD_Address
call LCD_Set_Address_Y
movlw 7
movwf LCD_Page
call LCD_Set_Page_X
movlw 24
; 24 pixeles de barra
movwf Temp_1
movfw Barra_Y
movwf Temp_2
; para saber cuando

cambiar de mitad
Pintar_Barra_L1

Pintar_Barra_L2

;
;

movlw 64
xorwf Temp_2, W
btfss STATUS, Z ; hay que cambiar de mitad LCD?
goto Pintar_Barra_L2
bsf
PORTA, LCD_CS1
bcf
PORTA, LCD_CS2 ; segunda mitad LCD
movlw 7
movwf LCD_Page
call LCD_Set_Page_X ; seguir en la misma X
clrf LCD_Address
call LCD_Set_Address_Y ; Y=0
call LCD_Write_Display_Data
incf Temp_2, F
decfsz
Temp_1, F
goto Pintar_Barra_L1
return

;
;
; Pinta la mitad de los bloques en la media pantalla activa
;
Pintar_Bloques

20

clrf LCD_Address
clrf LCD_Page
call LCD_Set_Address_Y
call LCD_Set_Page_X
movlw 2
movwf Temp_Y
Pintar_Bloques_LY

movlw 8
movwf Temp_X
movwf LCD_Address ;esta variable apunta a la

siguiente

;columna por si hay que

saltarse bloques
Pintar_Bloques_LX

bloque

movfw INDF
;
; Pintar_Bloque en el LCD
btfsc STATUS, Z
goto LCD_Saltarse_Bloque ; si era 0, saltarse

addlw 255
; -1
btfsc STATUS, Z ; si era 1, pintarlo en blanco
goto LCD_Pintar_Bloque_Blanco
LCD_Pintar_Bloque_Normal
; si era otro , pintar normal
movlw b'11111111'
movwf LCD_Data
call LCD_Write_Display_Data
movlw b'11000011'
movwf LCD_Data
call LCD_Write_Display_Data
movlw b'10100101'
movwf LCD_Data
call LCD_Write_Display_Data
movlw b'10011001'
movwf LCD_Data
call LCD_Write_Display_Data
movlw b'10011001'
movwf LCD_Data
call LCD_Write_Display_Data
movlw b'10100101'
movwf LCD_Data
call LCD_Write_Display_Data
movlw b'11000011'
movwf LCD_Data
call LCD_Write_Display_Data
movlw b'11111111'
movwf LCD_Data
call LCD_Write_Display_Data
goto LCD_Pintar_Bloque_Salir
LCD_Pintar_Bloque_Blanco
clrf LCD_Data
; pintar 8 pages en blanco
movlw 8
movwf Temp_3
LCD_Pintar_Bloque_Blanco_L1
call LCD_Write_Display_Data

21

LCD_Saltarse_Bloque

decfsz
Temp_3
goto LCD_Pintar_Bloque_Blanco_L1
;
decf INDF ; no pintar ya mas
goto LCD_Pintar_Bloque_Salir

call LCD_Set_Address_Y
LCD_Pintar_Bloque_Salir
; Fin de pintar_Bloque en el LCD
;
movlw 8
; incrementar el puntero
al
addwf LCD_Address, F ; siguiente bloque
incf FSR
decfsz
Temp_X
goto Pintar_Bloques_LX
;
movlw 8
; siguiente hilera de bloques
addwf FSR, F
incf LCD_Page
call LCD_Set_Page_X
decfsz
Temp_Y
goto Pintar_Bloques_LY
return
;
;
;
; Borra la pagina entera donde se encuentre el pixel especificado
; en LCD_Pixel_X, LCD_Pixel_Y
;
LCD_Clear_Pixel
movlw 0
goto LCD_Put_Pixel_L1
;
;
;
; Pinta pixel especificado en LCD_Pixel_X, LCD_Pixel_Y
;
LCD_Put_Pixel
movfw LCD_Pixel_X
andlw b'00000111'
call Pixel_Value
LCD_Put_Pixel_L1
movwf LCD_Data
bcf
PORTA, LCD_CS1
bsf
PORTA, LCD_CS2 ; primera mitad LCD
movfw LCD_Pixel_Y
movwf LCD_Address
andlw b'11000000'
btfsc STATUS, Z ; comprobar de que mitad es
goto LCD_Put_Pixel_L2
bsf
PORTA, LCD_CS1

22

LCD_Put_Pixel_L2

;
;

bcf

PORTA, LCD_CS2 ; segunda mitad LCD

call LCD_Set_Address_Y
movfw LCD_Pixel_X
movwf LCD_Page
rrf
LCD_Page, F
rrf
LCD_Page, F
rrf
LCD_Page, F
call LCD_Set_Page_X
call LCD_Write_Display_Data
return

;
; Borra la mitad del LCD seleccionada en ese momento
;
LCD_Clear
; Limpiar pantalla
clrf LCD_Data
clrf LCD_Address
clrf LCD_Page
call LCD_Set_Page_X
call LCD_Set_Address_Y
LCD_Clear_L1
movlw 64
movwf Temp_2
LCD_Clear_L2
call LCD_Write_Display_Data
decfsz
Temp_2, F
goto LCD_Clear_L2
;
incf LCD_Page, F
call LCD_Set_Page_X
btfss LCD_Page, 3
goto LCD_Clear_L1
;
return
;
;
;
; Aqui empiezan las rutinas de mas bajo nivel del manejo del LCD
;
;
; Situa la Pagina activa de la mitad del LCD seleccionada en ese momento
; a LCD_Page
;
LCD_Set_Page_X
call Wait_For_Busy_Flag
bcf
PORTA, LCD_RW
bcf
PORTA, LCD_RS

23

;
;

movfw LCD_Page
iorlw b'11111000'
xorlw b'01000000'
movwf PORTB
call Wait
bsf
PORTA, LCD_E
call Wait
bcf
PORTA, LCD_E
return

;
; Situa la Y Address activa de la mitad del LCD seleccionada
; en ese momento a LCD_Address
;
LCD_Set_Address_Y
call Wait_For_Busy_Flag
bcf
PORTA, LCD_RW
bcf
PORTA, LCD_RS
movfw LCD_Address
iorlw b'11000000'
xorlw b'10000000'
movwf PORTB
call Wait
bsf
PORTA, LCD_E
call Wait
bcf
PORTA, LCD_E
return
;
;
;
; Escribe el dato en LCD_Data en la Pagina X y Direccion Y
; activas en la mitad seleccionada del LCD en ese momento
;
LCD_Write_Display_Data
call Wait_For_Busy_Flag
bcf
PORTA, LCD_RW
bsf
PORTA, LCD_RS
movfw LCD_Data
movwf PORTB
call Wait
bsf
PORTA, LCD_E
call Wait
bcf
PORTA, LCD_E
return
;
;
;
; Apaga la mitad seleccionada del LCD en ese momento
;

24

LCD_Off

;
;

call Wait_For_Busy_Flag
bcf
PORTA, LCD_RW
bcf
PORTA, LCD_RS
movlw b'00111110'
movwf PORTB
call Wait
bsf
PORTA, LCD_E
call Wait
bcf
PORTA, LCD_E
return

;
; Enciende la mitad seleccionada del LCD en ese momento
; y situa su Display Start Line a 0
;
LCD_On
call Wait_For_Busy_Flag
bcf
PORTA, LCD_RW
bcf
PORTA, LCD_RS
movlw b'00111111'
movwf PORTB
call Wait
bsf
PORTA, LCD_E
call Wait
bcf
PORTA, LCD_E
;
; Display Start Line = 0
;
call Wait_For_Busy_Flag
bcf
PORTA, LCD_RW
bcf
PORTA, LCD_RS
movlw b'11000000'
movwf PORTB
call Wait
bsf
PORTA, LCD_E
call Wait
bcf
PORTA, LCD_E
;
return
;
;
;
; Espera por la Busy Flag de la mitad del LCD seleccionada en ese momento
;
Wait_For_Busy_Flag
;
bsf
STATUS, RP0
; poner puerto B a leer
movlw H'FF'
movwf
TRISB
;
bcf
STATUS, RP0
;
;

25

bcf
PORTA, LCD_RS
bsf
PORTA, LCD_RW
;
call Wait
bsf
PORTA, LCD_E
call Wait
L1

estaba

;
;

btfsc
PORTB, 7
; BUSY=bit 7
goto L1
bcf
PORTA, LCD_E
;
bsf
STATUS, RP0
; dejar el puerto B como
clrf TRISB
bcf
STATUS, RP0
;
return

;
;

;
; Rutinas de espera
;

Optamos por usar una nica rutina Wait para todas las esperas al mandar instrucciones al LCD
(Tiempo que tiene que estar E alto/bajo, tiempo antes de poder leer, etc, todas siempre menos de 1
us). De esta forma podremos adaptar a otras velocidades de reloj si hace falta.
;
; Esperar 1 us a 12 mhz (sobra 1 ciclo contando el call y el return)
;
Wait
return
;
;
;
; Espera aproximadamente 1 milisegundo a 12 mhz
;
; [(8+1+1+2)*248]+(8+1+2)+8=2995 (sin contar el call)
Esperar_2995_Ciclos
movlw 249
nop
goto $+1
goto $+1
Esperar_2995_Ciclos_L1
goto $+1
goto $+1
goto $+1
goto $+1
addlw -1
btfss STATUS, Z

26

goto Esperar_2995_Ciclos_L1
return
;
; Espera el numero de ms especificados en W
;
WaitNms
movwf Temp_Count
WaitNms_L1
call Esperar_2995_Ciclos
decfsz
Temp_Count
goto WaitNms_L1
return
;
;
;
; Espera 1 Segundo
;
Wait1Second
movlw
call
movlw
call
movlw
call
movlw
goto

250
WaitNms
250
WaitNms
250
WaitNms
250
WaitNms

END

27

Bibliografa:
1. Manual LCD grfico CFAG12864BTMIV (http://www.crystalfontz.com/)
2. Manual PIC16F84A (http://microchip.com/)
3. Standard TTL logic levels (http://www.twysted-pair.com/74xx.htm)

28

También podría gustarte