;
;					EJEMPLO 24
;
;				Autor: Mikel Etxebarria
;			(c) Microsystems Engineering (Bilbao)
;
;Ejemplo MicroPIC Trainer y Micro'PIC I/O
;
;El módulo conversor ADC. Variando la velocidad de un motor. Medir las rpm
;
;Los dispositivos PIC16F87X disponen de un convertidor A/D de 10 bits de resolución y 5 u 8 
;canales de entrada analógica. La tensión de referencia determina la resolución por bit:
;(Res. = Vref/1024). Con 5 Vref=4.8 mV/Bit; con 2.5 Vref=2.4 mV/Bit
;
;Se propone la regulación de velocidad del motor CC de la tarjeta Micro'PIC I/O mediante
;PWM. Para ello, el potenciómetro AN3 de la Micro'PIC Trainer conectado a la línea RA3/AN3,
;determina la duración de la anchura del pulso. El valor de dicho potenciómetro es leído
;cada 50 mS.
;
;La salida PWM en RC2/CCP1 se conecta a la señal Enable del motor. RC7 y RC6 determinan el
;sentido de giro. La salida del encoder se conecta a RC0/T1CKI para contar el número de
;pasos dado por el motor, durante un lapsus de 1 segundo.
;
;En la pantalla LCD se visualizará las rpm a las que gira el motor.

		List	p=16F873	;Tipo de procesador
		include	"P16F873.INC"	;Definiciones de registros internos

Lcd_var		equ	0x20		;Variables para las rutinas del LCD
Duty_H		equ	0x22
Duty_L		equ	0x23		;Variable para la anchura de pulso
DatoA_H		equ	0x24
DatoA_L		equ	0x25		;Multiplicando de 16 bits
DatoB_H		equ	0x26
DatoB_L		equ	0x27		;Multiplicador de 16 bits
DatoD_H		equ	0x28
DatoD_L		equ	0x29
DatoC_H		equ	0x2A
DatoC_L		equ	0x2B		;Resultado de 32 bits
Contador	equ	0x2c		;Variable contadora
BCD_2		equ	0x2d
BCD_1		equ	0x2e
BCD_0		equ	0x2f		;Variables BCD
Temporal	equ	0x30		;Variable temporal
Delay		equ	0x31		;Variable de temporización

		org	0x00		;Vector de Reset
		goto	Inicio

		org	0x05		;Salva el vector de interrupción
		
		include "LCD_CXX.INC"	;Incluye rutinas de manejo del LCD

;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

;Visualizar: Visualiza sobre la pantalla LCD los cinco dígitos situados en las variables
;BCD_0, BC_1 y BCD_2, seguido de la inscripción "rpm"

Visualizar	movlw	0x80
		call	LCD_REG		;Posiciona el cursor
		movlw	3
		movwf	Contador	;Inicia contador de bytes a convertir
		movlw	BCD_0
		movwf	FSR		;Inicia puntero índice
Visual_loop	swapf	INDF,W
		andlw	0x0f
		iorlw	0x30		;Convierte a ASCII el nible de más peso
		call	LCD_DATO	;Lo visualiza
		movf	INDF,W
		andlw	0x0f
		iorlw	0x30		;Convierte a ASCII el nible de menos peso
		call	LCD_DATO	;Lo visualiza
		decf	FSR,F		;Siguiente byte
		decfsz	Contador,F
		goto	Visual_loop
		movlw	' '
		call	LCD_DATO
		movlw	'r'
		call	LCD_DATO
		movlw	'p'
		call	LCD_DATO
		movlw	'm'
		call	LCD_DATO	;Visualiza "rpm"
		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

;ADC: Se activa el ADC y se selecciona el canal RA3/AN3. Frecuencia de trabajo Fosc/32.
;Finalizada la conversión, se actualiza el valor de la anchura de pulso

ADC		bsf	STATUS,RP0	;Selecciona banco 1
		clrf	ADCON1		;Puerta A analógica
		bcf	STATUS,RP0	;Selecciona banco 0
		movlw	b'10011001'
		movwf	ADCON0		;Módulo ADC en On
		bcf	PIR1,ADIF	;Restaurar el flag del ADC
		nop
		bsf	ADCON0,GO	;Inicio de conversión
