;**************************** Librería "LCD_4BIT.INC" ***********************************
;
; Estas subrutinas permiten realizar las tareas básicas de control de un módulo LCD de 2
; líneas por 16 caracteres, compatible con el modelo LM016L.
;
; El visualizador LCD está conectado al Puerto B del PIC mediante un bus de 4 bits. Las
; conexiones son:
; - Las 4 líneas superiores del módulo LCD, pines <DB7:DB4> se conectan a las 4
; líneas superiores del Puerto B del PIC, pines <RB7:RB4>.
; - Pin RS del LCD a la línea RA0 del PIC.
; - Pin R/W del LCD a la línea RA1 del PIC, o a masa.
; - Pin Enable del LCD a la línea RA2 del PIC.
;
; Se utilizan llamadas a subrutinas de retardo de tiempo localizadas en la librería
; RETARDOS.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
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_CursorIncr ; Cursor en modo incrementar.
movlw b'00000110'
goto LCD_EnviaComando
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_Linea3 ; Cursor al principio de la Línea 3
movlw b'10010100' ; Dirección 14h de la DDRAM
goto LCD_EnviaComando
LCD_Linea4 ; Cursor al principio de la Línea 4
movlw b'11010100' ; Dirección 54h de la DDRAM
goto LCD_EnviaComando
LCD_PosicionLinea1 ; Cursor a posición de la Línea 1, a partir de la
iorlw b'10000000' ; dirección 00h de la DDRAM más el valor del
goto LCD_EnviaComando ; registro W.
LCD_PosicionLinea2 ; Cursor a posición de la Línea 2, a partir de la
iorlw b'11000000' ; dirección 40h de la DDRAM más el valor del
goto LCD_EnviaComando ; registro W.
LCD_OFF ; Pantalla apagada.
movlw b'00001000'
goto LCD_EnviaComando
LCD_CursorON ; Pantalla encendida y cursor encendido.
movlw b'00001110'
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
; Subrutina "LCD_DosEspaciosBlancos" y "LCD_LineaBlanco" --------------------------------
;
; Visualiza espacios en blanco.
LCD_LineaEnBlanco
movlw LCD_CaracteresPorLinea
goto LCD_EnviaBlancos
LCD_UnEspacioBlanco
movlw .1
goto LCD_EnviaBlancos
LCD_DosEspaciosBlancos
movlw .2
goto LCD_EnviaBlancos
LCD_TresEspaciosBlancos
movlw .3
LCD_EnviaBlancos
movwf LCD_Auxiliar1 ; (LCD_Auxiliar1) se utiliza como contador.
LCD_EnviaOtroBlanco
movlw ' ' ; Esto es un espacio en blanco.
call LCD_Caracter ; Visualiza tanto espacios en blanco como se
decfsz LCD_Auxiliar1,F ; haya cargado en (LCD_Auxiliar1).
goto LCD_EnviaOtroBlanco
return
; Subrutinas "LCD_ByteCompleto" y "LCD_Byte" --------------------------------------------
;
; Subrutina "LCD_ByteCompleto", visualiza el byte que almacena el registro W en el
; lugar actual de la pantalla. Por ejemplo, si (W)=b'10101110' visualiza "AE".
;
; Subrutina "LCD_Byte" igual que la anterior, pero en caso de que el nibble alto sea cero
; visualiza en su lugar un espacio en blanco. Por ejemplo si (W)=b'10101110' visualiza "AE"
; y si (W)=b'00001110', visualiza " E" (un espacio blanco delante).
;
; Utilizan la subrutina "LCD_Nibble" que se analiza más adelante.
;
LCD_Byte
movwf LCD_Auxiliar2 ; 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_VisualizaAlto ; No es cero y lo visualiza.
movlw ' ' ; Visualiza un espacio en blanco.
call LCD_Caracter
goto LCD_VisualizaBajo
LCD_ByteCompleto
movwf LCD_Auxiliar2 ; Guarda el valor de entrada.
LCD_VisualizaAlto
swapf LCD_Auxiliar2,W ; Pone el nibble alto en la parte baja.
call LCD_Nibble ; Lo visualiza.
LCD_VisualizaBajo
movf LCD_Auxiliar2,W ; Repite el proceso con el nibble bajo.
; call LCD_Nibble ; Lo visualiza.
; return
; Subrutina "LCD_Nibble" ----------------------------------------------------------------
;
; Visualiza en el lugar actual de la pantalla, el valor hexadecimal que almacena en el nibble
; bajo del registro W. El nibble alto de W no es tenido en cuenta. Ejemplos:
; - Si (W)=b'01010110', se visualizará "6".
; - Si (W)=b'10101110', se visualizará "E".
;
LCD_Nibble
andlw b'00001111' ; Se queda con la parte baja.
movwf LCD_Auxiliar1 ; Lo guarda.
sublw 0x09 ; Comprueba si hay que representarlo con letra.
btfss STATUS,C
goto LCD_EnviaByteLetra
movf LCD_Auxiliar1,W
addlw '0' ; El número se pasa a carácter ASCII sumándole
goto LCD_FinVisualizaDigito ; el ASCII del cero y lo visualiza.
LCD_EnviaByteLetra
movf LCD_Auxiliar1,W
addlw 'A'-0x0A ; Sí, por tanto, se le suma el ASCII de la 'A'.
LCD_FinVisualizaDigito
goto LCD_Caracter ; Y visualiza el carácter. Se hace con un "goto"
; para no sobrecargar la pila.
;**************************** Librería "RETARDOS.INC" *********************************
;
; Librería con múltiples subrutinas de retardos, desde 4 microsegundos hasta 20 segundos.
; Además se pueden implementar otras subrutinas muy fácilmente.
;
; Se han calculado para un sistema microcontrolador con un PIC trabajando con un cristal
; de cuarzo a 4 MHz. Como cada ciclo máquina son 4 ciclos de reloj, resulta que cada
; ciclo máquina tarda 4 x 1/4MHz = 1 µs.
;
; En los comentarios, "cm" significa "ciclos máquina".
;
; ZONA DE DATOS *********************************************************************
CBLOCK
R_ContA ; Contadores para los retardos.
R_ContB
R_ContC
ENDC
;
; RETARDOS de 4 hasta 10 microsegundos ---------------------------------------------------
;
; A continuación retardos pequeños teniendo en cuenta que para una frecuencia de 4 MHZ,
; la llamada a subrutina "call" tarda 2 ciclos máquina, el retorno de subrutina
; "return" toma otros 2 ciclos máquina y cada instrucción "nop" tarda 1 ciclo máquina.
;
Retardo_10micros ; La llamada "call" aporta 2 ciclos máquina.
nop ; Aporta 1 ciclo máquina.
nop ; Aporta 1 ciclo máquina.
nop ; Aporta 1 ciclo máquina.
nop ; Aporta 1 ciclo máquina.
nop ; Aporta 1 ciclo máquina.
Retardo_5micros ; La llamada "call" aporta 2 ciclos máquina.
nop ; Aporta 1 ciclo máquina.
Retardo_4micros ; La llamada "call" aporta 2 ciclos máquina.
return ; El salto del retorno aporta 2 ciclos máquina.
;
; RETARDOS de 20 hasta 500 microsegundos ------------------------------------------------
;
Retardo_500micros ; La llamada "call" aporta 2 ciclos máquina.
nop ; Aporta 1 ciclo máquina.
movlw d'164' ; Aporta 1 ciclo máquina. Este es el valor de "K".
goto RetardoMicros ; Aporta 2 ciclos máquina.
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_100micros ; La llamada "call" aporta 2 ciclos máquina.
movlw d'31' ; 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".
;
; El próximo bloque "RetardoMicros" tarda:
; 1 + (K-1) + 2 + (K-1)x2 + 2 = (2 + 3K) ciclos máquina.
;
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.
;
;En total estas subrutinas tardan:
; - Retardo_500micros: 2 + 1 + 1 + 2 + (2 + 3K) = 500 cm = 500 µs. (para K=164 y 4 MHz).
; - Retardo_200micros: 2 + 1 + 1 + 2 + (2 + 3K) = 200 cm = 200 µs. (para K= 64 y 4 MHz).
; - Retardo_100micros: 2 + 1 + 2 + (2 + 3K) = 100 cm = 100 µs. (para K= 31 y 4 MHz).
; - Retardo_50micros : 2 + 1 + 1 + 2 + (2 + 3K) = 50 cm = 50 µs. (para K= 14 y 4 MHz).
; - Retardo_20micros : 2 + 1 + (2 + 3K) = 20 cm = 20 µs. (para K= 5 y 4 MHz).
;
; 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_100ms ; La llamada "call" aporta 2 ciclos máquina.
movlw d'100' ; Aporta 1 ciclo máquina. Este es el valor de "M".
goto Retardos_ms ; Aporta 2 ciclos máquina.
Retardo_50ms ; La llamada "call" aporta 2 ciclos máquina.
movlw d'50' ; 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_10ms ; La llamada "call" aporta 2 ciclos máquina.
movlw d'10' ; 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.
Retardo_1ms ; La llamada "call" aporta 2 ciclos máquina.
movlw d'1' ; Aporta 1 ciclo máquina. Este es el valor de "M".
;
; El próximo bloque "Retardos_ms" tarda:
; 1 + M + M + KxM + (K-1)xM + Mx2 + (K-1)Mx2 + (M-1) + 2 + (M-1)x2 + 2 =
; = (2 + 4M + 4KM) ciclos máquina. Para K=249 y M=1 supone 1002 ciclos máquina
; que a 4 MHz son 1002 µs = 1 ms.
;
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.
;
;En total estas subrutinas tardan:
; - Retardo_200ms: 2 + 1 + 2 + (2 + 4M + 4KM) = 200007 cm = 200 ms. (M=200 y K=249).
; - Retardo_100ms: 2 + 1 + 2 + (2 + 4M + 4KM) = 100007 cm = 100 ms. (M=100 y K=249).
; - Retardo_50ms : 2 + 1 + 2 + (2 + 4M + 4KM) = 50007 cm = 50 ms. (M= 50 y K=249).
; - Retardo_20ms : 2 + 1 + 2 + (2 + 4M + 4KM) = 20007 cm = 20 ms. (M= 20 y K=249).
; - Retardo_10ms : 2 + 1 + 2 + (2 + 4M + 4KM) = 10007 cm = 10 ms. (M= 10 y K=249).
; - Retardo_5ms : 2 + 1 + 2 + (2 + 4M + 4KM) = 5007 cm = 5 ms. (M= 5 y K=249).
; - Retardo_2ms : 2 + 1 + 2 + (2 + 4M + 4KM) = 2007 cm = 2 ms. (M= 2 y K=249).
; - Retardo_1ms : 2 + 1 + (2 + 4M + 4KM) = 1005 cm = 1 ms. (M= 1 y K=249).
;
; RETARDOS de 0.5 hasta 20 segundos ---------------------------------------------------
;
Retardo_20s ; La llamada "call" aporta 2 ciclos máquina.
movlw d'200' ; Aporta 1 ciclo máquina. Este es el valor de "N".
goto Retardo_1Decima ; Aporta 2 ciclos máquina.
Retardo_10s ; La llamada "call" aporta 2 ciclos máquina.
movlw d'100' ; Aporta 1 ciclo máquina. Este es el valor de "N".
goto Retardo_1Decima ; Aporta 2 ciclos máquina.
Retardo_5s ; La llamada "call" aporta 2 ciclos máquina.
movlw d'50' ; Aporta 1 ciclo máquina. Este es el valor de "N".
goto Retardo_1Decima ; Aporta 2 ciclos máquina.
Retardo_2s ; La llamada "call" aporta 2 ciclos máquina.
movlw d'20' ; Aporta 1 ciclo máquina. Este es el valor de "N".
goto Retardo_1Decima ; Aporta 2 ciclos máquina.
Retardo_1s ; La llamada "call" aporta 2 ciclos máquina.
movlw d'10' ; Aporta 1 ciclo máquina. Este es el valor de "N".
goto Retardo_1Decima ; Aporta 2 ciclos máquina.
Retardo_500ms ; La llamada "call" aporta 2 ciclos máquina.
movlw d'5' ; Aporta 1 ciclo máquina. Este es el valor de "N".
;
; El próximo bloque "Retardo_1Decima" tarda:
; 1 + N + N + MxN + MxN + KxMxN + (K-1)xMxN + MxNx2 + (K-1)xMxNx2 +
; + (M-1)xN + Nx2 + (M-1)xNx2 + (N-1) + 2 + (N-1)x2 + 2 =
; = (2 + 4M + 4MN + 4KM) ciclos máquina. Para K=249, M=100 y N=1 supone 100011
; ciclos máquina que a 4 MHz son 100011 µs = 100 ms = 0,1 s = 1 décima de segundo.
;
Retardo_1Decima
movwf R_ContC ; Aporta 1 ciclo máquina.
R1Decima_BucleExterno2
movlw d'100' ; Aporta Nx1 ciclos máquina. Este es el valor de "M".
movwf R_ContB ; Aporta Nx1 ciclos máquina.
R1Decima_BucleExterno
movlw d'249' ; Aporta MxNx1 ciclos máquina. Este es el valor de "K".
movwf R_ContA ; Aporta MxNx1 ciclos máquina.
R1Decima_BucleInterno
nop ; Aporta KxMxNx1 ciclos máquina.
decfsz R_ContA,F ; (K-1)xMxNx1 cm (si no salta) + MxNx2 cm (al saltar).
goto R1Decima_BucleInterno ; Aporta (K-1)xMxNx2 ciclos máquina.
decfsz R_ContB,F ; (M-1)xNx1 cm (cuando no salta) + Nx2 cm (al saltar).
goto R1Decima_BucleExterno ; Aporta (M-1)xNx2 ciclos máquina.
decfsz R_ContC,F ; (N-1)x1 cm (cuando no salta) + 2 cm (al saltar).
goto R1Decima_BucleExterno2 ; Aporta (N-1)x2 ciclos máquina.
return ; El salto del retorno aporta 2 ciclos máquina.
;
;En total estas subrutinas tardan:
; - Retardo_20s: 2 + 1 + 2 + (2 + 4N + 4MN + 4KMN) = 20000807 cm = 20 s.
; (N=200, M=100 y K=249).
; - Retardo_10s: 2 + 1 + 2 + (2 + 4N + 4MN + 4KMN) = 10000407 cm = 10 s.
; (N=100, M=100 y K=249).
; - Retardo_5s: 2 + 1 + 2 + (2 + 4N + 4MN + 4KMN) = 5000207 cm = 5 s.
; (N= 50, M=100 y K=249).
; - Retardo_2s: 2 + 1 + 2 + (2 + 4N + 4MN + 4KMN) = 2000087 cm = 2 s.
; (N= 20, M=100 y K=249).
; - Retardo_1s: 2 + 1 + 2 + (2 + 4N + 4MN + 4KMN) = 1000047 cm = 1 s.
; (N= 10, M=100 y K=249).
; - Retardo_500ms: 2 + 1 + (2 + 4N + 4MN + 4KMN) = 500025 cm = 0,5 s.
; (N= 5, M=100 y K=249).
;**************************** Librería "LCD_8BIT.INC" ***********************************
;
; Estas subrutinas permiten realizar las tareas básicas de control de un módulo LCD de 2
; líneas por 16 caracteres, compatible con el modelo LM016L.
;
; El visualizador LCD está conectado al Puerto B del PIC mediante un bus de 8 bits. Las
; conexiones son:
; - Las 8 líneas de datos del módulo LCD, pines <DB7:DB0> se conectan al Puerto B.
; - Pin RS del LCD a la línea RA0 del PIC.
; - Pin R/W del LCD a la línea RA1 del PIC, o a masa.
; - Pin Enable del LCD a la línea RA2 del PIC.
;
; Se utilizan llamadas a subrutinas de retardo de tiempo localizadas en la librería
; RETARDOS.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_200ms
movlw b'00111000'
call LCD_Envia ; Escribe el dato en el LCD.
call Retardo_5ms
movlw b'00110000'
call LCD_Envia
call Retardo_200micros
movlw b'00110000'
call LCD_Envia
call Retardo_200micros ; Este retardo es necesario para simular en PROTEUS.
movlw b'00110000' ; Interface de 8 bits.
call LCD_Envia
call Retardo_20micros ; Este retardo es necesario para simular en PROTEUS.
; Ahora configura el resto de los parámetros:
call LCD_2Lineas8Bits5x7 ; LCD de 2 líneas y caracteres de 5x7 puntos, bus de 8.
call LCD_Borra ; Pantalla encendida y limpia. Cursor al principio
call LCD_CursorOFF ; de la línea 1. Cursor apagado.
call LCD_CursorIncr ; Cursor en modo incrementar.
return
; Subrutinas variadas para el control del módulo LCD -----------------------------------------
;
;Los comandos que pueden ser ejecutados son:
;
LCD_CursorIncr ; Cursor en modo incrementar.
movlw b'00000110'
goto LCD_EnviaComando
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_Linea3 ; Cursor al principio de la Línea 3
movlw b'10010100' ; Dirección 14h de la DDRAM
goto LCD_EnviaComando
LCD_Linea4 ; Cursor al principio de la Línea 4
movlw b'11010100' ; Dirección 54h de la DDRAM
goto LCD_EnviaComando
LCD_PosicionLinea1 ; Cursor a posición de la Línea 1, a partir de la
iorlw b'10000000' ; dirección 00h de la DDRAM más el valor del
goto LCD_EnviaComando ; registro W.
LCD_PosicionLinea2 ; Cursor a posición de la Línea 2, a partir de la
iorlw b'11000000' ; dirección 40h de la DDRAM más el valor del
goto LCD_EnviaComando ; registro W.
LCD_OFF ; Pantalla apagada.
movlw b'00001000'
goto LCD_EnviaComando
LCD_CursorON ; Pantalla encendida y cursor encendido.
movlw b'00001110'
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_2Lineas8Bits5x7 ; Define la pantalla de 2 líneas, con caracteres
movlw b'00111000' ; de 5x7 puntos y conexión al PIC mediante bus de
; goto LCD_EnviaComando ; 8 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.
bsf STATUS,RP0 ; Acceso al Banco 1.
movf TRISB,W ; Guarda la configuración que tenía antes TRISB.
movwf LCD_GuardaTRISB
clrf TRISB ; Puerto B como salida.
bcf STATUS,RP0 ; Acceso al Banco 0.
;
movf LCD_GuardaDato,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
nop
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.
btfss LCD_PinRS ; Debe garantizar una correcta escritura manteniendo
call Retardo_20ms ; 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
; Subrutina "LCD_DosEspaciosBlancos" y "LCD_LineaBlanco" --------------------------------
;
; Visualiza espacios en blanco.
LCD_LineaEnBlanco
movlw LCD_CaracteresPorLinea
goto LCD_EnviaBlancos
LCD_UnEspacioBlanco
movlw .1
goto LCD_EnviaBlancos
LCD_DosEspaciosBlancos
movlw .2
goto LCD_EnviaBlancos
LCD_TresEspaciosBlancos
movlw .3
LCD_EnviaBlancos
movwf LCD_Auxiliar1 ; (LCD_Auxiliar1) se utiliza como contador.
LCD_EnviaOtroBlanco
movlw ' ' ; Esto es un espacio en blanco.
call LCD_Caracter ; Visualiza tanto espacios en blanco como se
decfsz LCD_Auxiliar1,F ; haya cargado en (LCD_Auxiliar1).
goto LCD_EnviaOtroBlanco
return
; Subrutinas "LCD_ByteCompleto" y "LCD_Byte" --------------------------------------------
;
; Subrutina "LCD_ByteCompleto", visualiza el byte que almacena el registro W en el
; lugar actual de la pantalla. Por ejemplo, si (W)=b'10101110' visualiza "AE".
;
; Subrutina "LCD_Byte" igual que la anterior, pero en caso de que el nibble alto sea cero
; visualiza en su lugar un espacio en blanco. Por ejemplo si (W)=b'10101110' visualiza "AE"
; y si (W)=b'00001110', visualiza " E" (un espacio blanco delante).
;
; Utilizan la subrutina "LCD_Nibble" que se analiza más adelante.
;
LCD_Byte
movwf LCD_Auxiliar2 ; 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_VisualizaAlto ; No es cero y lo visualiza.
movlw ' ' ; Visualiza un espacio en blanco.
call LCD_Caracter
goto LCD_VisualizaBajo
LCD_ByteCompleto
movwf LCD_Auxiliar2 ; Guarda el valor de entrada.
LCD_VisualizaAlto
swapf LCD_Auxiliar2,W ; Pone el nibble alto en la parte baja.
call LCD_Nibble ; Lo visualiza.
LCD_VisualizaBajo
movf LCD_Auxiliar2,W ; Repite el proceso con el nibble bajo.
; call LCD_Nibble ; Lo visualiza.
; return
; Subrutina "LCD_Nibble" ----------------------------------------------------------------
;
; Visualiza en el lugar actual de la pantalla, el valor hexadecimal que almacena en el nibble
; bajo del registro W. El nibble alto de W no es tenido en cuenta. Ejemplos:
; - Si (W)=b'01010110', se visualizará "6".
; - Si (W)=b'10101110', se visualizará "E".
;
LCD_Nibble
andlw b'00001111' ; Se queda con la parte baja.
movwf LCD_Auxiliar1 ; Lo guarda.
sublw 0x09 ; Comprueba si hay que representarlo con letra.
btfss STATUS,C
goto LCD_EnviaByteLetra
movf LCD_Auxiliar1,W
addlw '0' ; El número se pasa a carácter ASCII sumándole
goto LCD_FinVisualizaDigito ; el ASCII del cero y lo visualiza.
LCD_EnviaByteLetra
movf LCD_Auxiliar1,W
addlw 'A'-0x0A ; Sí, por tanto, se le suma el ASCII de la 'A'.
LCD_FinVisualizaDigito
goto LCD_Caracter ; Y visualiza el carácter. Se hace con un "goto"
; para no sobrecargar la pila.
;**************************** Librería "LCD_MENS.INC" ********************************
;
; Librería de subrutinas para el manejo de mensajes a visualizar en un visualizador LCD.
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.
; Subrutina "LCD_Mensaje" ---------------------------------------------------------------
;
; Visualiza por pantalla el mensaje apuntado por el registro W.
;
; Los mensajes deben localizarse dentro de una zona encabezada por la etiqueta "Mensajes" y que
; tenga la siguiente estructura:
;
; Mensajes ; ¡Etiqueta obligatoria!
; addwf PCL,F
; Mensaje0 ; Posición inicial del mensaje.
; DT ".. ..", 0x00 ; Mensaje terminado en 0x00.
; Mensaje1
; ...
; ...
; FinMensajes
;
; La llamada a esta subrutina se realizará siguiendo este ejemplo:
;
; movlw Mensaje0 ; Carga la posición del mensaje.
; call LCD_Mensaje ; Visualiza el mensaje.
;
LCD_Mensaje
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 etiqueta "Mensajes".
decf LCD_ApuntaCaracter,F ; Compensa la posición que ocupa "addwf PCL,F".
LCD_VisualizaOtroCaracter
movf LCD_ApuntaCaracter,W
call Mensajes ; Obtiene el código 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_FinMensaje ; de mensaje, sale fuera.
LCD_NoUltimoCaracter
call LCD_Caracter ; Visualiza el carácter ASCII leído.
incf LCD_ApuntaCaracter,F ; Apunta a la posición del siguiente carácter
goto LCD_VisualizaOtroCaracter ; dentro del mensaje.
LCD_FinMensaje
return ; 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 ; 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.