1 nov. 2012

Control LCD PICDEM 2 Plus

En esta entrada os enseñaré como podréis controlar fácilmente el LCD de esta placa fabricada por Microchip




Si alguno de vosotros ha trabajado con esta placa se habrá dado cuenta de que hay un montón de revisiones; y que en la última han hecho cambios a nivel de cableado del LCD, ya que ahora el LCD usa las lineas RD0-RD3 para enviar el dato al LCD y RD4-RD7 para el control, es decir, utiliza todo el puerto D para controlar el LCD, a diferencia de antes, donde también se usaba el puerto C. Este cambio me ha dado un montón de problemas, ya que incluso alguna parte del datasheet de la placa está sin corregir por lo que tuve que hacer cientos de pruebas para ver su conexión final y poder controlarlo correctamente.


Después de mil quebraderos de cabeza llegué a la conclusión de que el LCD efectivamente estaba conectado por completo al puerto D, por lo que a partir de ahí empecé a buscar por todos los lados soluciones de software que me permitiesen controlar el LCD fácilmente, encontré cientos de soluciones en lenguaje C, incluso Microchip proporciona ejemplos donde el LCD funcionaba correctamente, eso si, todo en lenguaje C y yo quería controlar mi LCD desde abajo, es decir, quería una librería en ensamblador, después de buscar durante varios días sin descanso no encontré nada, así que comencé a leer como controlar LCDs Hitachi HD44780 ( que es uno de los más usados, y el que utiliza la placa ), ya había programado varios programas en C para ese LCD, pero nunca lo había programado desde abajo.

Después de varios días aprendiendo cosas de este LCD vi que era relativamente fácil hacerlo funcionar, así que organicé mis ideas y me puse a escribir una librería para controlar este LCD desde 0.

Durante 2 semanas de vacaciones, parte del tiempo en un camping creé dicha librería ( mi mente no puede parar de pensar ), y a lo largo del verano la fui perfeccionando.




La librería consta de varias subrutinas, en total consta de 18 subrutinas, que se pueden resumir en:

LCD_Inicializa: Configura los puertos para el correcto funcionamiento del LCD
LCD_Escribe:  Escribe el dato del acumulador en el LCD
LCD_Linea_2: Sitúa el cursor del LCD en la linea 2
LCD_Linea_1: Sitúa el cursor del LCD en la linea 1
LCD_Borra: Borra la pantalla del LCD
LCD_Escribe_Eñe: Escribe en el LCD la letra " ñ " en pantalla
LCD_Escribe_Grado: Escribe en el LCD el simbolo " º "
LCD_Apagar: Apaga el LCD
LCD_Saludo: Aparecen en el LCD varios mensajes preprogramados en la librería
LCD_Home: Mueve el cursor a la posición 1 de la linea 1
LCD_Retardo_X: Genera un retardo de X tiempo ( Muestra un mensaje durante X tiempo )
LCD_Byte: Envia un dato en binario al LCD y lo escribe

Con todas esas subrutinas se puede hacer manejo del LCD casi al completo, todas son muy sencillas de modificar y se puede hacer uso de ellas en otros programas o pics para la que está pensada inicialmente, la librería completa os la escribo a continuación, recordad que la debéis de guardar con extensión .INC :




LCD.INC



;Libreria LCD.INC
;
;Creada por Mister_mst
;
;Pensada para usar con PIC16F877A y la placa de
;Experimentación PICDEM 2 PLUS
;
;Pines del LCD:
;
;-PWR -> RD7
;-Enable -> RD6
;-RW -> RD5
;-RS -> RD4
;-DATO0 -> RD0
;-DATO1 -> RD1
;-DATO2 -> RD2
;-DATO3 -> RD3



;Espacios de memoria reservados para estos registros

RS_SEL EQU 0x24
LOOP EQU 0x25
LOOPA EQU 0x26
STORE EQU 0x28
temp_1 EQU 0x30
temp_2 EQU 0x31
temp_3 EQU 0x32
Dato EQU 0x33
R_Cont1 EQU 0x34 ; Contadores para los retardos internos
R_Cont2 EQU 0x35 ; Contadores para los retardos internos
R_ContAA EQU 0x36 ; Contadores para los retardos de ON OFF LCD
R_ContBB EQU 0x37 ; Contadores para los retardos de ON OFF LCD
R_ContCC EQU 0x38 ; Contadores para los retardos de ON OFF LCD
Dato_2 EQU 0x39 ; Espacio reservado para la subrutina LCD_Byte
Dato_3 EQU 0x40 ; Espacio reservado para la subrutina LCD_Byte

LCD_Inicializa

bcf STATUS,RP0 ; Banco 0
bcf STATUS,RP1 ; Banco 0
clrf PORTD ; Pone a 0 los pines del Puerto D

