;
;					EJEMPLO 43
;
;				Autor: Mikel Etxebarria
;			(c) Microsystems Engineering (Bilbao)
;
;Ejemplo para MicroPIC Trainer, Micro'PIC Trainer Plus y Micro'PIC I/O
;
;La memoria EEPROM de datos. Control de acceso mediante clave secreta
;
;Este ejemplo de carácter didáctico pretende mostrar una aplicación de la memoria EEPROM de
;datos de los dispositivos PIC16F87X. Consiste en un control de acceso. Por el terminal, 
;el usuario pulsa A de apertura e introduce una clave de 4 caracteres. Si es correcta
;se activa el relé 1 de la tarjeta Micro'PIC I/O conectado a RB0, durante 1 s. Simula la 
;apertura. Cada pulsación va precedida de un "beep" generado por el piezoeléctrico de 
;Micro'PIC I/O conectado a RB1.
;
;La tecla C permite realiza el cambio de clave pidiendo una nueva y la correspondiente
;confirmación. La pantalla del terminal irá presentando los oportunos mensajes.
;
;En la memoria EEPROM de datos quedará registrada de forma no volátil la clave actual.

		List	p=16F873	;Tipo de procesador
		include	"P16F873.INC"	;Definiciones de registros internos

Tiempo		equ	0x20		;Variable de temporización
Caracter	equ	0x21		;Variable donde se almacena el carácter recibido
Contador	equ	0x22		;Variable contadora
Rc_Buffer_1	equ	0x23
Rc_Buffer_2	equ	0x24
Rc_Buffer_3	equ	0x25
Rc_Buffer_4	equ	0x26		;Variables para la recepción de la clave
Temporal_1	equ	0x27
Temporal_2	equ	0x28		;Temporales de propósito general
EE_Dato		equ	0x29		;Dato escrito/leído en EEPROM de datos
EE_Dir		equ	0x2a		;Dirección actual en la EEPROM de datos
Flag_Ok		equ	0x2b		;Representa la validez de la clave

		org	0x00		;Vector de Reset
		goto	Inicio

		org	0x05		;Salva el vector de interrupción

;******************************************************************************************
;Según el valor contenido en el registro W, se devuelve el carácter a visualizar

Tabla		movwf	PCL		;Calcula el desplazamiento sobre la tabla

;***********************************************************************************
;La directiva dt genera tantas intsrucciones retlw como bytes o caracteres contenga

Mens_1		equ	$
		dt	"(A) Apertura (C) Cambio de Clave      ",0x0d,0x00

Mens_2		equ	$
		dt	"(C) Cancelar: Clave ?                 ",0x00

Mens_3		equ	$
		dt	"(C) Cancelar: Nueva Clave ?           ",0x00

Mens_4		equ	$
		dt	"** ACCESO CONFIRMADO **               ",0x0d,0x00

Mens_5		equ	$
		dt	"** ACCESO DENEGADO **                 ",0x0d,0x00

;********************************************************************************************
;Test: Comprobar que la clave recién introducida coincide con la grabada en la EEPROM

Test		clrf	EE_Dir		;Dirección EEPROM inicial
		movlw	Rc_Buffer_1
		movwf	FSR		;Inicia puntero del buffer de recepción
		movlw	4
		movwf	Contador	;Nº de bytes a comprobar
Test_1		call	EE_Read
		movf	EE_Dato,W	;Lee byte de la EEPROM
		subwf	INDF,W		;Compara con el recibido en el buffer
		btfss	STATUS,Z	;Es igual ??
		goto	Test_Error	;No, error
		incf	EE_Dir,F	;Siguiente dirección  de EEPROM a comparar
		incf	FSR,F		;Siguiente byte del buffer a comparar
		decfsz	Contador,F	;Se han comparado los cuatro bytes ??
		goto	Test_1
		bsf	Flag_Ok,0	;Activa flag de clave OK
		return
Test_Error	movlw	Mens_5
		call	Mensaje		;Visualiza ACCESO DENEGADO
		movlw	.40
		call	Delay		;Espera 2 segundos
		bcf	Flag_Ok,0	;Desactiva flag de clave OK
		return

;******************************************************************************************
;EE_Read: Lee un byte de la EEPROM de datos, en su dirección actual, y lo devuelve en la
;variable EE_Dato

EE_Read		movf	EE_Dir,W
		bsf	STATUS,RP1	;Selecciona banco 1
		movwf	EEADR		;Escribe la dirección actual
		bsf	STATUS,RP0	;Selecciona banco 3
		bcf	EECON1,EEPGD	;Selecciona memoria EEPROM de datos
		bsf	EECON1,RD	;Activa modo lectura
		bcf	STATUS,RP0	;Selecciona banco 2
		movf	EEDATA,W	;Lee el byte
		bcf	STATUS,RP1	;Selecciona banco 0
		movwf	EE_Dato		;Salva el dato leído
		return

;******************************************************************************************
;EE_Write: Graba un byte en la dirección actual de la EEPROM de datos. El byte a grabar
;está presente en la variable EE_Dato

