;********************************** INT_Temporizador.asm ********************************
;
;	===================================================================
;	  Del libro "MICROCONTROLADOR PIC16F84. DESARROLLO DE PROYECTOS"
;	  E. Palacios, F. Remiro y L. López.			www.pic16f84a.com
; 	  Editorial Ra-Ma.  www.ra-ma.es
;	===================================================================
;
; Programa de control para un temporizador digital de precisión. La programación del tiempo
; de temporización se realiza mediante dos pulsadores: "AJUSTE" y "ON/INCREM". Funcionamiento:
;     -	En estado de reposo la salida del temporizador está apagada y el pantalla aparece el
;		tiempo deseado para la próxima temporización.
;     - Si se pulsa "ON/INCREM" comienza la temporización.
;     - Cuando acaba la temporización pasa otra vez a reposo.
;     -	Si pulsa "AJUSTE" antes que haya acabado el tiempo de temporización actúa como pulsador
;		de paro: interrumpe la temporización, apaga la carga y pasa al estado de reposo.
;
; Para ajustar la temporización al tiempo deseado. 
;     -	Pulsa "AJUSTE" y ajusta el tiempo deseado mediante el pulsador "ON/INCREM".
;     -	Se vuelve a pulsar "AJUSTE" y pasa a modo de reposo.
;
; Al apagar el sistema debe conservar el tiempo de temporización deseado para la próxima vez
; que se encienda.
;
; ZONA DE DATOS **********************************************************************

	LIST		P=16F84A
	INCLUDE		<P16F84A.INC>
	__CONFIG	_CP_OFF &  _WDT_OFF & _PWRTE_ON & _XT_OSC

	CBLOCK  0x0C
	TiempoDeseado				; El tiempo deseado de temporización.
	Tiempo						; Tiempo que resta de temporización.
	FlagsModos					; Guarda los flags con los diferentes
	ENDC						; modos de funcionamiento.

	ORG	0x2100					; Corresponde a la dirección 0 de la zona
								; EEPROM de datos. Aquí se va a guardar el
	DE	0x00					; tiempo de temporización deseado.

#DEFINE  F_Temporizador_ON		FlagsModos,2
#DEFINE  F_Temporizador_Ajuste	FlagsModos,1
#DEFINE  F_Temporizador_OFF		FlagsModos,0

#DEFINE  SalidaTemporizador 	PORTB,1		; Salida donde se conecta la carga.
#DEFINE  Zumbador	 			PORTB,2		; Salida donde se conecta el zumbador.
#DEFINE  AjustePulsador			PORTB,7		; Los pulsadores están conectados a estas
#DEFINE  IncrementarPulsador	PORTB,6		; líneas del Puerto B.

; ZONA DE CÓDIGOS ********************************************************************

	ORG 	0
	goto	Inicio
	ORG	4
	goto	ServicioInterrupcion

Mensajes
	addwf	PCL,F
Mensaje_ON
	DT "   En MARCHA", 0x00
Mensaje_Ajuste
	DT "Tiempo  deseado:", 0x00
Mensaje_OFF
	DT "     PARADO", 0x00

; Instrucciones de inicialización. ------------------------------------------------------
;
Inicio
	call	LCD_Inicializa
	bsf		STATUS,RP0
	movlw	b'10000111'					; Prescaler de 256 asignado al TMR0.
	movwf	OPTION_REG
	bsf		AjustePulsador				; Configurados como entradas.
	bsf		IncrementarPulsador
	bcf		SalidaTemporizador			; Configurados como salidas.
	bcf		Zumbador
	bcf		STATUS,RP0
	clrw								; Lee la posición 0x00 de memoria EEPROM de datos
	call	EEPROM_LeeDato				; donde se guarda el tiempo deseado de la última vez
	movwf	TiempoDeseado				; que se ajustó.
	call	ModoTemporizador_OFF		; Modo de funcionamiento inicial.
	movlw	b'10001000'					; Activa interrupciones RBI.
	movwf	INTCON
Principal
	goto	Principal

; Subrutina "ServicioInterrupcion" ------------------------------------------------------
;
; Detecta qué ha producido la interrupción y ejecuta la subrutina de atención correspondiente.

ServicioInterrupcion
	btfsc	INTCON,T0IF
	call	Temporizador
	btfss	INTCON,RBIF					; Si es una interrupción RBI lee los pulsadores.
	goto	FinInterrupcion
	btfss	AjustePulsador				; ¿Está presionado el pulsador de "AJUSTE"?.
	call	CambiarModo					; Sí, pues salta a la subrutina correspondiente.
	btfsc	IncrementarPulsador			; ¿Pulsado "ON/INCREM"?.
	goto	FinInterrupcion				; No, pues salta al final y sale.
