Programació en mpasm del PIC 16F690

Referència Trucs Perifèrics   Recursos CITCEA
Tutorial Exemples Projectes   Inici

Rellotge de temps real

Per fer les funcions de rellotge de temps real, hem triat un mòdul amb el DS1307 RTC que és un rellotge en temps real (real time clock, RTC).

Rellotge de temps real

El mòdul incorpora una petita pila que manté el rellotge en funcionament quan no hi ha alimentació externa. A més de la funció RTC, aquest mòdul proporciona a l'usuari 56 bytes de memòria RAM que no s'esborra quan es perd l'alimentació jaque hi ha la pila de reserva.

El connexionat d'aquest mòdul és molt senzill ja que només cal connectar dues potes d'alimentació i dues de comunicació. Encara que el PIC16F690 incorpora la comunicació I2C, s'ha preferit implementar des de zero les funcions de comunicació. En la configuració triada, les connexions són les de la taula següent:

Mòdul RTC PIC16F690 Funció
5V Vdd Positiu de l'alimentació
GND Vss Negatiu de l'alimentació
SDA RC7 Canal de comunicació I2C
SCL RC6 Rellotge I2C

El mòdul RTC també pot generar una sortida (SQW) que, en principi, s'activa un cop cada segon però que també pot fer-ho amb altres periodicitats.

Les dades corresponents a la data i l'hora estan guardades en la memòria del mòdul RTC. En aquesta memòria també hi podem escriure per posar la data i l'hora correctes. La comunicació I2C ens permet accedir a adreces concretes de memòria. La funció de cada una s'indica a la següent taula:

Adreça
(decimal)
Funció Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 Rang de valors
0 Segons CH Desenes segons Unitats segons 00 - 59
1 Minuts 0 Desenes minuts Unitats minuts 00 - 59
2 Hores 0 1 → 12 h 1 → PM
0 → AM
Desenes hores Unitats hores 01 - 12
0 → 24 h Desenes hores Unitats hores 00 - 23
3 Dia de la setmana 0 0 0 0 0 Dia de la setmana 1 - 7
4 Dia del mes 0 0 Desenes dia del mes Unitats dia del mes 00 - 31
5 Mes 0 0 0 Desenes mes Unitats mes 01 - 12
6 Any Desenes any Unitats any 00 - 99
7 Polsos Estat actual SQW 0 0 1 → activa sortida polsos
0 → desactiva sortida polsos
0 0 Freqüència polsos
8 - 63 RAM Memòria lliure

Quan el bit CH és 1 el rellotge s'atura i permet estalviar energia.

El dia del mes es gestiona a partir del calendari i, per tant, es té en compte el nombre de dies del mes actual (inclosos anys de traspàs). En canvi, el dia de la setmana es gestiona en forma independent de la resta, per tant, podem considerar que 1 és dilluns i 7 diumenge o qualsevol altra opció.

A l'hora de llegir i escriure dades, la comunicació I2C contempla diverses opcions. En aquest cas només contemplarem la lectura i l'escriptura d'un únic byte. Els elements ques es comuniquen amb I2C han de tenir una adreça ja que hi pot haver diversos elements connectats simultàniament. El nostre mòdul RTC té una adreça I2C de set bits 1101000 que haurem d'indicar quan ens hi connectem.

A continuació tenim un programa de mostra que envia al microcontrolador una data (que podem canviar, si ho desitgem) i porta un bucle que la va llegint cada un segon (aproximadament). S'ha triat una data i hora properes al moment de canvi de dia i de mes per poder veure com ho fa.

#include <p16F690.inc>
	__config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
	cblock	0x20
AdreI2C			; Adreça del dispositiu I2C
AdreMem			; Adreça de la memòria on volem llegir o escriure
Dades			; Dades que volem llegir o escriure
Port			; Valor a escriure al port C
			; RC7 és SDA
			; RC6 és SCL
Buffer			; Variable emprada per les funcions de llegir i escriure
Bits			; Compta els bits que es van rebent o enviant
Caracter		; Caràcter o codi a enviar a la pantalla
Retard1			; Variables de retard	
Retard2
Retard3
	endc
	org 0
