;************************************ Mensaje*******************************************
;
; El módulo LCD visualiza un mensaje largo (más de 16 caracteres) que se desplaza a lo largo
; de la pantalla. Se utiliza la subrutina LCD_MensajeMovimiento de la librería LCD_MENS.INC.
; 
; ZONA DE DATOS **********************************************************************

    LIST        P=16F84A
    #INCLUDE <P16F84A.INC>       
    __CONFIG    _CP_OFF &  _WDT_OFF & _PWRTE_ON & _XT_OSC

    CBLOCK  0x0C
ARRIBA	
    ENDC

; ZONA DE CÓDIGOS ********************************************************************

    ORG    0
Inicio
    call    LCD_Inicializa            ; Prepara la pantalla.
	clrf	ARRIBA
Principal

    movlw    Mensaje0                ; Apunta al mensaje.
    call    LCD_MensajeMovimiento

	btfss ARRIBA,0 ;1 salta, 0 continua
	goto mov_arriba
	btfsc ARRIBA,0 ;0 salta, 1 continua
	goto mov_abajo

	mov_abajo
	movlw 0X00
	movwf ARRIBA
    goto    Principal                ; Repite la visualización.
	
	mov_arriba
	movlw 0XFF
	movwf ARRIBA
    goto    Principal                ; Repite la visualización.

; "Mensajes" ----------------------------------------------------------------------------
;
Mensajes
    addwf    PCL,F
Mensaje0                            ; Posición inicial del mensaje.
    DT "                "                ; Espacios en blanco al principio para mejor
    DT "Covarrubias Por Favor  "
    DT "Califique Esta  "    ; visualización.
    DT "Practica "
    DT ";D!  "
    DT "                ", 0x0        ; Espacios en blanco al final.

    
    
    



;-----------------------LIBRERÍAS--------------------------------------------------

;**************************** Librería "LCD_4BIT.INC" ***********************************

; ZONA DE DATOS *********************************************************************

    CBLOCK
    LCD_Dato
    LCD_GuardaDato
    LCD_GuardaTRISB
    LCD_Auxiliar1
    LCD_Auxiliar2
    ENDC

LCD_CaracteresPorLinea    EQU    .16            ; Número de caracteres por línea de la pantalla.

#DEFINE  LCD_PinRS    PORTA,0
#DEFINE  LCD_PinRW    PORTA,1
#DEFINE  LCD_PinEnable    PORTA,2
#DEFINE  LCD_BusDatos    PORTB

; Subrutina "LCD_Inicializa" ------------------------------------------------------------
;
; Inicialización del módulo LCD: Configura funciones del LCD, produce reset por software,
; borra memoria y enciende pantalla. El fabricante especifica que para garantizar la
; configuración inicial hay que hacerla como sigue:
;
LCD_Inicializa
    bsf        STATUS,RP0                ; Configura las líneas conectadas al pines RS,
    bcf        LCD_PinRS                ; R/W y E.
    bcf        LCD_PinEnable
    bcf        LCD_PinRW
    bcf        STATUS,RP0
    bcf        LCD_PinRW                ; En caso de que esté conectado le indica
                                    ; que se va a escribir en el LCD.
    bcf        LCD_PinEnable            ; Impide funcionamiento del LCD poniendo E=0.
    bcf     LCD_PinRS                ; Activa el Modo Comando poniendo RS=0.
    call    Retardo_20ms
    movlw    b'00110000'    
    call    LCD_EscribeLCD            ; Escribe el dato en el LCD.
    call    Retardo_5ms    
    movlw    b'00110000'    
    call    LCD_EscribeLCD
    call    Retardo_200micros
    movlw    b'00110000'    
    call    LCD_EscribeLCD
    call    Retardo_20micros        ; Este retardo es necesario para simular en PROTEUS.
    movlw    b'00100000'                ; Interface de 4 bits.
    call    LCD_EscribeLCD
    call    Retardo_20micros        ; Este retardo es necesario para simular en PROTEUS.
    
; Ahora configura el resto de los parámetros: 

    call    LCD_2Lineas4Bits5x7        ; LCD de 2 líneas y caracteres de 5x7 puntos.
    call    LCD_Borra                ; Pantalla encendida y limpia. Cursor al principio

	btfss 	ARRIBA,0			;1 salta, 0 continua
	call 	LCD_Linea2
	btfsc	ARRIBA,0		;0 salta, 1 continua
	call 	LCD_Linea1
    call    LCD_CursorOFF            ; de la línea 1. Cursor apagado.
    call    LCD_CursorIncr            ; Cursor en modo incrementar.
    return

