; CLK[cubo]
; por Adrian Bulnes [Urriellu]
; clk3 @ urriellu . net


; Aviso: todos los comentarios estan escritos sin tildes porque MPLAB solo soporta archivos en ASCII (y en teoria tambien UTF16, pero no esta bien implementado y su uso es inutil, culpa de Microchip ;-)

 	LIST P=16F877A, R=hex, W=-302, W=-208	;elegimos modelo de PIC, raiz hexadecimal, eliminamos avisos 302 (registros en bancos !=0) y 208 (etiquetas demasiado largas)
	INCLUDE P16F877A.INC
	__CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _HS_OSC & _LVP_OFF & _DEBUG_OFF & _WRT_OFF & _CPD_OFF & _BODEN_ON ;mirar el archivo de cabecera. Brown-Out Reset activado para que ante caidas de tension no siga funcionando y que se consideren algunos botones como pulsados

;aliases para pines
PuertoAlarma				EQU PORTB
BitAlarma					EQU 1
PuertoPulsadores			EQU PORTB
BitPulsadorHm1			EQU 4	;pulsador que aumenta una hora
BitPulsadorMm1			EQU 5	;pulsador que aumento un minuto
BitPulsadorHora			EQU 6	;pulsador para configurar la hora
BitPulsadorAlarma			EQU 7	;pulsador para configurar la hora de la alarma
PuertoHoras				EQU PORTA
PuertoMinutos				EQU PORTC
PuertoSegundos			EQU PORTD

;variables GPR
HORA_REAL_S		EQU 20
HORA_REAL_M		EQU 21
HORA_REAL_H		EQU 22
HORA_ALARMA_S	EQU 23	;realmente siempre estara a b'00000000' pero es mas comodo a la hora de programar si existe esta variable
HORA_ALARMA_M	EQU 24
HORA_ALARMA_H	EQU 25
BitsSueltos		EQU 26	;un byte en el que se usaran bits sueltos para configurar distintas cosas
BitRecienReseteado	EQU 0
BitAlarmaActivada	EQU 1		;activado si la alarma esta activada
EstamosProcesandoMinutos	EQU 2
ContadorH		EQU 3	;Contador deben ser 9 bits, asi que usamos 8 de ContadorL y 1 de BitsSueltos
W_TEMP			EQU 27
STATUS_TEMP		EQU 28
ContadorL		EQU 29
MomentoFinRebote	EQU 2A



	ORG 0x00
	GOTO CONFIGURACION

 	ORG 0x04
 	GOTO INTERRUPCIONES


	ORG 0x05

CONFIGURACION
;=== CONFIGURACION ===
	CLRF PORTA	;necesario segun especificaciones de Microchip
	CLRF PORTB
	CLRF PORTC
	CLRF PORTD
	CLRF PORTE
	BSF STATUS,RP0
	MOVLW b'00000111'
	MOVWF ADCON1		;configuramos PORTA como E/S digitales
	;configuramos RB4:7 como entradas, todos los demas como salidas. Los pines no usados se configuran como salidas para poder dejar al aire
	CLRF TRISA
	MOVLW b'11110000'
	MOVWF TRISB
	CLRF TRISC
	CLRF TRISD
	CLRF TRISE
	
	MOVLW b'00000101'	;el primer bit esta puesto a cero para activar las pull-up de RB4:7
	MOVWF OPTION_REG

	MOVLW b'10100000'
	MOVWF INTCON		; activamos interrupciones para desbordamiento de TMR0. (INTCON es accesible desde todos los bancos)
	
	BCF STATUS,RP0

	CLRF BitsSueltos
	CLRF ContadorL
	CLRF HORA_REAL_S
	CLRF HORA_REAL_M
	CLRF HORA_REAL_H
	CLRF HORA_ALARMA_S
	CLRF HORA_ALARMA_M
	CLRF HORA_ALARMA_H
;=== FIN DE LA CONFIGURACION ===




; se espera a que se pulse una tecla para empezar a funcionar, mientras tanto todos los LEDs encendidos y no cuenta el tiempo
	BCF INTCON,RBIF
BucleInicial
	BSF PuertoAlarma,BitAlarma
	MOVLW b'11111111'
	MOVWF PuertoSegundos
	MOVWF PuertoMinutos
	MOVWF PuertoHoras
	CLRF TMR0
	CLRF ContadorL
	BTFSS INTCON,RBIF
	GOTO BucleInicial