EE_Write	bcf	PIR2,EEIF	;Restaura el flag de la EEPROM
		movf	EE_Dato,W
		bsf	STATUS,RP1	;Selecciona el banco 2
		movwf	EEDATA		;Dato a grabar en la EEPROM
		bcf	STATUS,RP1	;Selecciona el banco 0
		movf	EE_Dir,W
		bsf	STATUS,RP1	;Selecciona el banco 2
		movwf	EEADR		;Pone la direción actual		
		bsf	STATUS,RP0	;Selecciona el banco 3
		bcf	EECON1,EEPGD	;Selecciona EEPROM de datos
		bsf	EECON1,2	;Habilita la escritura
		movlw	0x55
		movwf	EECON2
		movlw	0xaa
		movwf	EECON2		;Secuencia descrita por Microchip
		bsf	EECON1,WR	;Inicio de escritura
		bcf	STATUS,RP0
		bcf	STATUS,RP1	;Selecciona banco 0
EE_Write_Wait	btfss	PIR2,EEIF	;Fin del ciclo de escritura ??
		goto	EE_Write_Wait
		return

;******************************************************************************************
;Leer_Clave: Lee los cuatro dígitos procedentes del terminal y correspondientes a la clave.
;Los almacena en el Buffer. La tecla C permite cancelar.

Leer_Clave	movlw	Rc_Buffer_1
		movwf	FSR		;Inicia el buffer de recepción
		movlw	4
		movwf	Contador	;Nº de caracteres a recibir
Leer_Clave_1	call	Rc_Dato		;Espera a recibir un carácter
		movlw	'C'
		subwf	Caracter,W
		btfsc	STATUS,Z	;Ha sido la C de cancelar ??
		goto	Cancelar	;Si, cancelar la recepción de caracteres
		movf	Caracter,W
		movwf	INDF		;Almacena carácter
		incf	FSR,F		;Siguiente posición en el buffer de recepción
		decfsz	Contador,F	;Se ha recibido 4 caracteres ??
		goto	Leer_Clave_1	;No
		movlw	0x0d
		call	Tx_Dato		;Envía CR
Cancelar	return			;Si


;*****************************************************************************************
;Key_Off: Genera un Beep de 0.25 s cada vez que se recibe una pulsación desde el terminal
;
Key_Off		bcf	RCSTA,SPEN	;USART en Off
		bsf	PORTB,1		;Activa el piezoeléctrico
		movlw	5
		call	Delay		;Temporiza 0.25 s
		bcf	PORTB,1		;Desactiva piezoeléctrico
		bsf	RCSTA,SPEN	;USART en On
		return

;*************************************************************************************
;Mensaje: Esta rutina envía vía serie RS232 el mensaje cuyo inicio está  indicado en
;el acumulador. El fin de un mensaje se determina mediante el código 0x00

Mensaje         movwf   Temporal_1      ;Salva posición de la tabla
Mensaje_1       movf    Temporal_1,W    ;Recupera posición de la tabla
                call    Tabla		;Busca caracter de salida
                movwf   Temporal_2      ;Guarda el caracter
                movf    Temporal_2,F
                btfss   STATUS,Z        ;Mira si es el último
                goto    No_es_ultimo
                return
No_es_ultimo   	call    Tx_Dato		;Transmite el carácter
                incf    Temporal_1,F    ;Siguiente caracter
                goto    Mensaje_1

;*****************************************************************************************
;Rc_Dato: Espera a recibir un carácter procedente del terminal. Lo almacena en la variable
;Caracter
;
Rc_Dato		movf	RCREG,W		;Limpia el registro receptor
		bcf	PIR1,RCIF	;Restaura el flag del receptor
Leer_Car_Wait	clrwdt
		btfss	PIR1,RCIF	;Se ha recibido un carácter ??
		goto	Leer_Car_Wait	;Todavía no, esperar
		movf	RCREG,W		;Si, leer el carácter
		movwf	Caracter	;Salvarlo
		call	Key_Off		;Genera un "Beep"
		return

;********************************************************************************************
;Tx_Dato: Transmite vía serie el dato presente en el reg. W

Tx_Dato		bcf	PIR1,TXIF	;Restaura el flag del transmisor
		movwf	TXREG		;Almacena el byte a transmitir
		bsf	STATUS,RP0
Tx_Dato_Wait	btfss	TXSTA,TRMT	;Byte transmitido ??
		goto	Tx_Dato_Wait	;No, esperar
		bcf	STATUS,RP0
		clrwdt
		return			;Si

;*****************************************************************************************
;Delay: 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 Tiempo para 
;obtener la temporización final deseada.

Delay		movwf	Tiempo
Wait_2		bcf	INTCON,T0IF	;Restaura el flag del TMR0
		movlw	~.195
		movwf	TMR0		;Reponer el TMR0 para 50mS de temporización
Wait_1		clrwdt			;Refrescar el WDT
		btfss	INTCON,T0IF	;Han pasado 50 mS ??
		goto	Wait_1		;Todavía no
		decfsz	Tiempo,F	;Se ha repitido las veces que indica Tiempo ?
		goto	Wait_2		;Todavía no
		return

;*******************************************************************************************
;Programa principal

