;
;					EJEMPLO 36
;
;				Autor: Mikel Etxebarria
;			(c) Microsystems Engineering (Bilbao)
;
;Ejemplo para MicroPIC Trainer, Micro'PIC Trainer Plus y Micro'PIC I/O
;
;El módulo MSSP. Modo I2C Master. Termómetro 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.
;
;El dispositivo I2C SAA1064. Controlador de displays de 7 segmentos
;
;Es un ejemplo similar al 4.25. El sensor de tª de la tarjeta Micro'PIC I/O proporciona una 
;tensión analógica de 10mV/ºC, que se aplica al canal ADC RA5/AN4. El ADC trabaja con una 
;tensión de referencia externa de 2.5V que se obtiene desde la tarjeta Micro'PIC I/O y se 
;aplica en RA3/Vref. Aproximadamente se toma una muestra de la temperatura actúal cada 
;segundo. La visualización se realiza sobre los display gobernados por el dispositivo I2C
;SAA1062 de la Micro'PIC Trainer.

		List	p=16F873	;Tipo de procesador
		include	"P16F873.INC"	;Definiciones de registros internos

DatoA_H		equ	0x20
DatoA_L		equ	0x21		;Multiplicando de 16 bits
DatoB_H		equ	0x22
DatoB_L		equ	0x23		;Multiplicador de 16 bits
DatoD_H		equ	0x24
DatoD_L		equ	0x25
DatoC_H		equ	0x26
DatoC_L		equ	0x27		;Resultado de 32 bits
Contador	equ	0x28		;Variable contadora
BCD_2		equ	0x29
BCD_1		equ	0x2a
BCD_0		equ	0x2b		;Variables BCD
Temporal	equ	0x2c		;Variable temporal
Delay		equ	0x2d		;Variable de temporización

		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

;******************************************************************************************
;Mul16x16: Esta rutina multiplica sin signo dos números de 16 bits (DatoB_H:DatoB_L * 
;DatoA_H:DatoA_L) el resultado de 32 bits se deposita en (DatoD_H:DatoD_L y DatoC_H:DatoC_L)

Mul16x16	movlw	.16
		movwf	Contador	;Inicia contador
		clrf	DatoD_H
		clrf	DatoD_L		;Borra parte alta del resultado

Mul_Loop	rrf	DatoB_H,F
		rrf	DatoB_L,F
		btfss	STATUS,C	;El bit LSB es "1" ?
		goto	No_suma		;No
		movf	DatoA_L,W
		addwf	DatoD_L,F	;Suma octeto de menos peso
		btfsc	STATUS,C
		incf	DatoD_H,F
		movf	DatoA_H,W
		addwf	DatoD_H,F	;Suma octeto de más peso
No_suma		rrf	DatoD_H,F
		rrf	DatoD_L,F
		rrf	DatoC_H,F
		rrf	DatoC_L,F
		decfsz	Contador,F
		goto	Mul_Loop
		return

;****************************************************************************************
;16Bits_BCD: Esta rutina convierte un número binario de 16 bits situado en DatoC_H y
;DatoC_L en 5 dígitos BCD que se depositan en las variables BCD_0, BCD_1 y BCD_2, siendo 
;esta última la de menos peso. Está presentada en la nota de aplicación AN544 de MICROCHIP 
;y adaptada por Microsystems Engineering

Bits16_BCD	bcf	STATUS,C
		clrf	Contador	
		bsf	Contador,4	;Carga el contador con 16		
		clrf	BCD_0
		clrf	BCD_1
		clrf	BCD_2		;Puesta a 0 inicial

Loop_16		rlf	DatoC_L,F
		rlf	DatoC_H,F
		rlf	BCD_2,F
		rlf	BCD_1,F
		rlf	BCD_0,F		;Desplaza a izda. (multiplica por 2)
		decfsz	Contador,F
		goto	Ajuste
		return

Ajuste		movlw	BCD_2
		movwf	FSR		;Inicia el índice
		call	Ajuste_BCD	;Ajusta el primer byte
		incf	FSR,F
		call	Ajuste_BCD	;Ajusta el segundo byte
		incf	FSR,F
		call	Ajuste_BCD
		goto	Loop_16