;
	call	Retardo_20ms				; Espera que se estabilice el nivel de tensión.
	btfsc	IncrementarPulsador			; Si es un rebote del pulsador "ON/INCREM" sale fuera.
	goto	FinInterrupcion
	btfsc	F_Temporizador_OFF			; ¿Estaba en reposo cuando pulsó "ON/INCREM"?
	call	ModoTemporizador_ON			; Sí, pues comienza la temporización.
	btfsc	F_Temporizador_Ajuste		; ¿Estaba ajustando tiempo?
	call	IncrementarTiempoDeseado	; Sí, pues pasa a incrementar el tiempo deseado.
FinInterrupcion
	bcf		INTCON,RBIF					; Limpia los flags de reconocimiento.
	bcf		INTCON,T0IF
	retfie

; Subrutinas "CambiarModo" y todas las de MODO de funcionamiento ------------------------
;
; Subrutina de atención a la interrupción producida al presionar el pulsador "AJUSTE" que 
; cambia el modo de funcionamiento.

; Hay identificados tres modos de funcionamiento que se diferencian mediante los tres flags:
;    A)	Modo "Temporizador_OFF" o estado inicial. A él se pasa en el estado inicial cada vez
;		que termina una temporización o cuando se aborta la temporización sin esperar a que
;		finalice. Reconocido por el flag F_Temporizador_OFF, bit 0 del registro FlagsModos.
; 		una temporización  o cada vez que se aborta la temporización sin esperar a que finalice.
;    B)	Modo "Temporizador_Ajuste", donde se ajusta la temporización deseada cuando funcione
;		como temporizador. Reconocido por el flag F_Temporizador_Ajuste, bit 1 del FlagsModos.
;    C)	Modo "Temporizador_ON", la salida está activada mientras dure la temporización.
;		Reconocido por el flag F_Temporización_ON, que es el bit 2 del registro FlagsModos.
;
; El programa consigue que esté activado uno sólo de los flags anteriores.

; El contenido del registro (FlagsModos) diferencia los siguientes modos de funcionamiento:
; - (FlagsModos)=b'00000001'. Está en el modo "Temporizador_OFF", en reposo.
; - (FlagsModos)=b'00000010'. Está en el modo "Temporizador_Ajuste", ajustando tiempo deseado.
; - (FlagsModos)=b'00000100'. Está en el modo "Temporizador_ON", activa la carga y temporizador.
;
; Al pulsar "AJUSTE" pueden darse tres casos:
; - Si estaba en modo "Temporizador_OFF", pasa a modo "Temporizador_Ajuste".
; - Si estaba en modo "Temporizador_Ajuste", pasa a modo "Temporizador_OFF", pero antes salva
;   el tiempo de temporización deseado en la EEPROM de datos.	   
; - Si estaba en modo "Temporizador_ON", pasa a modo "Temporizador_OFF". (Interrumpe la
;   temporización).

CambiarModo
	call	PitidoCorto					; Cada vez que pulsa origina un pitido. 
	btfsc	AjustePulsador				; Si es un rebote sale fuera.
	goto	EsperaDejePulsar
	btfsc	F_Temporizador_OFF			; ¿Está en reposo?
	goto	ModoTemporizador_Ajuste		; Sí, pues pasa a ajustar la temporización.
	btfss	F_Temporizador_Ajuste		; ¿Está ajustando?
	goto	ModoTemporizador_OFF		; No, pues pasa a reposo.
										; Sí, pues antes de pasar a reposo salva en la
	clrw								; posición 00h de memoria EEPROM de datos el tiempo 
	movwf	EEADR						; de temporización deseado. Se conserva aunque se
	movf	TiempoDeseado,W				; apague la alimentación.
	call	EEPROM_EscribeDato
ModoTemporizador_OFF
	bcf		SalidaTemporizador			; Apaga la carga y resetea tiempo deseado.
	call	Pitido
	movlw	b'00000001'				; Actualiza el registro FlagsModos pasando al
	movwf	FlagsModos				; modo inicial "Temporizador_OFF".
	bcf		INTCON,T0IE				; Prohíbe las interrupciones del TMR0.
	movf	TiempoDeseado,W			; Repone otra vez el tiempo que se desea para la 
	movwf	Tiempo					; próxima temporización.
	call	LCD_Borra				; Borra la pantalla.
	movlw	Mensaje_OFF				; En pantalla el mensaje correspondiente.
	goto	FinCambiarModo

