;
;					EJEMPLO 8
;
;				Autor: Mikel Etxebarria
;			(c) Microsystems Engineering (Bilbao)
;
;Ejemplo MicroPIC Trainer y Micro'PIC IO
;
;El TMR1 con oscilador externo. Reloj horario con indicación de hh:mm:ss
;
;Conctando al TMR1 un cristal oscilador externo, es posible realizar temporizaciones aún
;con el microcontrolador en el modo sleep de bajo consumo. En el ejemplo se propone emplear
;el cuarzo de 32.768 KHz disponible en la tarjeta Micro'PIC I/O.
;
;Esta estratégica frecuencia dividida por un divisor de 1:15 (32768) proporciona una 
;interrupción por segundo (1Hz) con precisión.
;
;El presente ejemplo propone implementar un reloj digital que, sobre la pantalla LCD de la
;tarjeta Micro'PIC Trainer, visualice las horas, minutos y segundos (hh:mm:ss)
;
;Los interruptores RA3 y RA4 permiten hacer el ajuste de los minutos y las horas respectivamente

		
		List	p=16F873	;Tipo de procesador
		include	"P16F873.INC"	;Definiciones de registros internos

Lcd_var		equ	0x20		;Inicio de variables de rutinas del LCD

Horas		equ	0x22		;Variables de las horas
Minutos		equ	0x23		;Variables de los minutos
Segundos	equ	0x24		;Variables de los segundos
Buffer_H	equ	0x25		;Variable de conversión
Buffer_L	equ	0x26		;Variable de conversión
Temporal	equ	0x27		;Variable temporal de propósito general
W_Temp		equ	0x28		;Registro temporal del W
Status_Temp	equ	0x29		;Registro temporal del STATUS
Contador	equ	0x2A		;Registro contador
Delay		equ	0x2C		;Variable de temporización

		org	0x00		;Vector de Reset
		goto	Inicio

		org	0x04		
		goto	Inter		;Vector de interrupción

		include	"LCD_Cxx.INC"	;Incluye rutinas de manejo del LCD
		
;**************************************************************************************
;BIN_BCD: Convierte el valor binario presente en el acumulador (W) en BCD. El resul-
;tado se deposita en las variables Buffer_H y Buffer_L.

BIN_BCD         clrf 	Buffer_L        ;Limpiar regs.de trabajo.
                clrf 	Buffer_H            
                                     
BIN_BCD_1       addlw 	0xf6          	;Resta 10 por suma de complemento a 2.
                btfss 	STATUS,C       	;Hay Carry?
                goto 	BIN_BCD_3       ;NO.
                movwf 	Temporal       	;SI.Guardar en reg.Temporal.
                incf 	Buffer_L,F      ;Incrementar byte bajo guardarlo y enviar...
                movf 	Buffer_L,W      ;..copia al ==> W...
                xorlw 	b'00001010'    	;..0Ah xor W(BCDL). 
                btfss 	STATUS,Z       	;Es "0" el resultado de la operación?
                goto 	BIN_BCD_2       ;NO.
                clrf 	Buffer_L        ;SI.Limpiar el byte bajo e...
                incf 	Buffer_H,F	;incrementar el alto.
BIN_BCD_2       movf 	Temporal,W	;Recuperar el dato.
                goto 	BIN_BCD_1       ;Continuar la operación.

BIN_BCD_3       addlw 	H'0A'          	;TEMPO + 0Ah
                swapf 	Buffer_L,F     	;<3:0> <==> <7:4> y ==> BCDL
                iorwf 	Buffer_L,F     	;W or BCDL ==> BCDL
                return

;Actualiza: Esta rutina incrementa los segundos en una unidad y ajusta, si es necesario, los
;minutos y las horas.

Actualiza	incf	Segundos,F	;Incrementa segundos
		movlw	.60
		subwf	Segundos,W
		btfss	STATUS,Z	;Mayor de 60 ?
		return			;No

		clrf	Segundos	;Si, segundos a 00
Act_Minutos	incf	Minutos,F	;Incrementa minutos
		movlw	.60
		subwf	Minutos,W
		btfss	STATUS,Z	;Mayor de 60 ?
		return			;No

		clrf	Minutos		;Si, minutos a 00
Act_Horas	incf	Horas,F		;Incrementa las horas
		movlw	.24
		subwf	Horas,W
		btfss	STATUS,Z	;Mayor de 24 ??
		return			;No
		clrf	Horas		;Si Horas a 00
		return

;Visualiza: Presenta en la pantalla LCD la hora actual

