# Librería para LCD y PIC16F877A



## ina85 (May 24, 2009)

buenas tardes...escribo ya que es primera vez que considero trabajar con librerías y tengo algunas dudas ...

debo trabajar con el pic 16f877a, una pantallada lcd y un teclado maticial...

el teclado es de 4x7 y pienso colocarlo entre el puerto b y el c...la pantalla pienso colocarla en el d y usar solo 4 bits en vez de los 8...

 he estado investigando y la librería  LCD_EASY.INC me permite trabajar con el puerto d como yo deseo... y la librería  LCD_MENS.INC me permite mostrar mensajes y desplazarlos que tambien debo hacerlo..

mi duda es...puedo utilizar las 2 librerias? la libreria  LCD_MENS.INC no trabaja con otro puerto distinto al d?

no encuentro en ningun lado el codigo de la libreria LCD_EASY.INC para comprobar que trabaja realmente con el d...

agradezco sus respuestas y su confirmación de que he encontrado información correcta ya que es primera vez q trabajo con librerias y me esta pareciendo medio enrrollado...


gracias y saludos!


----------



## ina85 (May 24, 2009)

ya resolvi esa duda... y encontre la libreria LCD.easy.inc...


ahora tengo otra duda...estoy usando la libreria lcd_mens.inc....en la subrutina "mensajes  en movimiento" no logro escribir en la segunda linea de la pantalla...



con el mensaje sin moverse si puedo....pero moviendose no lo hace...lo q hace es escribir en la primera linea....creo q es por el cblock que debo darle un nuevo valor pero de la forma en q lo he hecho no funciona....


ayudenme porfavor!


----------



## Chico3001 (May 25, 2009)

sube tu programa y posiblemente asi alguien te pueda ayudar


----------



## ina85 (May 25, 2009)

Mi código es el siguiente, en la primer linea muestra un mensaje superior a 16 caracteres por lo que se desplaza hasta leerse completamente (utilizo subrutina LCD_MensajeMovimiento), en la segunda línea muestro un mensaje inferior a 16 caracteres por lo que se muestra sin problema....


list p=16f877a
INCLUDE  <P16F877A.INC>

CBLOCK  0x20
ENDC

org 00
goto inicio
inicio org 05

	call LCD_Inicializa

	movlw Mensaje0
	call LCD_MensajeMovimiento

CALL LCD_Linea2

movlw Mensaje1
call LCD_Mensaje

a call LCD_CursorOFF
GOTO a

Mensajes
		addwf PCL,F
Mensaje0
DT "Hola Foro electronica", 0X00

Mensaje1
DT "Buen dia", 0x00




INCLUDE <LCD_MENS.INC>
INCLUDE <LCD_EASY.INC>
INCLUDE <RETARDOS.INC>

end


----------



## ina85 (May 25, 2009)

El problema está cuando deseo mostrar en ambas líneas mensakes superior a 16 caracteres utilizando la subrutina LCD_MensajeMovimiento....muestra el primer mensaje en la primera línea y se mueve pero cuando debe mostrar el segundo mensaje en la segunda línea, lo muestra en la primera...

list p=16f877a
INCLUDE  <P16F877A.INC>

CBLOCK  0x20
ENDC

org 00
goto inicio
inicio org 05

	call LCD_Inicializa

	movlw Mensaje0
	call LCD_MensajeMovimiento

CALL LCD_Linea2

movlw Mensaje1
call LCD_MensajeMovimiento

a call LCD_CursorOFF
GOTO a

Mensajes
		addwf PCL,F
Mensaje0
DT "Hola Foro electronica", 0X00

Mensaje1
DT "Buen dia foro electronica", 0x00

INCLUDE <LCD_MENS.INC>
INCLUDE <LCD_EASY.INC>
INCLUDE <RETARDOS.INC>

end




agradezco su ayuda porfavor!


----------



## andy1 (Jul 10, 2009)

hola ya pude prender unos leds y algunos efectos , 
ahora estoy tratando de poner un mnj en lcd , alguien me podria colaborar con esto "8 bits"
gracias por su colaboracion


----------



## mecatrodatos (Jul 11, 2009)