BSF 0x03,5 ; Me cambio al banco 1
clrf TRISD ; PORTD es configurado como Salida
BCF 0x03,5 ; Me cambio al banco 0
call delay ; Genera un retardo
bsf PORTD,7 ; Pone a 1 el PWR del LCD
call delay ; Vuelve a llamar a delay
call LCDSET
return ; Vuelve al programa principal


TABLCD: addwf PCL,F ; Tabla de inicializacion del LCD
retlw B'00110011' ; Inicializando LCD, Primer bit
retlw B'00110011' ; 2º bit, (repetición del primero)
retlw B'00110010' ; LCD con bus de 4 bits
retlw B'00101100' ; LCD de 2 lineas
retlw B'00000110' ; Incremento automatico de la posicion del cursor
retlw B'00001100' ; LCD ON, Cursor off, Blink off
retlw B'00000001' ; Clear display
retlw B'00000010' ; Return home, cursor & RAM to zero
MESSAGE
addwf PCL,F
movf Dato,W
return

LCDSET clrf LOOP ; Limpia este registro definido anteriormente en una posicion de memoria libre
bcf RS_SEL,4 ; Pone a 1 el bit 4 del registro RS_SEL ( el pin del LCD RS es el RD4 )
movlw 0x80
movwf RS_SEL

LCDST2: movf LOOP,W ; Mueve LOOP a W ( se limpió antes ? )
call TABLCD ; Obtener la tabla de instrucciones
call LCD_Salida ; Realizarlo
incf LOOP,F ; Incrementar bucle
btfss LOOP,3 ; Ya se ha hecho la puesta a punto del LCD? (?)
goto LCDST2 ; NO
call delay
return ; Vuelve a LCD_Inicializa

LCD_Escribe

movwf Dato
bsf RS_SEL,4 ; Set pin RS para enviar instrucciones

LCDMESS_1

movlw b'00000000' ; Obtener la tabla de direcciones
call MESSAGE ; Obtener la tabla de instrucciones
call LCD_Salida ; Inicializarlo
call delay ; SI, call delay
return ; Vuelve al programa principal


;Otros Comandos
;



LCD_Linea_2

bcf RS_SEL,4 ; Habilito la accion de enviar comando
movlw b'11000000' ; Me situo en la linea 2
call LCD_Salida ; Lo envio al LCD
return ; Vuelvo al programa principal

LCD_Linea_1

bcf RS_SEL,4 ; Habilito la accion de enviar comando
movlw b'10000000' ; Me situo en la linea 1
call LCD_Salida ; Lo envio al LCD
return ; Vuelvo al programa principal

LCD_Borra

bcf RS_SEL,4 ; Habilito la accion de enviar comando
movlw b'00000001' ; Borro el LCD
call LCD_Salida ; Lo envio al LCD
return ; Vuelvo al programa principal


LCD_Escribe_Eñe

bsf RS_SEL,4 ; Hablito la opción de escribir
movlw b'11101110' ; Código ASCII de la ñ
call LCD_Salida ; Lo envio al LCD
return ; Vuelvo al programa principal

LCD_Escribe_Grado

bsf RS_SEL,4 ; Hablito la opción de escribir
movlw b'11011111' ; Codigo ASCII de el simbolo º
call LCD_Salida ; Lo envio al LCD
return ; Vuelvo al programa principal

LCD_Apagar

bcf PORTD,6
return ; Vuelvo al programa principal

LCD_Saludo

goto LCD_Saludo_1
return ; Vuelvo al programa principal

LCD_Home

bcf RS_SEL,4 ; Hablito la opción de escribir
movlw b'00000010' ; Devuelvo el cursor a el primer digito
call LCD_Salida ; Lo envio al LCD
return ; Vuelvo al programa principal


;LCD RETARDOS



LCD_Retardo_20s

movlw d'200'
goto Ret
return

LCD_Retardo_10s

movlw d'100'
goto Ret
return

LCD_Retardo_5s

movlw d'50'
goto Ret
return

LCD_Retardo_2s

movlw d'20'
goto Ret
return

LCD_Retardo_1s

movlw d'10'
goto Ret
return

LCD_Retardo_500ms

movlw d'5'
goto Ret
return


;LCD OUT


LCD_Salida

movwf STORE
movlw 60
movwf LOOPA

DELAY
decfsz LOOPA,F
goto DELAY
call SENDIT
call SENDIT
return

SENDIT swapf STORE,F
movf STORE,W
andlw b'00001111' ; Me quedo con el nibble bajo
iorwf RS_SEL,W
movwf PORTD
nop
BSF PORTD,6
nop
BCF PORTD,6
return

;DELAY


