;****************************************************************************************************
;	Scansione Passiva di una Tastiera a Matrice
;	di Antonio Tricarico - Markantonio
;	http://www.markantonio.com
;
;	File di esempio di utilizzo di una tastiera a matrice senza la classica scansione
;	Il presente algoritmo pone tutte le colonne (PortB4-7) come uscite attive (basse)
;	restando in attesa di una variazione sulle righe (PortB0-3).
;	A questo punto esegue na procedura antirimbalzo e di verifica, e calcola, da una
;	singola lettura dell'intero port, su quale riga si trova il pulsante premuto.
;	Per stabilire anche quale sia la colonna, scambia ingressi ed uscite (le colonne
;	diventano ingressi e le righe uscite) e legge nuovamente tutto il Port calcolando
;	da questa singola lettura anche il numero di riga interessato.
;	Dall'unione delle due informazioni calcola il pulsante premuto, il cui valore
;	(4 bit) viene inviato al PortA per essere utilizzato o visualizzato su un display
;	esadecimale tipo il TIL311 della Texas Instruments o quattro LED.
;	Durante l'attesa della pressione di un tasto il micro entra in modalità basso
;	consumo spegnendo l'oscillatore e consumando 0.2uA.
;****************************************************************************************************

list	p=16F84,	f=inhx8m

_CP_ON		EQU	H'000F'
_CP_OFF		EQU	H'3FFF'
_PWRTE_ON	EQU	H'3FF7'
_PWRTE_OFF	EQU	H'3FFF'
_WDT_ON		EQU	H'3FFF'
_WDT_OFF	EQU	H'3FFB'
_LP_OSC		EQU	H'3FFC'
_XT_OSC		EQU	H'3FFD'
_HS_OSC		EQU	H'3FFE'
_RC_OSC		EQU	H'3FFF'

__CONFIG	_XT_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF

;*****************************************************************************************
;**************	Registri Funzioni Speciali ***********************************************
;*****************************************************************************************
INDF	EQU	00
TMR0	EQU	01
PCL	EQU	02
STATUS	EQU	03
FSR	EQU	04
PORTA	EQU	05
PORTB	EQU	06

INTCON	EQU	0Bh
OPTREG	EQU	081h	;REGISTRO OPZIONI



;*****************************************************************************************
;**************	Bit di Stato *************************************************************
;*****************************************************************************************
#DEFINE	CARRY	STATUS,0
#DEFINE	DCARRY	STATUS,1
#DEFINE	ZERO	STATUS,2
;#DEFINE	SLEEPON	STATUS,3
;#DEFINE	TRISBIT	STATUS,4
#DEFINE	RP0	STATUS,5
#DEFINE	RP1	STATUS,6
#DEFINE	IRP	STATUS,7




;*****************************************************************************************
;**************	Bit configurazione Interruzioni ******************************************
;*****************************************************************************************
#DEFINE	GIE	INTCON,7
#DEFINE	EEIE	INTCON,6	;Abilitazione Interrupt EEPROM
#DEFINE	TOIE	INTCON,5	;Abilitazione Interrupt Timer0
#DEFINE	INTB0	INTCON,4	;Abilitazione Interrupt PortB0
#DEFINE	RBIE	INTCON,3	;Abilitazione Interrupt su variazione PortB4..7
#DEFINE	RILTMR	INTCON,2	;Rilevato overflow su Timer0
#DEFINE	RILB0	INTCON,1	;Rilevato PortB0
#DEFINE	RBIF	INTCON,0	;Rilevata variazione su PortB4..7


;*****************************************************************************************
;**************	Ridenominazione Istruzioni ***********************************************
;*****************************************************************************************
#define	EXECSET	BTFSC
#define	EXECCLR	BTFSS


;*****************************************************************************************
;**************	V  A  R  I  A  B  I  L  I ************************************************
;*****************************************************************************************
DATO	EQU	0C		;Valore tasto premuto
VDATO	EQU	DATO+01h
CONT1	EQU	DATO+02h
;	EQU	DATO+0h




