;
;					EJEMPLO 37
;
;				Autor: Mikel Etxebarria
;			(c) Microsystems Engineering (Bilbao)
;
;Ejemplo para MicroPIC Trainer y Micro'PIC Trainer Plus
;
;El módulo MSSP. Modo I2C Master. Reloj Digital
;
;El módulo MSSP de los dispositivos PIC16F87X incorpora por hardware el interface I2C tanto
;en el modo Master como en el modo Slave. Las líneas RC3/SCL y RC4/SDA actúan como señales
;de reloj y datos del bus I2C respectivamente.
;
;Los dispositivos I2C SAA1064 y PCF8583. Controlador de displays de 7 segmentos y reloj/ca-
;lendario en tiempo real.
;
;Se trata de implementar un reloj en tiempo real. Los interruptores RA0 y RA1 de la Trainer
;permiten ajustar las minutos y la hora respectivamente. Esta se graba sobre el dispositivo
;I2C PCF8583. El dispositivo SAA1064 visualiza, sobre los displays de la Trainer PLUS, la 
;hora en curso.
;
;Mediante el TMR0 se realiza una temporización de 0.5 seg. para leer el estado de RA0 y RA1 
;y hacer los ajustes horarios oportunos si estuvieran activados.

		List	p=16F873	;Tipo de procesador
		include	"P16F873.INC"	;Definiciones de registros internos

Temporal	equ	0x20		;Temporal de propósito general
Minutos		equ	0x21		;Variable de minutos.
Horas		equ	0x22		;Variable de horas.
Delay		equ	0x23		;Temporal de temporización
DP_Status	equ	0x24		;Flag de estado del punto decimal del segundero
ACK		equ	0x25		;Flag de enviar o no  NACK

		org	0x00		;Vector de Reset
		goto	Inicio

		org	0x05		;Salva el vector de interrupción
		
;***************************************************************************************
;BCD_7seg: Rutina de conversión de BCD (0-9) a 7 segmentos. El acumulador W contiene,
;durante la llamada, el código BCD y, al retornar, el correspondiente a 7 segmentos
;
BCD_7seg:	andlw	b'00001111'
		addwf	PCL,F		;Desplazamiento sobre la tabla
		retlw	b'00111111'	;0
		retlw	b'00000110'	;1
		retlw	b'01011011'	;2
		retlw	b'01001111'	;3
		retlw	b'01100110'	;4
		retlw	b'01101101'	;5
		retlw	b'01111101'	;6
		retlw	b'00000111'	;7
		retlw	b'01111111'	;8
		retlw	b'01100111'	;9

;********************************************************************************************
;Inc_BCD: Incrementa una unidad en BCD el contenido del reg. W. El resultado se devuelve en
;el reg. W

Inc_BCD		movwf	Temporal
		incf	Temporal,F	;Incrementa el nible de menos peso
		movlw	0x0f
		andwf	Temporal,W
		sublw	0x09
		btfsc	STATUS,C	;Mayor de 9 ??
		goto	Inc_BCD_Fin	;No
		movlw	0xf0
		andwf	Temporal,F	;Si, pone a 0 el nible de menos peso
		movlw	0x10
		addwf	Temporal,F	;Incrementa el nible de más peso
		movlw	0xf0
		andwf	Temporal,W
		sublw	0x90
		btfsc	STATUS,C	;Mayor de 9 ??
		goto	Inc_BCD_Fin	;No
		clrf	Temporal
Inc_BCD_Fin	movf	Temporal,W
		return

;********************************************************************************************
;Leer_Clock: Lee desde el dispositivo PCF8583 los minutos y las horas
;
Leer_Clock	call	Send_Start	;Envía secuencia de inicio
		movlw	b'10100000'	;Modo escritura
		call	Send_Byte	;Dirección del dispositivo PCF8583 en la PLUS
		movlw	.3
		call	Send_Byte	;Dirección interna a leer (la 3, minutos)
		call	Send_Stop
		call	Send_Start	;Envía secuencia de inicio
		movlw	b'10100001'	;Modo lectura
		call	Send_Byte	;Dirección del dispositivo PCF8583 en la PLUS
		bcf	ACK,0		;Enviar ACK tras la lectura del byte
		call	Read_Byte	;Lee el byte de minutos
		movwf	Minutos
		bsf	ACK,0		;Enviar NACK trás la lectura del byte
		call	Read_Byte	;Lee el byte de horas (dirección interna 4)
		movwf	Horas
		call	Send_Stop	;Envía secuencia de Stop
		return