que tal te anexo documento para que aclares dudas manejo de lcd con asembler


----------



## andy1 (Jul 13, 2009)

hola mercatrodatos que pena la ignorancia pero estoy confundido, esas librerias que estan en el pdf se las escribe en el progama?
por que me sale error, pero cunado pongo end  al fin de todo ya no sale , pero sale error con esta libreria 

	include "LCD_LIB_CAPC_877_F.lib"
esta libreria donde la consigo?

q debo hacer con las otras librerias las dejo en el progama?
gracias por tu colaboracion 
att : andy


----------



## mecatrodatos (Jul 13, 2009)

Que tal amigo andy cuando creas un proyecto utilizando asembler en el software de mplab tenes que incluir las librerias para que cuando termines el programa y compiles no se genere ningun error.

te dejo un ejemplo basico todo comentado

LCD_01.asm 

```
title " Visualización de un mensaje en el LCD carácter a carácter"
;************************************** LCD_01.asm ********************************
; El modulo LCD visualiza el mensaje "Hola".
; ********************************************************************************
;Programa para PIC 16F877A.
;Velocidad del Reloj:4 MHz.
;Reloj instrucción: 250 KHz = 4 uS.
;Perro Guardián deshabilitado.
;Tipo de Reloj XT.
;Protección de Código: OFF.
;************************************** Elegimos PIC ******************************
list p=16f877A, f=inhx32
;******************* Asignación de nombres de Registros de Funciones especiales a direcciones *********
#include <p16f877A.inc> ;Este fichero contiene los nombres y direcciones de los
; registros de funciones especiales.
; Este fichero esta localizado en el directorio
; con el nombre MPASM.
;************************************** Igualdades *************************************
;************************************** Registros ************************************
CBLOCK 0x20
ENDC
;************************************* Sección Código de Reset ***************************
ORG 0 ;Dirección del Vector Reset
GOTO COMIENZO ;Comienzo del Programa
;*********************************** Sección de Configuración ******************************
ORG 5
COMIENZO clrf PORTA ; Reseteamos el PORTA.
bsf STATUS,RP0 ; Ir al Banco 1.
movlw 0x06 ; Poner el PORTA como señales digitales.
movwf ADCON1
bcf STATUS,RP0 ; Ir al Banco 0.
call LCD_Inicializa ; Inicializar el LCD.
;************************************* Principal ***********************************
PRINCIPAL movlw 'H' ; Escribimos el carácter en el donde apunta
call LCD_Caracter ; el Cursor del LCD e incrementa el cursor.
movlw 'o'
call LCD_Caracter
movlw 'l'
call LCD_Caracter
movlw 'a'
call LCD_Caracter
sleep ; Entra en modo de bajo consumo.
;*********************************************** Librerías ************************
INCLUDE <LCD_4BIT.INC> ; Subrutinas de control del módulo LCD.
INCLUDE <RETARDOS.INC> ; Subrutinas de retardo.
END ; Fin del programa.
```

lo anterior es el codigo te anexo librerias de lcd y retardos cuando ejecutes el programa incluyes las librerias que necesitas .
espero te sirva, si tienes dudas no se te olvide comentar.


----------



## mecatrodatos (Jul 13, 2009)

```
;**************************** 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.
```


----------



## andy1 (Jul 14, 2009)

hola compadre gracias por la colaboracion , le comento soy super renuevooo en esto de la progamacion como comente logre prender algunos led,
bueno el caso es que no entendi muy bien lo de la libreria que me aparece el error, esa ya esta en el mplab  o se la debe descargar de algun lado ? 
gracias por el progama pero no lo puedo leer , y por lo tanto no lo puedo probar, podras ponerlo en pdf  para poderlo descargar...
viejo que pena la intensidad pero creo que mejor es preguntar que quedarse con la duda y nueva/ gracias por tu colaboracion


----------



## nishotv (Jul 15, 2009)

ina85 dijo:
			
		

> El problema está cuando deseo mostrar en ambas líneas mensakes superior a 16 caracteres utilizando la subrutina LCD_MensajeMovimiento....muestra el primer mensaje en la primera línea y se mueve pero cuando debe mostrar el segundo mensaje en la segunda línea, lo muestra en la primera...
> 
> list p=16f877a
> INCLUDE  <P16F877A.INC>
> ...