; Subrutina "LCD_EscribeLCD" -----------------------------------------------------------
;
; Envía el dato del registro de trabajo W al bus de dato y produce un pequeño pulso en el pin
; Enable del LCD. Para no alterar el contenido de las líneas de la parte baja del Puerto B que
; no son utilizadas para el LCD (pines RB3:RB0), primero se lee estas líneas y después se
; vuelve a enviar este dato sin cambiarlo.

LCD_EscribeLCD
    andlw    b'11110000'                ; Se queda con el nibble alto del dato que es el
    movwf    LCD_Dato                ; que hay que enviar y lo guarda.
    movf    LCD_BusDatos,W            ; Lee la información actual de la parte baja
    andlw    b'00001111'                ; del Puerto B, que no se debe alterar.
    iorwf    LCD_Dato,F                ; Enviará la parte alta del dato de entrada
                                    ; y en la parte baja lo que había antes.
    bsf        STATUS,RP0                ; Acceso al Banco 1.
    movf    TRISB,W                    ; Guarda la configuración que tenía antes TRISB.
    movwf    LCD_GuardaTRISB
    movlw    b'00001111'                ; Las 4 líneas inferiores del Puerto B se dejan 
    andwf    PORTB,F                    ; como estaban y las 4 superiores como salida.
    bcf        STATUS,RP0                ; Acceso al Banco 0.
;
    movf    LCD_Dato,W                ; Recupera el dato a enviar.
    movwf    LCD_BusDatos            ; Envía el dato al módulo LCD.
    bsf        LCD_PinEnable            ; Permite funcionamiento del LCD mediante un pequeño
    bcf        LCD_PinEnable            ; pulso y termina impidiendo el funcionamiento del LCD.
    bsf        STATUS,RP0                ; Acceso al Banco 1. Restaura el antiguo valor en
    movf    LCD_GuardaTRISB,W        ; la configuración del Puerto B.
    movwf    TRISB
    bcf        STATUS,RP0                ; Acceso al Banco 0.
    return

; Subrutinas variadas para el control del módulo LCD -----------------------------------------
;
;Los comandos que pueden ser ejecutados son:
;
	
LCD_Linea1				; Cursor al principio de la Línea 1.
	movlw	b'10000000'		; Dirección 00h de la DDRAM
	goto	LCD_EnviaComando

LCD_Linea2				; Cursor al principio de la Línea 2.
	movlw	b'11000000'		; Dirección 40h de la DDRAM
	goto	LCD_EnviaComando

LCD_CursorIncr                        ; Cursor en modo incrementar.
    movlw    b'00000100'
    goto    LCD_EnviaComando

LCD_CursorOFF                        ; Pantalla encendida y cursor apagado.
    movlw    b'00001100'
    goto    LCD_EnviaComando

LCD_Borra                            ; Borra toda la pantalla, memoria DDRAM y pone el 
    movlw    b'00000001'                ; cursor a principio de la línea 1.
    goto    LCD_EnviaComando
LCD_2Lineas4Bits5x7                    ; Define la pantalla de 2 líneas, con caracteres
    movlw    b'00101000'                ; de 5x7 puntos y conexión al PIC mediante bus de
;    goto    LCD_EnviaComando        ; 4 bits. 

; Subrutinas "LCD_EnviaComando" y "LCD_Caracter" ------------------------------------
;
; "LCD_EnviaComando". Escribe un comando en el registro del módulo LCD. La palabra de
; comando ha sido entregada a través del registro W.  Trabaja en Modo Comando.
; "LCD_Caracter". Escribe en la memoria DDRAM del LCD el carácter ASCII introducido a
; a través del registro W. Trabaja en Modo Dato.
;
LCD_EnviaComando
    bcf        LCD_PinRS                ; Activa el Modo Comando, poniendo RS=0.
    goto    LCD_Envia

LCD_Caracter
    bsf        LCD_PinRS                ; Activa el "Modo Dato", poniendo RS=1.
    call    LCD_CodigoCGROM            ; Obtiene el código para correcta visualización.

LCD_Envia
    movwf    LCD_GuardaDato            ; Guarda el dato a enviar.
    call    LCD_EscribeLCD            ; Primero envía el nibble alto.
    swapf    LCD_GuardaDato,W        ; Ahora envía el nibble bajo. Para ello pasa el
                                    ; nibble bajo del dato a enviar a parte alta del byte.
    call    LCD_EscribeLCD            ; Se envía al visualizador LCD.
    btfss    LCD_PinRS                ; Debe garantizar una correcta escritura manteniendo 
    call    Retardo_2ms                ; 2 ms en modo comando y 50 µs en modo cáracter.
    call    Retardo_50micros
    return    