Inici
	bsf		STATUS,RP1	; Tria el banc 2
	movlw		b'00000101'
	movwf		ANSEL		; Configura AN0 i AN2 com entrada analògica
	clrf		ANSELH		; Desactiva les altres entrades analògiques
	bcf		STATUS,RP1
	bsf		STATUS,RP0	; Tria el banc 1
	movlw		0xFF		; Posa l'acumulador a FFh (tot uns)
	movwf		TRISA		; Posa tots els bits del port A com a entrada
	clrf		TRISB		; Tot el port B és de sortida
	clrf		TRISC		; Tot el port C és de sortida
	bsf		TXSTA,BRGH	; Configuració de velocitat
	bcf		BAUDCTL,BRG16	; Paràmetre de velocitat de 8 bits
	movlw		.25		; Velocitat de 9600 baud
	movwf		SPBRG		; Paràmetre de velocitat
	bcf		TXSTA,SYNC 	; Comunicació asíncrona
	bcf		TXSTA,TX9 	; Comunicació de 8 bits
	bcf		STATUS,RP0	; Tria el banc 0
	bsf		RCSTA,SPEN	; Activa comunicació sèrie
	bsf		STATUS,RP0	; Tria el banc 1
	bsf		TXSTA,TXEN 	; Activa comunicació
	bcf		STATUS,RP0	; Tria el banc 0
	clrf		PORTB		; Posa a zero totes les sortides del port B
	clrf		PORTC		; Posa a zero totes les sortides del port C
	clrf		Port		; Posa a zero tots els bits de Port
	movlw		b'01101000'	; Adreça del mòdul RTC
	movwf		AdreI2C		; Ho guarda com a adreça I2C
					; Com a mostra, es posen les 23.58.34
					; del dijous 31-3-2016
					; per poder veure un canvi de dia i de mes
	movlw		.0		; Segons
	movwf		AdreMem
	movlw		0x34		; 34
	movwf		Dades
	call		EscriuI2C
	movlw		.1		; Minuts
	movwf		AdreMem
	movlw		0x58		; 58
	movwf		Dades
	call		EscriuI2C
	movlw		.2		; Hores
	movwf		AdreMem
	movlw		0x23		; 23
	movwf		Dades
	call		EscriuI2C
	movlw		.3		; Dia setmana
	movwf		AdreMem
	movlw		0x04		; Dijous
	movwf		Dades
	call		EscriuI2C
	movlw		.4		; Dia mes
	movwf		AdreMem
	movlw		0x31		; 31
	movwf		Dades
	call		EscriuI2C
	movlw		.5		; Mes
	movwf		AdreMem
	movlw		0x03		; Març
	movwf		Dades
	call		EscriuI2C
	movlw		.6		; Any
	movwf		AdreMem
	movlw		0x16		; 2016
	movwf		Dades
	call		EscriuI2C
	movlw		.7		; Polsos
	movwf		AdreMem
	movlw		0x00		; Desactivat
	movwf		Dades
	call		EscriuI2C
Bucle
	movlw		.5		; Retard d'1 s
	call		Rets
	movlw		.254		; Control de la posició del cursor
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		.64		; Filera 2 columna 1
	iorlw		b'10000000'	; Posa el bit de posicionat a 1
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		.2		; Hores
	movwf		AdreMem
	call		LlegirI2C
	call		HEXtoBCD	; Ho mostra a la pantalla
	movlw		'.'		; Separador
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		.1		; Minuts
	movwf		AdreMem
	call		LlegirI2C
	call		HEXtoBCD	; Ho mostra a la pantalla
	movlw		'.'		; Separador
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		.0		; Segons
	movwf		AdreMem
	call		LlegirI2C
	call		HEXtoBCD	; Ho mostra a la pantalla
	movlw		.254		; Control de la posició del cursor
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		.0		; Filera 1 columna 1
	iorlw		b'10000000'	; Posa el bit de posicionat a 1
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		.4		; Dia
	movwf		AdreMem
	call		LlegirI2C
	call		HEXtoBCD	; Ho mostra a la pantalla
	movlw		'-'		; Separador
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		.5		; Mes
	movwf		AdreMem
	call		LlegirI2C
	call		HEXtoBCD	; Ho mostra a la pantalla
	movlw		'-'		; Separador
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		'2'		; Any primer dígit
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		'0'		; Any segon dígit
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		.6		; Any
	movwf		AdreMem
	call		LlegirI2C
	call		HEXtoBCD	; Ho mostra a la pantalla
	movlw		' '		; Espai
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		' '		; Espai
	movwf		Caracter	; Ho guarda a la variable
	call		EnviaL		; Ho envia
	movlw		.3		; Dia setmana
	movwf		AdreMem
	call		LlegirI2C
	call		Hex1		; Ho mostra a la pantalla
	goto		Bucle
					;
					; Llegeix un byte I2C
					; En el dispositiu indicat a AdreI2C
					; A l'adreça de memòria guardada a AdreMem
					; Deixa el valor llegit a Dades
					; 