ModoTemporizador_Ajuste
	bcf		SalidaTemporizador		; Apaga la carga
	movlw	b'00000010'				; Actualiza el registro FlagsModos pasando al
	movwf	FlagsModos				; modo "Temporizador_Ajuste".
	clrf	Tiempo					; Resetea el tiempo.
	clrf	TiempoDeseado
	bcf		INTCON,T0IE				; Prohíbe las interrupciones del TMR0.
	call	LCD_Borra
	movlw	Mensaje_Ajuste			; En pantalla el mensaje correspondiente.
	goto	FinCambiarModo

ModoTemporizador_ON
	movf	TiempoDeseado,W			; Si el tiempo deseado es cero pasa a modo
	btfsc	STATUS,Z				; de trabajo "Temporizador_OFF".
	goto	ModoTemporizador_OFF
	movwf	Tiempo
	call	PitidoCorto
	movlw	b'00000100'				; Actualiza el registro FlagsModos pasando al
	movwf	FlagsModos				; modo "Temporizador_ON".
	movlw	TMR0_Carga50ms			; Carga el TMR0.
	movwf	TMR0
	movlw	CARGA_1s				; Y el registro cuyo decremento contará los
	movwf	Registro50ms			; segundos.
	bsf		INTCON,T0IE				; Autoriza las interrupciones de TMR0.
	call	LCD_Borra
	bsf		SalidaTemporizador		; Enciende la carga.
	movlw	Mensaje_ON				; En pantalla el mensaje correspondiente.
FinCambiarModo
	call	LCD_Mensaje
	call	VisualizaTiempo
EsperaDejePulsar
	btfss	AjustePulsador			; Espera deje de pulsar.
	goto	EsperaDejePulsar
	return

; Subrutina "Temporizador" ----------------------------------------------------------------
;
; Esta subrutina va decrementando el tiempo de temporización y visualizándolo en la pantalla.
; Se ejecuta debido a la petición de interrupción del Timer 0 cada 50 ms exactos, comprobado
; experimentalmente con la ventana "Stopwatch" del simulador del MPLAB.

	CBLOCK
	Registro50ms					; Guarda los incrementos cada 50 ms.
	ENDC

TMR0_Carga50ms	EQU	d'256'-d'195'			; Para conseguir la interrupción cada 50 ms.
CARGA_1s		EQU	d'20'			; Leerá cada segundo (20 x 50ms = 1000 ms).	

Temporizador
	call	Retardo_50micros		; Ajuste fino de 71 microsegundos para
	call	Retardo_20micros		; ajustar a 50 milisegundos exactos.
	nop
  	movlw	TMR0_Carga50ms			; Carga el Timer0.
	movwf	TMR0
	decfsz	Registro50ms,F			; Decrementa el contador.
	goto	FinTemporizador			; No ha pasado 1 segundo y por tanto sale.
	movlw	CARGA_1s				; Repone el contador nuevamente.
	movwf	Registro50ms
	btfss	F_Temporizador_ON		; Si no está en modo "Temporizador_ON" sale
	goto	FinTemporizador			; fuera.
	decfsz	Tiempo,F
	goto	VisualizaContador		; Visualiza el tiempo restante.
	bcf		SalidaTemporizador		; Apaga la salida
	call	VisualizaTiempo			; Visualiza cero segundos en la pantalla.
	call	Pitido					; Tres pitidos indican final de la temporización.
	call	Retardo_500ms
	call	Pitido
	call	Retardo_500ms
	call	PitidoLargo
	call	Retardo_500ms
	call	ModoTemporizador_OFF	; Acabó la temporización.
	goto	FinTemporizador
VisualizaContador
	call	VisualizaTiempo
FinTemporizador
	return

; Subrutina "VisualizaTiempo" -----------------------------------------------------------------
;
; Visualiza el registro Tiempo en formato "Minutos:Segundos". Así por ejemplo, si
; (Tiempo)=124 segundos en la segunda línea de la pantalla visualiza " 2:04", ya que 124
; segundos es igual a 2 minutos más 4 segundos.
;
VisualizaTiempo
	movlw	.5						; Para centrar visualización en la
	call	LCD_PosicionLinea2		; segunda línea.
	movf	Tiempo,W				; Convierte el tiempo deseado (y expresado sólo en
	call	MinutosSegundos			; segundos) a minutos y segundos.
	movf	TemporizadorMinutos,W	; Visualiza los minutos.
	call	BIN_a_BCD				; Lo pasa a BCD.
	call	LCD_Byte
	movlw	':'						; Visualiza dos puntos.
	call	LCD_Caracter
	movf	TemporizadorSegundos,W	; Visualiza los segundos.
	call	BIN_a_BCD				; Lo pasa a BCD.
	goto	LCD_ByteCompleto
	return