;*****************************************************************************************
;**************	Inizio memoria programma	******************************************
;*****************************************************************************************
INIZ	ORG	0000H
	CLRF	STATUS
	CLRF	INTCON
	GOTO	MAIN


;*****************************************************************************************
;**************	Gestore Interruzioni *****************************************************
;*****************************************************************************************
	ORG	0004h
	BCF	RBIE	;Disabilito le interruzioni sui pin PortB4-7
	RETFIE		;RetFIE riabilita il GIE.






;*****************************************************************************************
;**************	Inizio Programma	**************************************************
;*****************************************************************************************
MAIN
	MOVLW	b'11110000'	;PortB Alto come ingresso (righe) e basso come uscita (colonne)
	BSF	RP0	;/BANCO\
	MOVWF	PORTB	;Solo i pin PortB4-PortB7 possono generare interrupt e risvegliare il micro.
	CLRF	PORTA
	CLRF	OPTREG	;Abilito il Pull-Up per il PortB ed assegno al timer il clock interno.
	BCF	RP0	;\BANCO/
	CLRF	PORTB	;Azzerando tutto il PortB porto a livello basso
			;le sue quattro uscite, relative alle colonne.
	;Inizializzazioni varie ed eventuali
	;Inizializzazioni varie ed eventuali
	;Inizializzazioni varie ed eventuali

;*****************************************************************************************
;**************	Inizio Ciclo	**********************************************************
;*****************************************************************************************
CICLO	MOVF	PORTB,F	;Serve per poter disabilitare il flag delle interruzioni su PortB
	BCF	RBIF	;Disabilito il Flag Interruzioni PortB4-7
	BSF	RBIE	;Abilito Interrupt su variazione PortB4-7
	BSF	GIE


;*****************************************************************************************
;**************	Sospensione del Micro ****************************************************
;*****************************************************************************************
DORMI	SLEEP	;Spengo l'oscillatore interno: la MCU consuma pochissimo.
		;Dopo il risveglio verrà eseguita la procedura fittizzia
		;di gestione delle interruzioni, che non farà altro che
		;ritornare all'istruzione successiva allo sleep.

;*****************************************************************************************
;**************	Dopo il risveglio	**************************************************
;*****************************************************************************************
ATTESA	MOVF	PORTB,W		;Leggo il contenuto del PortB.
				;Questo azzera il flag di interruzione.
	MOVWF	VDATO

	CLRF	CONT1	;contatore usato per verificare la stabilità del tasto premuto

;*****************************************************************************************
;**************	Antirimbalzo	**********************************************************
;*****************************************************************************************
ANTIR1	MOVF	PORTB,W	;leggo il PortB, sapendo che la parte bassa è irrilevante, dato che a zero
	XORWF	VDATO,F	;Confronto con la vecchia lettura
	EXECCLR	ZERO	;Se i dati sono diversi, a causa di un falsocontatto
	CLRF	CONT1	;azzero il contatore e ricomincio l'attesa.
	MOVWF	VDATO	;Salvo l'attuale come vecchio valore per la prox
	DECFSZ	CONT1,F	;Per poter valere zero dopo il decremento, Cont1
			;deve valere uno. Se è stato azzerato varrà zero
			;e si decrementerà a 255 ricominciando il conteggio
			;senza attivare il salto condizionato.
	GOTO	ANTIR1	;Continua regolarmente il conto alla rovescia.


;*****************************************************************************************
;**************	Controllo tasto effettivamente premuto	**********************************
;*****************************************************************************************
			;A questo punto il dato è stabile, ma non è detto che sia valido
	XORLW	0F0h	;Se i quattro ingressi sono tutti alti in W vrò 1111 0000,
	EXECSET	ZERO	;ovvero nessun tasto è premuto, ma si è trattato di un disturbo.
	GOTO	CICLO	;In tal caso ri-addormento il micro.

;	MOVWF	CONT1	;A questo punto so che un tasto è premuto, e conservo lo stato su VDATO

