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).

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

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