Inicio	     	clrwdt
		clrf	PORTC		
		clrf	PORTB
		bsf	STATUS,RP0	;Selecciona banco 1
		clrf	PORTB		;Puerta B salida
		movlw	b'10111111'
		movwf	TRISC		;RC7/Rx entrada, RC6/Tx salida
		movlw	b'11000111'
		movwf	OPTION_REG	;Preescaler de 256 asociado al TMR0

		movlw	b'00100100'	
		movwf	TXSTA		;TX en On, modo asíncrono con 8 bits y alta velocidad
		movlw	.25
		movwf	SPBRG		;9600 baudios con Fosc=4MHz
		bsf	PIE1,RCIE	;Habilita interrupción en la recepción
		bcf	STATUS,RP0	;Selecciona banco 0
		
		movlw	b'10010000'
		movwf	RCSTA		;USART en On, recepción contínua

;Se hace necesario determinar si la EEPROM ha sido usada alguna vez. En caso de ser virgen se
;le carga la clave inicial 1234.

		clrf	EE_Dir		;Dirección inicial
		call	EE_Read		;Lee el primer byte
		movlw	0xff		;Comparando el primer byte con 0xff se determina si
		subwf	EE_Dato,W	;a sido usada alguna vez
		btfss	STATUS,Z	;Es virgen ??
		goto	Loop		;No

		movlw	'1'		;Si, cargar clave inicial 1234 en las 4 primeras po-
		movwf	EE_Dato		;siciones de la memoria EEPROM de datos
		clrf	EE_Dir		;Selecciona dirección inicial en EEPROM
		movlw	4
		movwf	Contador	;Nº de bytes a grabar
Inicio_EEPROM	call	EE_Write	;Escribe un byte
		incf	EE_Dato,F	;Siguiente byte a grabar
		incf	EE_Dir,F	;Siguiente drección EEPROM
		decfsz	Contador,F	;Se han escrito los 4 bytes de la clave ??
		goto	Inicio_EEPROM	;No

Loop		clrwdt
		clrf	Flag_Ok		;Desactiva flag de clave OK
		movlw	Mens_1
		call	Mensaje		;Visualiza mensaje principal

		call	Rc_Dato		;Espera recibir un dato
		movlw	'A'
		subwf	Caracter,W
		btfsc	STATUS,Z	;Ha sido A de Apertura ??
		goto	Apertura	;Si
		movlw	'C'
		subwf	Caracter,W	
		btfsc	STATUS,Z	;Ha sido C de Cambio de clave ??
		goto	Cambio		;Si
		nop
		nop
		goto	Loop

Apertura	movlw	Mens_2
		call	Mensaje		;Visualiza mensaje pidiendo la clave
		call	Leer_Clave	;Leer la clave
		movlw	'C'
		subwf	Caracter,W
		btfss	STATUS,Z	;Ha sido C de Cancelar
		goto	Apertura_1	;No
		movlw	0x0d
		call	Tx_Dato		;Si, envía CR
		goto	Loop

Apertura_1	call	Test		;Verifica si la clave es correcta
		btfss	Flag_Ok,0	;Lo es ??
		goto	Loop		;No
		movlw	Mens_4		;Si
		call	Mensaje		;Visualiza ACCESO CONFIRMADO
		bsf	PORTB,0		;Activa relé de apertura
		movlw	.20
		call	Delay		;Espera 1 segundo
		bcf	PORTB,0		;Desactiva relé de apertura
		goto	Loop
		
Cambio		movlw	Mens_2
		call	Mensaje		;Visualiza pidiendo la clave
		call	Leer_Clave	;Leer la clave
		movlw	'C'
		subwf	Caracter,W
		btfss	STATUS,Z	;Ha sido C de Cancelar
		goto	Cambio_1	;No
		movlw	0x0d
		call	Tx_Dato		;Si, envía CR
		goto	Loop

Cambio_1	call	Test		;Verifica si es la correcta actual
		btfss	Flag_Ok,0	;Es la correcta ??
		goto	Loop		;No
		movlw	Mens_3		;Si
		call	Mensaje		;Pide nueva clave
		call	Leer_Clave	;Leer la clave
		movlw	'C'
		subwf	Caracter,W
		btfss	STATUS,Z	;Ha sido C de Cancelar
		goto	Cambio_2	;No
		movlw	0x0d
		call	Tx_Dato		;Si, envía CR
		goto	Loop

;Graba la nueva clave en la EEPROM

Cambio_2	clrf	EE_Dir		;Dirección inicial de EEPROM
		movlw	Rc_Buffer_1
		movwf	FSR		;Primer byte del buffer
		movlw	4
		movwf	Contador	;Nº de bytes a grabar en EEPROM
Cambio_2_1	movf	INDF,W
		movwf	EE_Dato		;Lee byte a grabar
		call	EE_Write	;Graba un byte
		incf	EE_Dir,F	;Siguiente dirección de EEPROM
		incf	FSR,F		;Siguiente byte del buffer
		decfsz	Contador,F	;Se han grabado los 4 bytes ??
		goto	Cambio_2_1	;No
		goto	Loop		;Si

		end			;Fin del programa fuente