;********************************************************************************************
;Esc_Clock: Escribe el contenido de las variables Minutos y Horas sobre las posiciones 3 y 4
;del dispositivo PCF8583. De esta forma se ajusta a una nueva hora.
;
Esc_Clock	call	Send_Start	;Envía secuencia de inicio
		movlw	b'10100000'	;Modo escritura
		call	Send_Byte	;Dirección del dispositivo PCF8583 en la PLUS
		movlw	.3
		call	Send_Byte	;Direcciona posición interna 3 (minutos)
		movf	Minutos,W
		call	Send_Byte	;Escribe los minutos
		movf	Horas,W
		call	Send_Byte	;Escribe las horas
		call	Send_Stop	;Envía secuencia de stop
		return

;********************************************************************************************
;Visualiza: Recoge el valor actual de las variables Horas y Minutos, desempaqueta en dígitos
;individuales, convierte a 7 segmentos y visualiza sobre los cuatros displays controlados por
;el dispositivo I2C SAA1064

Visualiza	call	Send_Start	;Envía secuencia de inicio
		movlw	b'01110110'
		call	Send_Byte	;Dirección del dispositivo SAA1064 en la PLUS
		movlw	.0
		call	Send_Byte	;Envía byte de instrucción
		movlw	b'01000111'
		call	Send_Byte	;Envía byte de control
		swapf	Horas,W
		andlw	0x0f		;Coge decenas de horas
		call	BCD_7seg	;Convierte a 7 segmentos
		call	Send_Byte	;Visualiza sobre el dígito D1
		movlw	0x0f
		andwf	Horas,W		;Coge unidades de horas
		call	BCD_7seg	;Convierte a 7 segmentos
		btfsc	DP_Status,0	;Pusto decimal del segundero en ON ??
		goto	Visu_1		;Si
		iorlw	b'10000000'	;No, puesta a 1 del segundero, DP en On
		goto	Visu_2		
Visu_1		andlw	b'01111111'	;Puesta a 0 del segundero, DP en Off
Visu_2		call	Send_Byte	;Visualiza sobre el dígito D2
		swapf	Minutos,W
		andlw	0x0f		;Coge decenas de minutos
		call	BCD_7seg	;Convierte a 7 segmentos
		call	Send_Byte	;Visualiza sobre el dígito D3
		movlw	0x0f
		andwf	Minutos,W	;Coge unidades de minutos
		call	BCD_7seg	;Convierte a 7 segmentos
		call	Send_Byte	;Visualiza sobre el dígito D4
		call	Send_Stop	;Envía secuencia de stop
		return

;*******************************************************************************************
;Send_Start: Envía la condición de inicio

Send_Start	bcf	PIR1,SSPIF	;Restaura el flag del módulo MSSP
		bsf	STATUS,RP0	;Seleciona página 1
		bsf	SSPCON2,SEN	;Activa secuencia de inicio
		bcf	STATUS,RP0
Start_Wait	btfss	PIR1,SSPIF	;Fin de secuencia de inicio ??
		goto	Start_Wait	;No, esperar		
		return

;*******************************************************************************************
;Send_Stop: Envía la secuencia de stop

Send_Stop	bcf	PIR1,SSPIF	;Restaura el flag del módulo MSSP
		bsf	STATUS,RP0	;Seleciona página 1
		bsf	SSPCON2,PEN	;Activa secuencia de stop
		bcf	STATUS,RP0
Stop_Wait	btfss	PIR1,SSPIF	;Fin de secuencia de Stop ??
		goto	Stop_Wait	;No, esperar
		return

;*******************************************************************************************
;Send_Byte: Transmite el byte del W vía I2C. La rutina finaliza cuando se recibe /ACK

Send_Byte	bcf	PIR1,SSPIF	;Restaura el flag del módulo MSSP
		movwf	SSPBUF		;Byte a transmitir pasa al buffer de salida
Send_Byte_Wait	btfss	PIR1,SSPIF	;Recibido el bit /ACK ??
		goto	Send_Byte_Wait	;No, esperar
		return

;*******************************************************************************************
;Read_Byte: Lee un byte procedente del dispositivo I2C selecionado y lo devuelve en W
;Seguidamente se genera y transmite el bit /ACK

Read_Byte	bcf	PIR1,SSPIF	;Restaura el flag del módulo MSSP
		bsf	STATUS,RP0	;Selecciona página 1
		bsf	SSPCON2,RCEN	;Activa el modo receptor
		bcf	STATUS,RP0	;Selecciona página 0
Read_Wait	btfss	PIR1,SSPIF	;Recibidos los 8 bits ??
		goto	Read_Wait	;No, esperar

		bcf	PIR1,SSPIF	;Restaura el flag del módulo MSSP
		btfsc	ACK,0		;Enviar ACK ??
		goto	No_ACK		;No, enviar NACK
		
		bsf	STATUS,RP0	;Si, selecciona página 1
		bcf	SSPCON2,ACKDT	;Pone bit ACK a "0"
		bsf	SSPCON2,ACKEN	;Aciva la secuencia de generación del bit ACK
		bcf	STATUS,RP0	;Selecciona página 0