delay: ; Retardo de 20 ms ( Suficiente ? )

movlw d'20'
movwf R_Cont2
BucleExterno
movlw d'249' ; Aporta Mx1 ciclos máquina. Este es el valor de "K".
movwf R_Cont1 ; Aporta Mx1 ciclos máquina.
BucleInterno
nop ; Aporta KxMx1 ciclos máquina.
decfsz R_Cont1,F ; (K-1)xMx1 cm (cuando no salta) + Mx2 cm (al saltar).
goto BucleInterno ; Aporta (K-1)xMx2 ciclos máquina.
decfsz R_Cont2,F ; (M-1)x1 cm (cuando no salta) + 2 cm (al saltar).
goto BucleExterno ; Aporta (M-1)x2 ciclos máquina.
return

Ret

movwf R_ContCC ; Aporta 1 ciclo máquina.
Bucle_Ext_2
movlw d'100' ; Aporta Nx1 ciclos máquina. Este es el valor de "M".
movwf R_ContBB ; Aporta Nx1 ciclos máquina.
Bucle_Ext_1
movlw d'249' ; Aporta MxNx1 ciclos máquina. Este es el valor de "K".
movwf R_ContAA ; Aporta MxNx1 ciclos máquina.
Bucle_Int_1
nop ; Aporta KxMxNx1 ciclos máquina.
decfsz R_ContAA,F ; (K-1)xMxNx1 cm (si no salta) + MxNx2 cm (al saltar).
goto Bucle_Int_1 ; Aporta (K-1)xMxNx2 ciclos máquina.
decfsz R_ContBB,F ; (M-1)xNx1 cm (cuando no salta) + Nx2 cm (al saltar).
goto Bucle_Ext_1 ; Aporta (M-1)xNx2 ciclos máquina.
decfsz R_ContCC,F ; (N-1)x1 cm (cuando no salta) + 2 cm (al saltar).
goto Bucle_Ext_2 ; Aporta (N-1)x2 ciclos máquina.
return


; LCD_Byte



LCD_Byte

movwf Dato_3 ; Guarda el valor de entrada.
andlw b'11110000' ; Analiza si el nibble alto es cero.
btfss STATUS,Z ; Si es cero lo apaga.
goto LCD_Alto ; No es cero y lo visualiza.
movlw ' ' ; Visualiza un espacio en blanco.
call LCD_Escribe
goto LCD_Bajo


LCD_ByteCompleto

movwf Dato_3 ; Guarda el valor de entrada.
LCD_Alto
swapf Dato_3,W ; Pone el nibble alto en la parte baja.
call LCD_Nibble ; Lo visualiza.
LCD_Bajo
movf Dato_3,W ; Repite el proceso con el nibble bajo.


LCD_Nibble
andlw b'00001111' ; Se queda con la parte baja.
movwf Dato_2 ; Lo guarda.
sublw 0x09 ; Comprueba si hay que representarlo con letra.
btfss STATUS,C
goto Letra
movf Dato_2,W
addlw '0' ; El número se pasa a carácter ASCII sumándole
goto Fin_Visualizacion ; el ASCII del cero y lo visualiza.
Letra
movf Dato_2,W
addlw 'A'-0x0A ; Sí, por tanto, se le suma el ASCII de la 'A'.
Fin_Visualizacion
call LCD_Escribe
return




; SALUDO


LCD_Saludo_1



movlw 'H'
call LCD_Escribe
movlw 'o'
call LCD_Escribe
movlw 'l'
call LCD_Escribe
movlw 'a'
call LCD_Escribe
movlw ' '
call LCD_Escribe
movlw 'a'
call LCD_Escribe
movlw ' '
call LCD_Escribe
movlw 't'
call LCD_Escribe
movlw 'o'
call LCD_Escribe
movlw 'd'
call LCD_Escribe
movlw 'o'
call LCD_Escribe
movlw ' '
call LCD_Escribe
movlw 'e'
call LCD_Escribe
movlw 'l'
call LCD_Escribe

call LCD_Linea_2 ; Segunda Liena de la primera pantalla
movlw 'M'
call LCD_Escribe
movlw 'u'
call LCD_Escribe
movlw 'n'
call LCD_Escribe
movlw 'd'
call LCD_Escribe
movlw 'o'
call LCD_Escribe
call LCD_Retardo_1s ; Retardo mensaje mostrado
call LCD_Borra ; Borro el LCD para escribir la nueva pantalla
call LCD_Retardo_500ms ; Retardo mensaje borrado