Visualiza	movlw	0x80
		call	LCD_REG		;Cursor Home
		movlw	Horas
		movwf	FSR		;Inicia puntero
		movlw	3
		movwf	Contador	;Contador de bytes a visualizar
Visu_2		movf	INDF,W		;Carga el byte
		call	BIN_BCD		;Lo convierte a BCD
		swapf	Buffer_L,W
		andlw	0x0f
		iorlw	0x30		;Convierte a ASCII nibble de más peso
		call	LCD_DATO	;Lo visualiza
		movf	Buffer_L,W
		andlw	0x0f
		iorlw	0x30		;Convierte a ASCII el nibble de menos peso
		call	LCD_DATO	;Lo visualiza
		incf	FSR,F		;Siguiente byte
		decfsz	Contador,F
		goto	Visu_1
		return
Visu_1		movlw	':'
		call	LCD_DATO	;Visualiza ':'
		goto	Visu_2

;Delay_250mS: Temporiza 250mS. Trabajando a una frecuencia de 1 MHz el TMR0 evoluciona cada
;1 uS. Con un preescaleer de 256, habrá que cargarle con 195 para hacer una temporización
;de 50mS. Esta se repite 5 veces para hacer un total de 250 mS (0.25 seg.)

Delay_250mS	movlw	.5
		movwf	Delay
Delay_Loop_2	movlw	~.195
		movwf	TMR0		;Inicia el temporizador
Delay_Loop_1	btfss	INTCON,2	;Fin de los 50 mS ??
		goto	Delay_Loop_1	;Todavía no
		bcf	INTCON,2	;Restaura el flag del TMR0
		decfsz	Delay		;Se ha repetido 5 veces ??
		goto	Delay_Loop_2	;Todavía no
		return			;Ahora si. Han pasado 250mS aprox.

;Programa de tratamiento de la interrupción que provoca el TMR1 cada segundo.

Inter		movwf	W_Temp
		swapf	STATUS,W
		movwf	Status_Temp	;Salva el contexto

		movlw	low ~.32765
		movwf	TMR1L
		movlw	high ~.32765		
		movwf	TMR1H		;Restaura el TMR1 con el Nº de pulsos a contar
		bcf	PIR1,0		;Restaura el frag del TMR1		

		call	Actualiza	;Actualiza la hora
		call	Visualiza	;Visualiza la hora

		swapf 	Status_Temp,W
		movwf	STATUS
		swapf	W_Temp,F
		swapf	W_Temp,W	;Recupera el contexto
		retfie		

;Programa principal

Inicio	        clrf	PORTB		;Desconecta salidas
		clrf	PORTA
		bsf	STATUS,RP0	;Selecciona banco 1
		movlw	b'00000110'
		movwf	ADCON1		;Puerta A E/S digitales
		clrf	TRISB		;Puerta B se configura como salida
		clrf	TRISA		;Puerta A como salida
		movlw	b'11000111'
		movwf	OPTION_REG	;Preescaler de 256 asociado al TMR0
		movlw	b'11111110'
		movwf	TRISC		;RC0/T1OSO salida y RC1/T1OSI entrada
		bsf	PIE1,0		;Habilita interrupción del TMR1
		bcf	STATUS,RP0	;Selecciona banco 0

		call	UP_LCD		;Configura puertos para el LCD
		call	LCD_INI		;Inicia el LCD
		movlw	b'00001100'
		call	LCD_REG		;LCD en On, cursor y blink en Off

;El TMR1 actúa con cristal oscilador externo asíncrono y con un preescaler de 1:1

		movlw	b'00001111'
		movwf	T1CON
		
		clrf	Segundos	;Puesta a 0 inicial del reloj
		clrf	Minutos
		clrf	Horas
		movlw	low ~.32768
		movwf	TMR1L
		movlw	high ~.32768
		movwf	TMR1H		;Carga el TMR1 con el Nº de pulsos a contar
		movlw	b'11000000'
		movwf	INTCON		;Habilita interrupciones

Loop		clrwdt
		btfsc	PORTA,3		;Ajuste de minutos (RA3 On) ??
		goto	A_Minutos	;Si
		btfss	PORTA,4		;Ajuste de horas (RA4 On) ??
		goto	Loop		;No

A_Horas		call	Delay_250mS	;Temporiza 0.25 segundos
		call	Act_Horas	;Actualiza las horas
		call	Visualiza	;Visualiza
		goto	Loop

A_Minutos	call	Delay_250mS	;Temporiza 0.25 segundos
		call	Act_Minutos	;Actualiza los minutos
		call	Visualiza	;Visualiza

		goto	Loop		;Bucle infinito		

		end			;Fin del programa fuente