Ajuste_BCD	movf	INDF,W		
		addlw	0x03
		movwf	Temporal	
		btfsc	Temporal,3	;Mayor de 7 el nibble de menos peso ??
		movwf	INDF		;Si, lo acumula
		movf	INDF,W		
		addlw	0x30
		movwf	Temporal
		btfsc	Temporal,7	;Mayor de 7 el nibble de menos peso ??
		movwf	INDF		;Si, lo acumula
		return

;********************************************************************************************
;Visualiza: Recoge el valor actual de las variables BCD_0 y BCD_1, 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	BCD_0,W
		andlw	0x0f		;Coge nibble de más peso de BCD_0
		call	BCD_7seg	;Convierte a 7 segmentos
		call	Send_Byte	;Visualiza sobre el dígito D1
		movlw	0x0f
		andwf	BCD_0,W		;Coge nibble de menos peso de BCD_0
		call	BCD_7seg	;Convierte a 7 segmentos
		call	Send_Byte	;Visualiza sobre el dígito D2
		swapf	BCD_1,W
		andlw	0x0f		;Coge nibble de más peso de BCD_1
		call	BCD_7seg	;Convierte a 7 segmentos
		iorlw	b'10000000'	;Activa punto decimal en D3
		call	Send_Byte	;Visualiza sobre el dígito D3
		movlw	0x0f
		andwf	BCD_1,W		;Coge nibble de menos peso de BCD_1
		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
		bsf	STATUS,RP0	;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

;*******************************************************************************************
;Programa principal

Inicio	     	clrf	PORTA
		clrf	PORTC		
		bsf	STATUS,RP0	;Selecciona banco 1
		movlw	b'10000001'	;Vref+=RA3 (se propone 2.5V)
		movwf	ADCON1		;Justificación dcha. RA5/AN4 entrada analógica
		movlw	b'11111111'
		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

;ADC en On con frecuencia de Fosc/32 y selección del canal RA5/AN4

		movlw	b'10100001'
		movwf	ADCON0		;ADC en On

;Temporizar 1 segundo mediante el TMR0. Este, con un preescaler de 256, evoluciona cada
;256uS. Se carga con 195 para realizar una temporización de 50mS. Los 50 mS se repiten 20 
;veces para conseguir un segundo. Seguidamente se toma una muestra de la temperatura.

Loop		movlw	.20
		movwf	Delay
Delay_1		movlw	~.195
		movwf	TMR0		;Inicia el TMR0
		bcf	INTCON,T0IF	;Restaura el flag del TMR0
Delay_2		clrwdt
		btfss	INTCON,T0IF	;Han pasado 50 mS ??
		goto	Delay_2		;Todavía no
		decfsz	Delay,F		;Se ha repetido 20 veces ??
		goto	Delay_1		;No

		bcf	PIR1,ADIF	;Restaurar el flag del ADC
		bsf	ADCON0,GO	;Inicio de conversión
ADC_Wait	btfss	PIR1,ADIF	;Fin de conversión ??
		goto	ADC_Wait	;Todavía no, esperar

;Al resultado de la conversión se le multiplica por 243 (2.43) que es la resolución por bit.
;P.Ej. La Tª es de 25.6º, como el sensor proporciona 10mV/ºC la tensión a convertir será de
;256 mV. Siendo la resolución de 2.43mV/bit el resultado de la conversión dará 0x69 (105)
;que, multiplicado por 0xf3 (243) queda un resultado de 0x63ab (25515). Se desplaza la coma 
;tres posiciones a la izda. y queda finalmente 25.51º (un error de +/- 1 bit)

		bsf	STATUS,RP0	;Banco 1
		movf	ADRESL,W
		bcf	STATUS,RP0
		movwf	DatoB_L
		movf	ADRESH,W
		movwf	DatoB_H		;Carga el multiplicando (valor de conversión)
		movlw	.243
		movwf	DatoA_L
		clrf	DatoA_H		;Carga el multiplicador (243)

		call	Mul16x16	;Multiplica
		call	Bits16_BCD	;Convierte a BCD

		call	Visualiza	;Visualiza sobre los displays de la Trainer PLUS

		goto	Loop

		end			;Fin del programa fuente