; Subrutina "LCD_CodigoCGROM" -----------------------------------------------------------
;
; A partir del carácter ASCII número 127 los códigos de los caracteres definidos en la
; tabla CGROM del LM016L no coinciden con los códigos ASCII. Así por ejemplo, el código
; ASCII de la "Ñ" en la tabla CGRAM del LM016L es EEh.
;
; Esta subrutina convierte los códigos ASCII de la "Ñ", "º" y otros, a códigos CGROM para que
; que puedan ser visualizado en el módulo LM016L.
; 
; Entrada:    En (W) el código ASCII del carácter que se desea visualizar.
; Salida:    En (W) el código definido en la tabla CGROM.

LCD_CodigoCGROM
    movwf    LCD_Dato                ; Guarda el valor del carácter y comprueba si es
LCD_EnheMinuscula                    ; un carácter especial.
    sublw    'ñ'                     ; ¿Es la "ñ"?
    btfss    STATUS,Z
    goto    LCD_EnheMayuscula        ; No es "ñ".
    movlw    b'11101110'                ; Código CGROM de la "ñ".
    movwf    LCD_Dato
    goto    LCD_FinCGROM
LCD_EnheMayuscula
    movf    LCD_Dato,W                ; Recupera el código ASCII de entrada.
    sublw    'Ñ'                     ; ¿Es la "Ñ"?
    btfss    STATUS,Z
    goto    LCD_Grado                ; No es "Ñ".
    movlw    b'11101110'                ; Código CGROM de la "ñ". (No hay símbolo para
    movwf    LCD_Dato                ; la "Ñ" mayúscula en la CGROM).
    goto    LCD_FinCGROM    
LCD_Grado
    movf    LCD_Dato,W                ; Recupera el código ASCII de entrada.
    sublw    'º'                     ; ¿Es el símbolo "º"?
    btfss    STATUS,Z
    goto    LCD_FinCGROM            ; No es "º".
    movlw    b'11011111'                ; Código CGROM del símbolo "º".
    movwf    LCD_Dato
LCD_FinCGROM
    movf    LCD_Dato,W                ; En (W) el código buscado.
    return





;**************************** Librería "LCD_MENS.INC" ********************************


    CBLOCK
    LCD_ApuntaCaracter                ; Indica la posición del carácter a visualizar
                                    ; respecto del comienzo de todos los mensajes,
                                    ; (posición de la etiqueta "Mensajes").
    LCD_ValorCaracter                ; Código ASCII del carácter a 
    ENDC                            ; visualizar.

; Los mensajes tienen que estar situados dentro de las 256 primeras posiciones de la 
; memoria de programa, es decir, no pueden superar la dirección 0FFh.

                    ; Vuelve al programa principal.

; Subrutina "LCD_MensajeMovimiento" -----------------------------------------------------
;
; Visualiza un mensaje de mayor longitud que los 16 caracteres que pueden representarse
; en una línea, por tanto se desplaza a través de la pantalla.
;
; En el mensaje debe dejarse 16 espacios en blanco, al principio y al final para
; conseguir que el desplazamiento del mensaje sea lo más legible posible.
;
    CBLOCK    
    LCD_CursorPosicion                ; Contabiliza la posición del cursor dentro de la
    ENDC                            ; pantalla LCD

LCD_MensajeMovimiento
    movwf    LCD_ApuntaCaracter        ; Posición del primer carácter del mensaje.
    movlw    Mensajes                ; Halla la posición relativa del primer carácter
    subwf    LCD_ApuntaCaracter,F    ; del mensaje respecto de la etiqueta "Mensajes".
    decf    LCD_ApuntaCaracter,F    ; Compensa la posición que ocupa "addwf PCL,F".
LCD_PrimeraPosicion
    clrf    LCD_CursorPosicion        ; El cursor en la posición 0 de la línea.
    call    LCD_Borra 

	btfss 	ARRIBA,0			;1 salta, 0 continua
	call 	LCD_Linea2
	btfsc	ARRIBA,0		;0 salta, 1 continua
	call 	LCD_Linea1

           ; Se sitúa en la primera posición de la línea 1 y
LCD_VisualizaCaracter                ; borra la pantalla.
    movlw    LCD_CaracteresPorLinea    ; ¿Ha llegado a final de línea?
    subwf    LCD_CursorPosicion,W
    btfss    STATUS,Z
    goto    LCD_NoEsFinalLinea
LCD_EsFinalLinea
    call    Retardo_200ms            ; Lo mantiene visualizado durante este tiempo.
    call    Retardo_200ms
    movlw    LCD_CaracteresPorLinea-1; Apunta a la posición del segundo carácter visualizado
    subwf    LCD_ApuntaCaracter,F    ; en pantalla, que será el primero en la siguiente
    goto    LCD_PrimeraPosicion         ; visualización de línea, para producir el efecto