LlegirI2C					
	call		SDAsortida	; Posa SDA com a sortida
	call		StartI2C	; Envia el bit d'inici I2C
					; En la primera fase enviem l'adreça del dispositiu
	movf		AdreI2C,w	; Adreça a enviar
	movwf		Buffer		; Preparada per enviar
					; Només hem d'enviar set bits
	bcf		STATUS,C	; i després un zero per indicar escriptura
	rlf		Buffer,f	; Eliminem el bit de més pes
					; i afegim el zero al final
	call		EnvByteI2C	; Envia el byte
	call		SDAentrada	; Posa SDA com a entrada
	call		ACK_S		; Reb un ACK des de l'esclau
					; A la segona fase enviem l'adreça de memòria
	call		SDAsortida	; Posa SDA com a sortida
	movf		AdreMem,w	; Adreça a enviar
	movwf		Buffer		; Preparada per enviar
	call		EnvByteI2C	; Envia el byte
	call		SDAentrada	; Posa SDA com a entrada
	call		ACK_S		; Reb un ACK des de l'esclau
	call		SDAsortida	; Posa SDA com a sortida
	call		RestartI2C	; Envia el bit de reinici I2C
					; En la tercera fase enviem l'adreça del dispositiu
	movf		AdreI2C,w	; Adreça a enviar
	movwf		Buffer		; Preparada per enviar
					; Només hem d'enviar set bits
	bsf		STATUS,C	; i després un 1 per indicar lectura
	rlf		Buffer,f	; Eliminem el bit de més pes
					; i afegim l'1 al final
	call		EnvByteI2C	; Envia el byte
	call		SDAentrada	; Posa SDA com a entrada
	call		ACK_S		; Reb un ACK des de l'esclau
					; A la quarta fase enviem el valor
	call		RebByteI2C	; Rep el byte
	movf		Buffer,w	; Agafa el valor rebut
	movwf		Dades		; I el guarda a dades
	call		SDAsortida	; Posa SDA com a sortida
	call		NACK_M		; Envia un NACK a l'esclau
	call		StopI2C		; Envia el bit d'aturada I2C
	return
					;
					; Escriu un byte I2C
					; Amb el valor indicat a Dades
					; En el dispositiu indicat a AdreI2C
					; A l'adreça de memòria guardada a AdreMem
					; 
EscriuI2C					
	call		SDAsortida	; Posa SDA com a sortida
	call		StartI2C	; Envia el bit d'inici I2C
					; En la primera fase enviem l'adreça del dispositiu
	movf		AdreI2C,w	; Adreça a enviar
	movwf		Buffer		; Preparada per enviar
					; Només hem d'enviar set bits
	bcf		STATUS,C	; i després un zero per indicar escriptura
	rlf		Buffer,f	; Eliminem el bit de més pes
					; i afegim el zero al final
	call		EnvByteI2C	; Envia el byte
	call		SDAentrada	; Posa SDA com a entrada
	call		ACK_S		; Reb un ACK des de l'esclau
					; A la segona fase enviem l'adreça de memòria
	call		SDAsortida	; Posa SDA com a sortida
	movf		AdreMem,w	; Adreça a enviar
	movwf		Buffer		; Preparada per enviar
	call		EnvByteI2C	; Envia el byte
	call		SDAentrada	; Posa SDA com a entrada
	call		ACK_S		; Reb un ACK des de l'esclau
					; A la tercera fase enviem el valor
	call		SDAsortida	; Posa SDA com a sortida
	movf		Dades,w		; Adreça a enviar
	movwf		Buffer		; Preparada per enviar
	call		EnvByteI2C	; Envia el byte
	call		SDAentrada	; Posa SDA com a entrada
	call		ACK_S		; Reb un ACK des de l'esclau
	call		SDAsortida	; Posa SDA com a sortida
	call		StopI2C		; Envia el bit d'aturada I2C
	return
					;
					; Posa SDA com a entrada
					;
SDAentrada
	bsf		STATUS,RP0	; Tria el banc 1
	bsf		TRISC,7		; Posem SDA com a entrada
	bcf		STATUS,RP0	; Tria el banc 0
	return
					;
					; Posa SDA com a sortida
					;
SDAsortida
	bsf		STATUS,RP0	; Tria el banc 1
	bcf		TRISC,7		; Posem SDA com a sortida
	bcf		STATUS,RP0	; Tria el banc 0
	return
					; 
					; Envia el bit d'inici I2C
					;