ACK_Wait	btfss	PIR1,SSPIF	;Secuencia ACK finalizada ??
		goto	ACK_Wait	;No, esperar
		movf	SSPBUF,W	;Lee el byte recibido
		return

;Algunos dispositivos precisan que el master envíe un NACK tras recibir el último byte

No_ACK		bsf	STATUS,RP0	;Selecciona página 1
		bsf	SSPCON2,ACKDT	;Pone bit ACK a "1"
		bsf	SSPCON2,ACKEN	;Aciva la secuencia de generación del bit NACK
		bcf	STATUS,RP0	;Selecciona página 0
NACK_Wait	btfss	PIR1,SSPIF	;Secuencia NACK finalizada ??
		goto	NACK_Wait	;No, esperar
		movf	SSPBUF,W	;Lee el byte recibido
		return

;*******************************************************************************************
;Programa principal

Inicio	     	clrwdt
		clrf	PORTA
		clrf	PORTC		
		bsf	STATUS,RP0	;Selecciona banco 1
		movlw	b'00000110'	
		movwf	ADCON1		;Puerta A DIGITAL
		movlw	b'11111111'
		movwf	TRISA		;RA0 y RA1 entradas digitales
		movwf	TRISC		;RC3/SCL y RC4/SDA entradas
		movlw	b'11000111'
		movwf	OPTION_REG	;Preescaler de 256 asociado al TMR0
		movlw	b'10000000'
		movwf	SSPSTAT		;Velocidad estándar con niveles I2C
		movlw	.9
		movwf	SSPADD		;Velocidad del bus I2C 100KHz
		bcf	STATUS,RP0	;Selecciona banco 0

;Módulo MSSP en el modo Master I2C

		movlw	b'00101000'
		movwf	SSPCON		;Módulo MSSP en On

;Trabajando a 4MHz y con un preescaler de 256, el TMR0 se carga con 195 para conseguir que
;rebase cada 50mS. Esto se repite tantas veces como indique la variable Delay para 
;obtener la temporización final deseada.

		bcf	INTCON,T0IF	;Restaura el flag del TMR0
		movlw	~.195
		movwf	TMR0		;El TMR0 rebasa cada 50 mS
		movlw	.10
		movwf	Delay		;Repetir 10 veces para obtener 0.5 seg.

		clrf	ACK

Loop		clrwdt
		call	Leer_Clock	;Lee hora actual
		call	Visualiza	;Visualiza la hora actual
		btfss	INTCON,T0IF	;Ha rabasado el TMR0 ??
		goto	Loop		;No

;Se espera que el TMR0 rebase 10 veces para un delay de 0.5 s. Entonces se lee el estado
;de RA0 y RA1. Si alguno estuviera activado, se realiza el ajuste de los minutos y las horas
;respectivamente.

		bcf	INTCON,T0IF	;Restaura el flag del TMR0
		movlw	~.195
		movwf	TMR0		;Reponer el TMR0
		decfsz	Delay,F		;Se han provocado 10 rebasamientos de 50mS (0.5s.) ?
		goto	Loop		;Todavía no

		movlw	.10
		movwf	Delay		;Reponer variable de temporización con el valor 10
		incf	DP_Status,F	;Cambia el estado del flag del punto decimal DP,
					;para provocar el efecto de segundero
		btfsc	PORTA,0		;Ajuste de minutos en On ?
		goto	Inc_Min		;Si, incrementa los minutos
		btfsc	PORTA,1		;Ajuste de horas en On ??
		goto	Inc_Horas	;Si, incrementa las horas
		goto	Loop		;No, no hay ningún ajuste.

;Incrementa los minutos en una unidad y ajusta a 0 al llegar a 60

Inc_Min		movf	Minutos,W
		call	Inc_BCD		;Incrementa en BCD
		movwf	Minutos
		movlw	0x60
		subwf	Minutos,W
		btfss	STATUS,Z	;Han llegado a 60 ??
		goto	Inc_Min_1	;No
		clrf	Minutos		;Si, ajuste a 0
Inc_Min_1	call	Esc_Clock	;Escribe sobre el dispositivo PCF8583	
		goto	Loop

;Incrementa las horas en una unidad y ajusta a 0 cuando lleguen a 24 h

Inc_Horas	movf	Horas,W
		call	Inc_BCD		;Incrementa en BCD
		movwf	Horas
		movlw	0x24
		subwf	Horas,W
		btfss	STATUS,Z	;Han llegado a 24h
		goto	Inc_Horas_1 	;No
		clrf	Horas		;Si,ajuste a 0h
Inc_Horas_1	call	Esc_Clock	;Escribe sobre el dispositivo PCF8583
		goto	Loop

		end			;Fin del programa fuente