ADC_Wait	btfss	PIR1,ADIF	;Fin de conversión ??
		goto	ADC_Wait	;Todavía no, esperar
		movf	ADRESH,W
		movwf	CCPR1L		;Carga los 8 bits de más peso para el PWM
		bsf	STATUS,RP0	;Selecciona banco 1
		rrf	ADRESL,F
		rrf	ADRESL,W
		
;El módulo CCP1 actúa en el modo PWM con salida por RC2/CCP1.

		andlw	b'00110000'
		iorlw	b'00001100'
		bcf	STATUS,RP0	;Selecciona banco 0
		movwf	CCP1CON		;CCP1 en modo PWM
		return

;Programa principal

Inicio	        clrf	PORTA
		clrf	PORTB
		clrf 	PORTC
		bsf	STATUS,RP0	;Selecciona banco 1
		movlw	b'00000110'
		movwf	ADCON1		;Puerta A digital
		clrf	TRISB		;Puerta B se configura como salida
		clrf	TRISA		;RA4-RA0 salidas
		movlw	b'00111011'
		movwf	TRISC		;RC2/CCP1, RC6 y RC7 salidas, RC0/T1CKI entrada
		movlw	b'11000111'
		movwf	OPTION_REG	;Preescaler de 256 asociado al TMR0
		movlw	.250
		movwf	PR2		;Periodo del PWM de 4 mS
		bcf	STATUS,RP0	;Selecciona banco 0

		call	UP_LCD		;Configuración de E/S para el LCD
		call	LCD_INI		;Inicialización del LCD
		movlw	b'00001100'
		call	LCD_REG		;LCD On, cursor y Blink Off

;El TMR2 emplea un preescaler de 1:16. Evoluciona cada 16 uS

		call	ADC		;Conversión y ajuste PWM inicial
		movlw	b'00000110'
		movwf	T2CON		;TMR2 en On

;El TMR1 actua como contador externo con entrada por RC0/T1CKI y preescaler 1:1

Loop		movlw	b'00000010'
		movwf	T1CON		;TMR1 en Off
		clrf	TMR1H
		clrf	TMR1L		;Inicia el TMR1

		bsf	PORTC,6		;Motor en On, giro horario
		bsf	T1CON,TMR1ON	;TMR1 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. Transcurridos esos 50mS
;se lee el valor del potenciómetro RA3/AN3 para actualizar el PWM. Los 50 mS se repiten 20 
;veces para conseguir un segundo. Transcurrido este lapsus se captura el contenido del TMR1. 

		movlw	.20
		movwf	Delay
Delay_1		movlw	~.195
		movwf	TMR0		;Inicia el TMR0
		bcf	INTCON,T0IF	;Restaura el flag del TMR0
		call	ADC		;Conversión y ajuste PWM
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	T1CON,TMR1ON	;Desconecta el TMR1
		
;Si el encoder empleado dispone de 4 ranuras, al valor capturado se divide entre 4 para
;determinar el nº de vueltas por segundo y se multiplica por 60 para obtener el nº de vueltas
;por minuto. Ello es equivalente a multiplicar el valor del TMR1 por 15. Se puede apreciar 
;error de +/- 15 rpm

		clrf	DatoA_H
		movlw	.15
		movwf	DatoA_L		;Inicia el multiplicador
		movf	TMR1H,W
		movwf	DatoB_H
		movf	TMR1L,W
		movwf	DatoB_L		;Carga el multiplicando
		call	Mul16x16	;Multiplica 2 números de 16 bits
		call	Bits16_BCD	;Convierte a BCD el resultado

;Visualiza el resultado sobre la pantalla LCD. La pantalla LCD se actualiza por tanto a cada
;segundo
		
		bsf	STATUS,RP0	;Selecciona banco 1
		movlw	b'00000110'
		movwf	ADCON1		;Puerta A digital
		bcf	STATUS,RP0	;Selecciona banco 0
		call	UP_LCD		;Reconfigura puertos para el LCD
		call	Visualizar	;Visualiza sobre pantalla
		goto	Loop

		end			;Fin del programa fuente