StartI2C
RestartI2C
	bsf		Port,7		; Abans de començar, SDA ha d'estar activat
	bsf		Port,6		; i SCL també
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	nop				; Allarguem el pols
	nop
	bcf		Port,7		; Bit d'inici, posem SDA a zero
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	nop				; Allarguem el pols
	nop
	bcf		Port,6		; Fi del bit d'inici, posem SCL a zero
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	return
					; 
					; Envia el bit d'aturada I2C
					;
StopI2C
	bcf		Port,7		; Desactivem SDA
	bsf		Port,6		; i activem SCL
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	bsf		Port,7		; Posem SDA a 1
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	return
					; 
					; Reb un ACK des de l'esclau
					;
ACK_S
	bsf		Port,6		; Posem SCL a 1
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	nop				; Esperem
	nop
	bcf		Port,6		; Posem SCL a zero
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	bsf		Port,7		; Posem SDA a 1
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	return
					; 
					; Envia un NACK a l'esclau
					;
NACK_M
	bsf		Port,7		; Posem SDA a 1
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	bsf		Port,6		; Posem SCL a 1
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	nop				; Esperem
	nop
	bcf		Port,6		; Posem SCL a zero
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	return
					;
					; Funció interna que envia un byte a I2C
					; El byte a enviar ha d'estar a Buffer
					; 
EnvByteI2C					
	movlw		.8		; Hem d'enviar 8 bits
	movwf		Bits		; Ho posem al comptador
BucEnv
	bcf		Port,6		; Posem SCL a zero per modificar SDA
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	bsf		Port,7		; Suposem que hem agafat un 1
	rlf		Buffer,f	; Agafem el bit que hem d'enviar
	btfss		STATUS,C	; Mirem si realment era un 1
	bcf		Port,7		; Si era un zero, corregim
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	bsf		Port,6		; Activem SCL perquè el receptor llegeixi el bit
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	decfsz		Bits,f		; Ja hem enviat un altre bit
	goto		BucEnv		; Si no era l'últim, repetim
	bcf		Port,6		; Posem SCL a zero per modificar SDA
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	return
					;
					; Funció interna que rep un byte a I2C
					; El byte rebut estarà a Buffer
					; 
RebByteI2C					
	bcf		Port,6		; Posem SCL a zero
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	movlw		.8		; Hem de rebre 8 bits
	movwf		Bits		; Ho posem al comptador
BucReb
	bsf		Port,6		; Posem SCL a 1
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	bsf		STATUS,C	; Suposem que llegirem un 1
	btfss		PORTC,7		; Mirem si realment era un 1
	bcf		STATUS,C	; Si era un zero, corregim
	rlf		Buffer,f	; Agafem el bit que acabem de rebre
	bcf		Port,6		; Posem SCL a zero
	movf		Port,w		; Agafem els valors
	movwf		PORTC		; I els copiem al port C
	decfsz		Bits,f		; Ja hem enviat un altre bit
	goto		BucReb		; Si no era l'últim, repetim
	return
					;
					; Enviem caràcters a visualitzar
					;
EnviaL					
	movf		Caracter,w	; Agafa el caràcter
	movwf		TXREG		; L'envia
	nop			
	nop				; Espera 2 us
	btfss		PIR1,TXIF	; El registre TXREG ha quedat lliure?
	goto		$-1		; No, doncs esperem
	return				; Tornem al lloc des d'on hem vingut
					;
					; Mostrem a la pantalla un valor BCD
					; a partir de dues xifres hexadecimals
					;
HEXtoBCD
	swapf		Dades,w
	andlw		0x0F
	movwf		Caracter	; Ho guarda a la variable
	movlw		'0'		; Carrega el codi ASCII del número 0
					; Sumant-li la xifra tindrem el codi ASCII
	addwf		Caracter,f	; Ara Caracter conté el caràcter ASCII
	call		EnviaL		; Ho envia
Hex1				; Si només té una xifra, entrem per aquí
	movf		Dades,w
	andlw		0x0F
	movwf		Caracter	; Ho guarda a la variable
	movlw		'0'		; Carrega el codi ASCII del número 0
					; Sumant-li la xifra tindrem el codi ASCII
	addwf		Caracter,f	; Ara Caracter conté el caràcter ASCII
	call		EnviaL		; Ho envia
	return
					;
					; Funció de retard de 0,2 W s
					;
Rets
	movwf		Retard3
Bucles
	decfsz		Retard1,f		
	goto		Bucles		
	decfsz		Retard2,f		
	goto		Bucles		
	decfsz		Retard3,f	
	goto		Bucles
	return
	end

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.