;=========== PROGRAMA PRINCIPAL ===========
BUCLE
	;=== Mostramos la hora real o la hora configurada para la alarma ===
	BCF STATUS,IRP	;elegimos el primer banco de la RAM para direccionamiento indirecto
	MOVLW HORA_REAL_S
	BTFSS PuertoPulsadores,BitPulsadorAlarma
	MOVLW HORA_ALARMA_S	;si el pulsador de configurar alarma esta pulsado (nivel BAJO) entonces elegimos la direccion de la RAM donde esta configurada la hora de la alarma, de lo contrario se eligio anteriormente la direccion de la hora real
	MOVWF FSR		;...entonces ahora direccionamos indirectamente hacia los segundos de la hora real o de la alarma.
	MOVF INDF,W		;cogemos el valor de los segundos que debemos mostrar...
	MOVWF PuertoSegundos	;...y lo mostramos
	INCF FSR,F	;ahora elegimos la siguiente direccion de memoria RAM, que sera el de los minutos de lo que se deba mostrar
	MOVF INDF,W		;cogemos el valor de los minutos que debemos mostrar...
	MOVWF PuertoMinutos	;...y lo mostramos
	INCF FSR,F
	MOVF INDF,W
	MOVWF PuertoHoras

	;=== Comprobarmos si la hora actual es igual a la hora configurada para que suene la alarma ===
	CALL ComprobarAlarma

	;=== Activamos la alarma si debe estarlo ===
	BTFSC BitsSueltos,BitAlarmaActivada
	BSF PuertoAlarma,BitAlarma
	BTFSS BitsSueltos,BitAlarmaActivada
	BCF PuertoAlarma,BitAlarma
	;Nota: hacer lo anterior es absurdo porque seria mas eficiente trabajar directamente sobre PuertoAlarma<BitAlarma>, pero de esta manera se puede reutilizar el programa y solo con modificar esta parte se pueden hacer otras cosas, como por ejemplo que suene a intervalos

	;=== Chequeo del estado de los pulsadores ===
	BTFSC INTCON,RBIF
	CALL TeclaCambioDeValor

	;=== Procesar el temporizador ===
	BTFSC INTCON,TMR0IF
	CALL InterrupcionTMR0

	GOTO BUCLE
;=========== FIN DEL PROGRAMA PRINCIPAL ===========



;=========== Funciones sueltas ===========
TeclaCambioDeValor
	BCF BitsSueltos,BitAlarmaActivada	; al pulsar cualquier tecla apagamos la alarma

	CALL EvitarRebotes		;antes de procesar los pulsadores esperamos a que se estabilicen

	MOVF PuertoPulsadores,W	;hacemos esto para poder poner a cero RBIF, que DEBE hacerse aqui, no al final de TeclaCambioDeValor
	BCF INTCON,RBIF

	BTFSC BitsSueltos,BitRecienReseteado
	GOTO ComprobarSiPodemosAumentarTiempo

	; detectamos cual es el pulsador accionado (se activan en nivel BAJO)
	BTFSS PuertoPulsadores,BitPulsadorHm1
	GOTO TeclaHorasMas1pulsada
	BTFSS PuertoPulsadores,BitPulsadorMm1
	CALL AumentarUnMinuto

	RETURN




;Cuando se acaba de resetear el tiempo entonces en lugar de procesar si se debe aumentar una hora o un minuto comprobamos que las teclas H+1 y M+1 esten sueltas (valor logico alto) y si lo estan entonces podemos salir del estado "RecienReseteado"
ComprobarSiPodemosAumentarTiempo
	BTFSS PuertoPulsadores,BitPulsadorMm1
	RETURN
	BTFSS PuertoPulsadores,BitPulsadorMm1
	RETURN
	BCF BitsSueltos,BitRecienReseteado ;ponemos a cero este bit cuando ambos pulsadores estan a UNO
	RETURN





;si HORA_ALARMA_M==HORA_ALARMA_H==0 entonces no hacer que suene
ComprobarAlarma
	MOVF HORA_ALARMA_M,W
	XORLW 0	
	BTFSS STATUS,Z
	GOTO ComprobarAlarma1
	MOVF HORA_ALARMA_H,W
	XORLW 0	
	BTFSC STATUS,Z
	RETURN

;Funciones para comprobar si la hora configurada es la misma que la hora actual y entonces se debe activar la alarma
ComprobarAlarma1
	MOVF HORA_REAL_S,W
	XORWF HORA_ALARMA_S,W
	BTFSC STATUS,Z	;si Z==1 entonces los segundos de la alarma configurada son iguales a los de la hora real
	CALL ComprobarAlarma2
	RETURN