;*****************************************************************************************
;**************	Calcolo logaritmo di Riga	******************************************
;*****************************************************************************************
	CLRF	DATO	;Azzero Dato
	SWAPF	VDATO,F	;Scambio il nibble alto di VDATO con quello basso
			;per avere in quest'ultimo il dato relativo alla colonna.
QUALEC	RRF	VDATO,F	;Per avere il valore della colonna, devo calcolare
	EXECCLR	CARRY	;il logaritmo in base due del contenuto di VDATO shiftandolo
	GOTO	HOCOL	;finché non trovo il bit basso (che so essere tra i bit 0..3)
	INCF	DATO,F	;Fintanto incremento DATO
	GOTO	QUALEC	;e ciclo

	;Moltiplico il Dato per 4 shiftandolo 2 volte a sinistra
	;BCF	CARRY	;Il contenuto del Carry entra nel registro shiftato, ma è già zero

;*****************************************************************************************
;**************	Moltiplicazione per Quattro	******************************************
;*****************************************************************************************
HOCOL	RLF	DATO,F	;Col primo shift ciò che esce è uno Zero, quindi Carry è pronto
	RLF	DATO,F	;per il secondo shift senza necessità di azzeramento


;*****************************************************************************************
;**************	Scambio ingressi-uscite	**************************************************
;*****************************************************************************************
	MOVLW	b'00001111'	;PortB Alto come Uscite (righe) e basso come Ingressi (colonne)
	;Se non avessi i Pull-UP interni, quì dovrei abilitare il pin di controllo della
	;rete resistiva esterna per garantire il Pull-UP alle colonne quando sono ingressi.
	BSF	RP0	;/BANCO\
	MOVWF	PORTB	;Scambio ingressi ed uscite
	BCF	RP0	;\BANCO/
	CLRF	PORTB	;Porto bassi i pin del PortB che sono uscite, ovvero le righe


;*****************************************************************************************
;**************	Lettura Colonna	**********************************************************
;*****************************************************************************************
	;Leggendo la colonna non ho bisogno di un nuovo controllo antirimbalzo, il dato è già stabile
	MOVF	PORTB,W	;Ora ciò che mi serve è la sola parte bassa del PortB,
			;che so già contenere uno ZERO.

;*****************************************************************************************
;**************	Calcolo logaritmo di Colonna	******************************************
;*****************************************************************************************
	MOVWF	VDATO	;Come prima userò VDATO come variabile di Shift
			;per il calcolo deel logaritmo
QUALER	RRF	VDATO,F	;Per avere il valore della riga, devo calcolare
	EXECCLR	CARRY	;il logaritmo in base due del contenuto di VDATO shiftandolo
	GOTO	HORIG	;finché non trovo il bit basso (che so essere tra i bit 0..3)
	INCF	DATO,F	;Fintanto incremento DATO
	GOTO	QUALER	;e ciclo

;*****************************************************************************************
;**************	Reimpostazione PIN *******************************************************
;*****************************************************************************************
	MOVLW	b'11110000'	;PortB Alto come ingresso (righe) e basso come uscita (colonne)
	BSF	RP0	;/BANCO\
	MOVWF	PORTB	;Solo i pin PortB4-PortB7 possono generare interrupt e risvegliare il micro.
	BCF	RP0	;\BANCO/
	CLRF	PORTB	;Azzerando tutto il PortB porto a livello basso


;*****************************************************************************************
;**************	Fine Lettura	**********************************************************
;*****************************************************************************************
	;Ora DATO contiene il valore del tasto premuto, dove la colonna dà i due bit più
	;significativi e la riga quelli meno significativi.
HORIG	MOVF	DATO,W	;Trasferisco il valore del tasto sul PortA per poterlo
	MOVWF	PORTA	;visualizzare su un display esadecimale o dei LED.

;*****************************************************************************************
;**************	Uso dei dati (P.E. Trasmissione) *****************************************
;*****************************************************************************************
	;Quì userò il dato letto
	;istruzioni...
	;istruzioni...
	;istruzioni...
	
;*****************************************************************************************
;**************	Ritorno al ciclo d'attesa e Sleep	**********************************
;*****************************************************************************************
	GOTO	CICLO


	END