; Subrutina "MinutosSegundos" -----------------------------------------------------------
;
; Una cantidad expresada exclusivamente en segundos y contenida en el registro W es
; convertida a minutos y segundos. El resultado se guarda en dos posiciones de memoria
; llamadas TemporizadorMinutos y TemporizadorSegundos.
;
; El máximo número a convertir será el 255 que es el máximo valor que puede adquirir el
; número binario de entrada de 8 bits. (255 segundos = 4 minutos + 15 segundos)
;
; El procedimiento utilizado es mediante restas de 60 tal como se explica en el siguiente
; ejemplo que trata de la conversión del 124 segundos a minutos y segundos.
; 124 segundos = 2 minutos + 4 segundos. 
;
; Minutos		Segundos 	¿(Segundos)<60?
; -------		--------	------------------------------------------------
;     0		      124		NO. Resta 60 a (Segundos) e incrementa (Minutos).
;     1		       64 		NO. Resta 60 e (Segundos) e incrementa (Minutos).
;     2		        4		Sí, se acabó. 
;
; Entrada:	En el registro W el número de segundos a convertir.
; Salidas:	En (TemporizadorMinutos) y (TemporizadorSegundos) el resultado.

	CBLOCK
	TemporizadorMinutos
	TemporizadorSegundos
	ENDC
;
MinutosSegundos
	movwf	TemporizadorSegundos	; Carga el número de segundos a convertir.
	clrf	TemporizadorMinutos		; Carga los registros con el resultado inicial.
Resta60
	movlw	.60						; Resta 60 en cada pasada.
	subwf	TemporizadorSegundos,W	; (W)=(TemporizadorSegundos)-60.
	btfss	STATUS,C				; ¿(W) positivo?, ¿(TemporizadorSegundos)>=60?.
	goto 	FinMinutosSegundos		; No, es menor de 60. Acabó.
	movwf	TemporizadorSegundos	; Sí, por tanto, recupera lo que queda por restar.
	incf	TemporizadorMinutos,F	; Incrementa los minutos.
	goto	Resta60					; Y vuelve a dar otra pasada.
FinMinutosSegundos
	return

; Subrutina "IncrementarTiempoDeseado" --------------------------------------------------
;
; Subrutina de atención a la interrupción por cambio de la línea RB6 a la cual se ha
; conectado el pulsador "INCREMENTAR".
; Estando en el modo "Temporizador_Ajustar" incrementa el valor del tiempo deseado
; expresado en segundos en intervalos de 5 segundos y hasta un máximo de 255 segundos.
;
SALTO_INCR	EQU	.5

IncrementarTiempoDeseado
	call	PitidoCorto				; Cada vez que pulsa se oye un pitido.
	movlw	SALTO_INCR				; Incrementa el tiempo deseado de temporización
	addwf	Tiempo,F				; saltos de SALTO_INCR segundos.
	btfsc	STATUS,C				; Si pasa del valor máximo lo inicializa.
	clrf	Tiempo
	call	VisualizaTiempo			; Visualiza mientras espera que deje de pulsar.
	call	Retardo_200ms
	btfss	IncrementarPulsador		; Mientras permanezca pulsado,
	goto	IncrementarTiempoDeseado ; incrementa el dígito.
	movf	Tiempo,W				; Actualiza el tiempo deseado.
	movwf	TiempoDeseado			; Este es el tiempo deseado.
	return
	
; Subrutinas "PitidoLargo", "Pitido" y "PitidoCorto" -------------------------------------
;
PitidoLargo
	bsf		Zumbador
	call	Retardo_500ms
Pitido
	bsf		Zumbador
	call	Retardo_200ms
PitidoCorto
	bsf		Zumbador
	call	Retardo_20ms
	bcf		Zumbador
	return
;
	INCLUDE  <RETARDOS.INC>
	INCLUDE  <BIN_BCD.INC>
	INCLUDE  <LCD_4BIT.INC>
	INCLUDE  <LCD_MENS.INC>
	INCLUDE  <EEPROM.INC>
	END

;	===================================================================
;	  Del libro "MICROCONTROLADOR PIC16F84. DESARROLLO DE PROYECTOS"
;	  E. Palacios, F. Remiro y L. López.		www.pic16f84a.com
; 	  Editorial Ra-Ma.  www.ra-ma.es
;	===================================================================