ComprobarAlarma2
	MOVF HORA_REAL_M,W
	XORWF HORA_ALARMA_M,W
	BTFSC STATUS,Z	;si Z==1 entonces los minutos de la alarma configurada son iguales a los de la hora real
	CALL ComprobarAlarma3
	RETURN

ComprobarAlarma3
	MOVF HORA_REAL_H,W
	XORWF HORA_ALARMA_H,W
	BTFSC STATUS,Z	;si Z==1 entonces las horas de la alarma configurada son iguales a los de la hora real
	BSF BitsSueltos,BitAlarmaActivada		;hora de alarma==hora real, asi que encendemos alarma
	RETURN





TeclaHorasMas1pulsada
	;BTFSS PuertoPulsadores,TeclaMinutosMas1puldasa
	BTFSS PuertoPulsadores,BitPulsadorMm1
	GOTO ResetearHora	; si hay nivel bajo en TeclaMinutosMas1puldasa entonces estan pulsadas las teclas de aumentar minutos y horas
	
;	BTFSC PuertoPulsadores,BitPulsadorHm1
	CALL AumentarUnaHora		;si hay nivel alto en TeclaMinutosMas1puldasa entonces solo esta pulsada la tecla de aumentar horas, asi que aumentamos una hora

	RETURN





AumentarUnMinuto
	;NOTA: si los pulsadores de configurar hora y configurar alarma estan ambos apretados se muestra la hora configurada para la alarma, asi que si se aprieta el pulsador de aumentar un minuto a la vez que los otros dos nombrados, solo se cambiara el de la hora configurada para la alarma
	BTFSS PuertoPulsadores,BitPulsadorAlarma
	GOTO AumentarUnMinutoDeLaAlarmaConfigurada
	BTFSS PuertoPulsadores,BitPulsadorHora
	GOTO AumentarUnMinutoDeLaHoraReal
	RETURN

AumentarUnMinutoDeLaAlarmaConfigurada
	BCF STATUS,IRP
	MOVLW HORA_ALARMA_M
	BSF BitsSueltos,EstamosProcesandoMinutos		;EstamosProcesandoMinutos se usa como parametro para AumentarUnMinutoOsegundoDeLoElegido para decidir si se esta trabajando con segundos o con minutos
	GOTO AumentarUnMinutoOsegundoDeLoElegido

AumentarUnMinutoDeLaHoraReal
	BCF STATUS,IRP
	MOVLW HORA_REAL_M
	BSF BitsSueltos,EstamosProcesandoMinutos
	GOTO AumentarUnMinutoOsegundoDeLoElegido


AumentarUnMinutoOsegundoDeLoElegido
	MOVWF FSR	;anteriormente se eligio lo que se debe aumentar
	INCF INDF,F

	;ahora comprobaremos si nos pasamos de 9 en las unidades, es decir, si hay d'10' (b'xxxx1010')
	BTFSS INDF,1
	GOTO NoSeSalioDelRango	;si el bit 1 de los minutos esta a cero entonces no nos salimos del rango
	BTFSS INDF,3
	GOTO NoSeSalioDelRango
	;si los bits 1 y 3 estan activados entonces ponemos las unidades a cero y aumentamos una decena
	BCF INDF,1
	BCF INDF,3
	MOVLW b'00010000'
	ADDWF INDF,F
	;ahora comprobaremos si nos pasamos de 5 en las decenas, es decir, si hay d'6' (b'x110xxxx')
	BTFSS INDF,5
	GOTO NoSeSalioDelRango
	BTFSS INDF,6
	GOTO NoSeSalioDelRango
	;si los bits 5 y 6 estan activados entonces ponemos las decenas a cero y aumentamos un minuto o una hora
	BCF INDF,5
	BCF INDF,6

	BTFSC BitsSueltos,EstamosProcesandoMinutos
	CALL AumentarUnaHoraDeLoElegidoPorDesbordamiento	;como estamos procesando minutos, entonces ahora debemos aumentar una hora

	BTFSS BitsSueltos,EstamosProcesandoMinutos
	GOTO AumentarUnMinutoDeLoElegidoPorDesbordamiento


NoSeSalioDelRango
	RETURN		;NOTA: este return pertenece a AumentarUnMinuto y al CALL que viene desde las interrupciones, no a AumentarUnMinutoDeLoElegido porque en AumentarUnMinuto no se debe aumentar un minuto cuando ninguno de los pulsadores de configurar hora real ni alarma estan pulsados




AumentarUnMinutoDeLoElegidoPorDesbordamiento
	INCF FSR,W
	BSF BitsSueltos,EstamosProcesandoMinutos
	GOTO AumentarUnMinutoOsegundoDeLoElegido