call LCD_Linea_1 ; Me vuelvo a la linea 1
movlw 'P'
call LCD_Escribe
movlw 'o'
call LCD_Escribe
movlw 'd'
call LCD_Escribe
movlw 'e'
call LCD_Escribe
movlw 'i'
call LCD_Escribe
movlw 's'
call LCD_Escribe
movlw ' '
call LCD_Escribe
movlw 'l'
call LCD_Escribe
movlw 'l'
call LCD_Escribe
movlw 'a'
call LCD_Escribe
movlw 'm'
call LCD_Escribe
movlw 'a'
call LCD_Escribe
movlw 'r'
call LCD_Escribe
movlw 'm'
call LCD_Escribe
movlw 'e'
call LCD_Escribe

call LCD_Linea_2 ; Segunda linea de la segunda pantalla
movlw 'M'
call LCD_Escribe
movlw 'i'
call LCD_Escribe
movlw 's'
call LCD_Escribe
movlw 't'
call LCD_Escribe
movlw 'e'
call LCD_Escribe
movlw 'r'
call LCD_Escribe
movlw '_'
call LCD_Escribe
movlw 'm'
call LCD_Escribe
movlw 's'
call LCD_Escribe
movlw 't'
call LCD_Escribe
call LCD_Retardo_1s ; Retardo mensaje mostrado
call LCD_Borra ; Borro el LCD para escribir la nueva pantalla
call LCD_Retardo_500ms ; Retardo LCD apagado





call LCD_Linea_1 ; Vuelvo a la linea 1
movlw 'E'
call LCD_Escribe
movlw 's'
call LCD_Escribe
movlw 'p'
call LCD_Escribe
movlw 'e'
call LCD_Escribe
movlw 'r'
call LCD_Escribe
movlw 'o'
call LCD_Escribe
movlw ' '
call LCD_Escribe
movlw 'q'
call LCD_Escribe
movlw 'u'
call LCD_Escribe
movlw 'e'
call LCD_Escribe

call LCD_Linea_2 ; Segunda linea de la tercera pantalla
movlw 'D'
call LCD_Escribe
movlw 'i'
call LCD_Escribe
movlw 's'
call LCD_Escribe
movlw 'f'
call LCD_Escribe
movlw 'r'
call LCD_Escribe
movlw 'u'
call LCD_Escribe
movlw 't'
call LCD_Escribe
movlw 'e'
call LCD_Escribe
movlw 'i'
call LCD_Escribe
movlw 's'
call LCD_Escribe
movlw ' '
call LCD_Escribe
movlw 'c'
call LCD_Escribe
movlw 'o'
call LCD_Escribe
movlw 'n'
call LCD_Escribe
call LCD_Retardo_1s ; Retardo mensaje mostrado
call LCD_Borra ; Borro el LCD para mostrar la nueva pantalla
call LCD_Retardo_500ms ; Retardo mensaje borrado



call LCD_Linea_1 ; Me vuelvo a la linea 1
movlw 'M'
call LCD_Escribe
movlw 'i'
call LCD_Escribe
movlw ' '
call LCD_Escribe
movlw 'p'
call LCD_Escribe
movlw 'r'
call LCD_Escribe
movlw 'o'
call LCD_Escribe
movlw 'y'
call LCD_Escribe
movlw 'e'
call LCD_Escribe
movlw 'c'
call LCD_Escribe
movlw 't'
call LCD_Escribe
movlw 'o'
call LCD_Escribe

call LCD_Linea_2 ; Segunda linea cuerta pantalla
movlw '.'
call LCD_Escribe
movlw '.'
call LCD_Escribe
movlw '.'
call LCD_Escribe
movlw ' '
call LCD_Escribe
movlw 'U'
call LCD_Escribe
movlw 'n'
call LCD_Escribe
movlw ' '
call LCD_Escribe
movlw 'S'
call LCD_Escribe
movlw 'a'
call LCD_Escribe
movlw 'l'
call LCD_Escribe
movlw 'u'
call LCD_Escribe
movlw 'd'
call LCD_Escribe
movlw 'o'
call LCD_Escribe
call LCD_Retardo_2s ; Retardo mensaje mostrado
call LCD_Borra ; Borro el LCD para escribir nueva información
call LCD_Linea_1 ; Me vuelvo a la linea 1
call LCD_Retardo_500ms ; Retardo para escribir la proxima información

return ; Vuelvo a LCD_Saludo



4 comentarios:

Anónimo dijo...

Alguna lcd facil de usar con la raspberry pi?

Mister_mst dijo...

Hola, cualquiera compatible con el Hitachi HD44780


Un saludo

Anónimo dijo...

Y se conectaria por USB?

Mister_mst dijo...

Hay "modulos" que permiten conectar dicho LCD por USB... pero en un principio no... se conectaría a los pines GPIO... de todas formas, para lo poco que "estudié" ese aspecto, encontré mucha documentación.

ir arriba