----------



## danielpardo (Oct 7, 2010)

ina85 dijo:


> ya resolvi esa duda... y encontre la libreria LCD.easy.inc...
> 
> 
> ahora tengo otra duda...estoy usando la libreria lcd_mens.inc....en la subrutina "mensajes en movimiento" no logro escribir en la segunda linea de la pantalla...
> ...


 

oye me pocrias pasar esa libreria o por lo menos decirme de donde la puedo descargar, tambien la necesito

se lo agradeceria de corazon


----------



## 7milazzo (Nov 26, 2012)

Buenas, soy estudiante de electronica y estoy buscando el .inc  de las librerias LCD y no las e encontrado si alguno de ustedes que ya las tienen las pudiera subir, estarían salvando una vida =) ajjajaja  en realidad las necesito y no las e encontrado =/


----------



## voltio (Feb 22, 2016)

Hola a todos, disculpen pero tengo un problema con estas rutinas y no logro hacer funcionar mi pic.
usado la rutina del lcd del libro de RAMA y los delays no puedo ver el mensaje en el display usando un 16F877A
subo los archivos a ver si alguien me ayuda.gracias


----------



## D@rkbytes (Feb 22, 2016)

En ese programa no estás escribiendo nada en la pantalla, sólo la estás inicializando.


----------



## cisneros626 (Jun 19, 2020)

Tengo un problema con mi programa.
Estoy simulando en Proteus un programa muy sencillo en el cual debo imprimir "Hola" (Apenas estoy aprendiendo a usar la LCD), estoy usando la librería "LCD_4BIT.IN" pero al momento de correro la simulación no me aparece nada en la pantalla del LCD.

Estoy usando un PIC16f877A.

```
;ZONA DE DATOS
    LIST P=16F877A
    #INCLUDE<P16F877A.INC>
    __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC
    CBLOCK  0x0C
    ENDC

;ZONA DE CÓDIGOS
    ORG        0x00

Inicio
    call    LCD_Inicializa
    movlw   'H'
    call    LCD_Caracter
    movlw   'o'
    call    LCD_Caracter
    movlw   'l'
    call    LCD_Caracter
    movlw   'a'
    call    LCD_Caracter
    movlw   'M'
    call    LCD_Caracter
    movlw   'u'
    call    LCD_Caracter
    movlw   'n'
    call    LCD_Caracter
    movlw   'd'
    call    LCD_Caracter
    movlw   'o'
    call    LCD_Caracter
    sleep

    INCLUDE <LCD_4BIT.INC>
    INCLUDE <RETARDOS.INC>
    END
```


----------



## D@rkbytes (Jun 20, 2020)

En el PIC16F877A las cosas cambian.
La dirección de RAM para las variables debe estar en 0x20, no en 0x0C
Este PIC tiene conversor AD en el puerto A y el E, si no se va a usar se debe deshabilitar.
¿Cómo? Ver el registro ADCON1 en la hoja de datos.

Por lo tanto, para que ese programa funcione se debe agregar lo siguiente:

```
CBLOCK  0x20
    ENDC

;ZONA DE CÓDIGOS
    ORG        0x00

Inicio
    bsf      STATUS, RP0       ; Seleccionar el Banco 1
    movlw    b'00000111'       ; Puertos A y E en modo digital.
    movwf    ADCON1
    ; Continuación del programa
```


----------



## cisneros626 (Jun 20, 2020)

Perfecto, muchas gracias.
Pero también debo seleccionar el banco 0 antes seguir con el programa no?


----------



## D@rkbytes (Jun 20, 2020)

cisneros626 dijo:


> ¿Pero también debo seleccionar el banco 0 antes seguir con el programa, no?


Si te fijas, la librería lo hará nuevamente cuando se llama a "LCD_Inicializa"


----------



## cisneros626 (Jun 20, 2020)

D@rkbytes dijo:


> Si te fijas, la librería lo hará nuevamente cuando se llama a "LCD_Inicializa"


Tienes toda la razón, Muchas gracias.
Bonito día


----------