LCD_NoEsFinalLinea                    ; de desplazamiento hacia la izquierda.
    movf    LCD_ApuntaCaracter,W
    call    Mensajes                ; Obtiene el ASCII del carácter apuntado.
    movwf    LCD_ValorCaracter        ; Guarda el valor de carácter.
    movf    LCD_ValorCaracter,F        ; Lo único que hace es posicionar flag Z. En caso
    btfsc    STATUS,Z                ; que sea "0x00", que es código indicador final    
    goto    LCD_FinMovimiento        ; de mensaje, sale fuera.
LCD_NoUltimoCaracter2
    call    LCD_Caracter            ; Visualiza el carácter ASCII leído.
    incf    LCD_CursorPosicion,F    ; Contabiliza el incremento de posición del 
                                   ; cursor en la pantalla.
    incf    LCD_ApuntaCaracter,F    ; Apunta a la siguiente posición por visualizar.
    goto    LCD_VisualizaCaracter    ; Vuelve a visualizar el siguiente carácter
LCD_FinMovimiento                   ; de la línea.
    return                            ; Vuelve al programa principal.
    

;**************************** Librería "RETARDOS.INC" *********************************

; ZONA DE DATOS *********************************************************************

    CBLOCK
    R_ContA                        ; Contadores para los retardos.
    R_ContB
    R_ContC
    ENDC

; RETARDOS de 20 hasta 500 microsegundos ------------------------------------------------

Retardo_200micros                ; La llamada "call" aporta 2 ciclos máquina.
    nop                            ; Aporta 1 ciclo máquina.
    movlw    d'64'                ; Aporta 1 ciclo máquina. Este es el valor de "K".
    goto    RetardoMicros        ; Aporta 2 ciclos máquina.

Retardo_50micros                ; La llamada "call" aporta 2 ciclos máquina.
    nop                            ; Aporta 1 ciclo máquina.
    movlw    d'14'                ; Aporta 1 ciclo máquina. Este es el valor de "K".
    goto    RetardoMicros        ; Aporta 2 ciclos máquina.

Retardo_20micros                ; La llamada "call" aporta 2 ciclos máquina.
    movlw    d'5'                ; Aporta 1 ciclo máquina. Este es el valor de "K".

RetardoMicros
    movwf    R_ContA                ; Aporta 1 ciclo máquina.
Rmicros_Bucle
    decfsz    R_ContA,F            ; (K-1)x1 cm (cuando no salta) + 2 cm (al saltar).
    goto    Rmicros_Bucle        ; Aporta (K-1)x2 ciclos máquina.
    return                        ; El salto del retorno aporta 2 ciclos máquina.

; RETARDOS de 1 ms hasta 200 ms. --------------------------------------------------------
;
Retardo_200ms                    ; La llamada "call" aporta 2 ciclos máquina.
    movlw    d'200'                ; Aporta 1 ciclo máquina. Este es el valor de "M".
    goto    Retardos_ms            ; Aporta 2 ciclos máquina.

Retardo_20ms                    ; La llamada "call" aporta 2 ciclos máquina.
    movlw    d'20'                ; Aporta 1 ciclo máquina. Este es el valor de "M".
    goto    Retardos_ms            ; Aporta 2 ciclos máquina.

Retardo_5ms                        ; La llamada "call" aporta 2 ciclos máquina.
    movlw    d'5'                ; Aporta 1 ciclo máquina. Este es el valor de "M".
    goto    Retardos_ms            ; Aporta 2 ciclos máquina.

Retardo_2ms                        ; La llamada "call" aporta 2 ciclos máquina.
    movlw    d'2'                ; Aporta 1 ciclo máquina. Este es el valor de "M".
    goto    Retardos_ms            ; Aporta 2 ciclos máquina.

Retardos_ms
    movwf    R_ContB                ; Aporta 1 ciclo máquina.
R1ms_BucleExterno
    movlw    d'249'                ; Aporta Mx1 ciclos máquina. Este es el valor de "K".
    movwf    R_ContA                ; Aporta Mx1 ciclos máquina.
R1ms_BucleInterno
    nop                            ; Aporta KxMx1 ciclos máquina.
    decfsz    R_ContA,F            ; (K-1)xMx1 cm (cuando no salta) + Mx2 cm (al saltar).
    goto    R1ms_BucleInterno    ; Aporta (K-1)xMx2 ciclos máquina.
    decfsz    R_ContB,F            ; (M-1)x1 cm (cuando no salta) + 2 cm (al saltar).
    goto    R1ms_BucleExterno     ; Aporta (M-1)x2 ciclos máquina.
    return                        ; El salto del retorno aporta 2 ciclos máquina.






END                                ; Fin del programa.