AumentarUnaHora
	MOVLW HORA_REAL_H
	BTFSS PuertoPulsadores,BitPulsadorAlarma
	MOVLW HORA_ALARMA_H
	MOVWF FSR
	GOTO AumentarUnaHoraDada





AumentarUnaHoraDeLoElegidoPorDesbordamiento
	INCF FSR,F	;como antes estaban puestos en el FSR los minutos que debian aumentarse ahora ponemos las horas

AumentarUnaHoraDada
	INCF INDF,F	;aumentamos una hora
	;si esta a 10 en binario lo ponemos en BCD
	MOVF INDF,W
	XORLW b'00001010'
	BTFSC STATUS,Z
	GOTO PonerINDFaBCD10

	;si son las 19h+1 lo ponemos en BCD
	MOVF INDF,W
	XORLW b'00011010'
	BTFSC STATUS,Z
	GOTO PonerINDFaBCD20

	;si son las 24h lo reseteamos
	MOVF INDF,W
	XORLW b'00100100'
	BTFSC STATUS,Z
	CLRF INDF

HoraCorregida
	RETURN







PonerINDFaBCD10
	MOVLW b'00010000'
	MOVWF INDF
	GOTO HoraCorregida


PonerINDFaBCD20
	MOVLW b'00100000'
	MOVWF INDF
	GOTO HoraCorregida



; Rutina para esperar sin hacer nada.
; Sabemos que entre cada valor consecutivo de ContadorL van a pasar 3ms, asi que si esperamos a que ContadorL aumente 7 unidades estaremos esperando entre 18ms y 21ms, no necesitamos exactitud
EvitarRebotes
	MOVF ContadorL,W
	ADDLW d'7'
	MOVWF MomentoFinRebote
EsperandoFinRebote	MOVF MomentoFinRebote,W
	XORWF ContadorL,W
	BTFSS STATUS,Z
	GOTO EsperandoFinRebote
	RETURN



ResetearHora
	MOVLW HORA_REAL_S
	BTFSS PuertoPulsadores,BitPulsadorAlarma
	MOVLW HORA_ALARMA_S	;si BitPulsadorAlarma esta a cero entonces debemos elegir la alarma
	BCF STATUS,IRP
	MOVWF FSR		;elegimos para direccionar indirectamente los segundos de lo que debe ser borrado
	CLRF INDF	;borramos los segundos
	INCF FSR,F		;elegimos la siguiente posicion de la RAM, que seran los minutos
	CLRF INDF	;borramos los minutos
	INCF FSR,F		;elegimos las horas
	CLRF INDF	;borramos las horas
	BSF BitsSueltos,BitRecienReseteado
	RETURN

;=========== fin de funciones sueltas ===========


;===== INTERRUPCIONES =====

INTERRUPCIONES
	MOVWF W_TEMP
	MOVF STATUS,W
	MOVWF STATUS_TEMP

	BTFSC INTCON,TMR0IF
	GOTO InterrupcionTMR0

FinInterrupcion
	MOVF STATUS_TEMP,W
	MOVWF STATUS
	MOVF W_TEMP,W
	RETFIE



InterrupcionTMR0
	BCF INTCON,TMR0IF

	;Tenemos que contar 306 desbordamientos del TMR0
	INCF ContadorL,F
	
	MOVF ContadorL,W
	XORLW 0
	BTFSC STATUS,Z
	BSF BitsSueltos,ContadorH	;ContadorL se desbordo asi que aumentamos ContadorH
	;comprobamos si Contador es 305
	MOVF ContadorL,W
	XORLW b'00110001'	;100110000 es 305, siendo ContadorH el MSB
	BTFSS STATUS,Z
	GOTO FinInterrupcion	;si Z es 0 entonces "Contador" NO es 305 y podemos terminar de procesar la interrupcion
	BTFSS BitsSueltos,ContadorH
	GOTO FinInterrupcion	;si Contador H es 0 entonces "Contador" NO es 305 y podemos terminar de procesar la interrupcion

	;ContadorL es 00110001 y ContadorH es 1, asi que estamos en 305 para "Contador", aumentamos un segundo y configuramos todo para que cuente otro segundo
	MOVLW HORA_REAL_S
	BCF STATUS,IRP
	BCF BitsSueltos,EstamosProcesandoMinutos
	CALL AumentarUnMinutoOsegundoDeLoElegido

	BCF BitsSueltos,ContadorH
	CLRF ContadorL
	MOVLW d'211'
	MOVWF TMR0

	GOTO FinInterrupcion	

	END
