/* ********************************************************************** ** Programma: Rtcseg.c - Versione: 1.2 - 21 Luglio 2003 ** ** Compilatore : uC/51 Ver. 1.10.7 ** ** Scheda : K51-AVR e microcontrollore ATMEL AT89c4051 ** ** Ditta: grifo(r) ITALIAN TECHNOLOGY ** ** Via Dell' Artigiano 8/6 40016 San Giorgio di Piano (BO) ** ** Tel.+39 051 892 052 Fax +39 051 893 661 ** ** http://www.grifo.com http://www.grifo.it ** ** sales@grifo.it tech@grifo.it grifo@grifo.it ** ** ** ** Realizzato da: Graziano Gaiba ** ********************************************************************** Questo programma permette di visualizzare RTC o orologio IC7(PCF8583) nei 4 display a 7 seg. Per inpostare RTC si utilizzano i tasti T2 e T3, precisamente con T2 si incrementano la cifra delle ore, mentre con T3 la cifra dei minuti. Ad ogni pressione di uno dei 2 tasti viene azzerato il conteggio dei secondi. Tramite il tasto T1 si passa alla visualizzazione dei secondi o della ora. Ad ogni pressione di un tasto viene emesso un seganle acustico. Non viene gestita la data ed aventuale allarme. La presente versione e' adatta al microcontrollore ATMEL AT89c4051. 05.06.2000 by Adriano Pedrielli (per la sola K51) (Versione originale in BASCOM 8051) 21.07.2003 by Graziano Gaiba Traduzione in uC/51 e versione per ATMEL AT89c4051. */ #include #include #include #define FALSE 0x00 // Valori booleani #define TRUE 0xFF #define LF 0x0A #define CRET 0x0D /***************** Elenco indirizzi per Saa1064 ***********************/ #define Saa1064 0x38 // Slave address SAA1064 #define Wsaa1064 0x70 // Slave address SAA1064 in Write #define Rsaa1064 0x71 // Slave address SAA1064 in Read #define Ctb 0x00 // Ind. Control byte #define Dig1 0x01 // Ind. Digit 1 #define Dig2 0x02 // Ind. Digit 2 #define Dig3 0x03 // Ind. Digit 3 #define Dig4 0x04 // Ind. Digit 4 /************************************************************************/ #define Rtc 0x50 // Slave address RTC PCF8583 #define Wrtc 0xA0 // Slave address RTC PCF8583 in Write #define Rrtc 0xA1 // Slave address RTC PCF8583 in Read // Valori di inizializzazione dell'SAA1064 #define saa1064_init_length 0x06 // Lunghezza sequenza inizializzazione const unsigned char saa1064_init[]={ Ctb, // Punto al registro di controllo 0x27, // = 0b00100111 // bit0 =1 dynamic mode // bit1 =1 digit 1+3 not blanked // bit2 =1 digit 2+4 not blanked // bit3 =0 no test segment // bit4 =0 no 3mA segment current // bit5 =1 6mA segment current // bit6 =0 no 12mA segment current // bit7 =0 indifferente 0, // scrive DY1 off 0, // scrive DY2 off 0, // scrive DY3 off 0 // scrive DY4 off }; // Codici per convertire una cifra da 0 a F nella codifica a 7 segmenti const unsigned char cifra_7seg[]={0x3F,0x06,0x5B,0x4F,0x66, 0x6D,0x7D,0x07,0x7F,0x6F, 0x77,0x7C,0x39,0x5E,0x79,0x71}; // Variabili globali gestione I2C BUS bit unsigned char SDA @ 0x97; // Pin SDA=P1.7 bit unsigned char SCL @ 0x96; // Pin SCL=P1.6 // Variabili globali gestione tasti e buzzer bit unsigned char T1 @ 0x95; // P1.5 bit unsigned char T2 @ 0x94; // P1.4 bit unsigned char T3 @ 0x93; // P1.3 bit unsigned char BUZ @ 0xB5; // P3.5 /*************************** Variabili Globali *****************************/ char unsigned bit d_dot; // Controlla lo stato del punto decimale near unsigned char secondi; // Ultimo valore dei secondi char unsigned bit secondi_ora; // Visualizza orario o secondi /********* Funzioni di utility generale e di gestione sezioni hw ***********/ void iniser(unsigned long baud) /* Inizializza la linea seriale con: Bit x chr = 8 Stop bit = 1 Parity = None Baud rate = baud usando come baud rate generator il timer 1. */ { SCON=0x052; // Modo 1, abilita ricevitore TMOD&=0x00F; // Timer 1 in modo auto-reload TMOD|=0x020; TR1=0; // Stop al TIMER 1 TH1=256-((2*11059200)/(384*baud)); // baud a 14.7456 MHz PCON=PCON|0x080; // Setta SMOD=1 per baud rate alti TR1=1; // Start al TIMER 1 } void clrscr(void) /* Effettua la funzione di clear screen per una generica console */ { unsigned char r; putc(CRET); for (r = 0 ; r < 25 ; r++) { putc(LF); // Trasmette 25 Line Feed } //endfor } void waitkey(void) /* Rappresenta messaggio ed attende pressione tasto */ { printf("Premere tasto per continuare.."); getc(); puts(""); } void ritardo(unsigned int rit) /* Effettua un ritardo software di rit millisecondi, calibrato su un Clock di CPU da 14.7456 MHz, a seconda della CPU montata. */ { unsigned int r,rit1ms; rit1ms=100; // Valore sperimentale per ritardo di 1 msec. con 80c32 do { for (r=0 ; r0); } void riti2c(void) /* Genera ritardo per comunicazione sincrona in I2CBUS. */ { #asm nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop #endasm } void starti2c(void) /* Genera sequenza di start per I2C BUS */ { SCL = 0; // Genera sequenza di start SDA = 1; riti2c(); SCL = 1; SDA = 0; riti2c(); SCL = 0; } void stopi2c(void) /* Genera sequenza di stop per I2C BUS */ { SCL = 0; // Genera sequenza di stop SDA = 0; riti2c(); SCL = 1; SDA = 1; riti2c(); SCL = 0; } void wri2c_bit(unsigned char i2cbit) /* Serializza il bit D0 di i2cbit su I2C BUS */ { SCL = 0; // Setta SDA e genera impulso positivo su SCL SDA = i2cbit; riti2c(); SCL = 1; riti2c(); SCL = 0; } unsigned char rdi2c_bit(void) /* Deserializza un bit da I2C BUS e lo salva in D0 del risultato */ { unsigned char biti2c; SDA = 1; // Evita conflitti su acquisizione SDA SCL = 0; // Assicura stato SCL riti2c(); SCL = 1; // Genera impulso positivo su SCL e su questo legge SDA biti2c = SDA; riti2c(); SCL = 0; return biti2c; } void wri2c_byte(unsigned char i2cbyte) /* Serializza il byte i2cbyte su I2C BUS */ { unsigned char b; for (b = 1; b <= 8; b++) { if ((i2cbyte & 0x80) == 0) // Determina e setta bit b wri2c_bit(0); else wri2c_bit(1); i2cbyte = i2cbyte << 1; } } unsigned char rdi2c_byte(void) /* Deserializza un byte da I2C BUS e lo salva nel risultato */ { unsigned char b,tmp; tmp = 0; for (b = 1; b <= 8; b++) { tmp = tmp << 1; tmp = tmp | rdi2c_bit(); // Preleva e salva bit b } return tmp; } unsigned char wr_i2c(unsigned char i2csla,unsigned char i2cadd,unsigned char i2cdat) /* Scrive il byte i2cdat all'indirizzo i2caddr del dispositivo I2C BUS che ha uno slave address i2csla. Restituisce flag booleano che indica il risultato dell'operazione: 0=corretta,1=errata. */ { unsigned char i2cris; i2cris = 0; // Setta risultato corretto starti2c(); // Fornisce sequenza di start wri2c_byte(i2csla); // Fornisce slave address+W i2cris = i2cris | rdi2c_bit(); // Controlla ACK su slave address+W wri2c_byte(i2cadd); // Fornisce address i2cris = i2cris | rdi2c_bit(); // Controlla ACK su address wri2c_byte(i2cdat); // Fornisce dato i2cris = i2cris | rdi2c_bit(); // Controlla ACK su dato stopi2c(); // Fornisce sequenza di stop return i2cris; // Restituisce risultato operazione } unsigned char rd_i2c(unsigned char i2csla,unsigned char i2cadd,unsigned char *i2cdat) /* Legge il byte i2cdat dall'indirizzo i2caddr del dispositivo I2C BUS che ha uno slave address i2csla. Restituisce flag booleano che indica il risultato dell'operazione: 0=corretta,1=errata. */ { unsigned char i2cris; i2cris = 0; // Setta risultato corretto starti2c(); // Fornisce sequenza di start wri2c_byte(i2csla); // Fornisce slave address+W i2cris = i2cris | rdi2c_bit(); // Controlla ACK su slave address+W wri2c_byte(i2cadd); // Fornisce address i2cris = i2cris | rdi2c_bit(); // Controlla ACK su address starti2c(); // Fornisce sequenza di start wri2c_byte(i2csla | 0x01); // Fornisce slave address+R i2cris = i2cris | rdi2c_bit(); // Controlla ACK su slave address+R *i2cdat = rdi2c_byte(); // Preleva dato stopi2c(); // Fornisce sequenza di stop return i2cris; // Restituisce risultato operazione } // Invia allo slave address indicato (primo parametro) il numero di bytes indicato // (terzo parametro) memorizzati a partire dall'indirizzo indicato (secondo // parametro). // Non fa nulla se indicati 0 dati. // Restituisce flag booleano che indica il risultato dell'operazione: // 0=corretta,1=errata. unsigned char i2c_invia(unsigned char slave, unsigned char *dati, unsigned char ndati) { unsigned char i2cris, i; if(! ndati) // Se ndati e' zero, esce return 0; i2cris=0; starti2c(); // Sequenza di start wri2c_byte(slave); // Invia lo slave address i2cris|=rdi2c_bit(); // Controlla ACK i=0; while(ndati--) // Invia ndati bytes { wri2c_byte(dati[i++]); // Invia lo slave address i2cris|=rdi2c_bit(); // Controlla ACK } stopi2c(); // Sequenza di stop return i2cris; // Restituisce il risultato } // Invia allo slave address indicato (primo parametro) il numero di bytes indicato // (terzo parametro) memorizzati a partire dall'indirizzo indicato (secondo // parametro), poi si aspetta di ricevere in risposta il numero di bytes indicati // nel quarto parametro e li memorizza a partire dall'indirizzo prima specificato. // Non fa nulla se indicati 0 dati in arrivo e in invio. // Restituisce flag booleano che indica il risultato dell'operazione: // 0=corretta,1=errata. unsigned char i2c_ricevi(unsigned char slave, unsigned char *dati, unsigned char ndati_out, unsigned char ndati_in) { unsigned char i2cris, i; if((! ndati_in) && (! ndati_out)) // Se ndati_in e ndati_out sono zero,esce return 0; i2cris=0; // Invia i dati starti2c(); // Sequenza di start wri2c_byte(slave); // Invia lo slave address i2cris|=rdi2c_bit(); // Controlla ACK i=0; while(ndati_out--) // Invia ndati_out bytes { wri2c_byte(dati[i++]); // Invia lo slave address i2cris|=rdi2c_bit(); // Controlla ACK } // Riceve i dati starti2c(); wri2c_byte(slave | 0x01); // Fornisce slave address+R i2cris|=rdi2c_bit(); // Controlla ACK su slave address+R i=0; while(ndati_in--) // Riceve ndati_in bytes { dati[i++]=rdi2c_byte(); // Legge il dato if(ndati_in) { wri2c_bit(0); // Invia ACK } else { wri2c_bit(1); // Ultimo dato da leggere, invia NACK } } stopi2c(); // Sequenza di stop return i2cris; // Restituisce il risultato } unsigned char bintobcd(unsigned char d) /* Procedura di trasformazione di un byte in codifica binaria (0-99) nella corrispondente codifica bcd. */ { d=((d/10)<<4)|(d%10); return d; } unsigned char bcdtobin(unsigned char d) /* Procedura di trasformazione di un byte in codifica bcd nella corrispondente codifica binaria (0-99). */ { d=((d>>4)*10)+(d&0x0F); return d; } /***************** Converte un numero da 0 a 9 in 7 segmenti *********** Questa procedura converte una cifra da 0 a 9 nel formato 7 segmenti. Parametri: Ingresso : valore da 0 a 9 Uscita : valore in formato 7 segmenti. ************************************************************************/ unsigned char digit(unsigned char n) { if(n < 10) { return cifra_7seg[n]; // legge in tabella il valore } else { return 0; // Se la cifra supera 9 spegne il display } } /***************** Converte un numero BCD in due cifre ************** Questa procedura converte un numero BCD da 0 a 153 (DEC 0-99) in due cifre separate, e restituisce il valore decimale, in questo modo si possono rappresentare. Se il numero supera 153, le due cifre assumono il valore 10 che indica display 7 seg. spento. Parametri: Ingresso : v, valore da 0 a 153 BCD ( Decimale 0..99) Uscita : d, cifra delle decine u, cifra delle unita v, contiene il valore in decimale del BCD ************************************************************************/ void cifre(unsigned char *v, unsigned char *d, unsigned char *u) { if(*v < 154) // risulta minore di 154 BCD( 100 in DEC) { *d = *v >> 4; // Ricavo la cifra delle decine *u = *v & 0x0F; // Ricavo la cifra delle unita *v = *d *10 + *u; // Ricavo il valore in decimale } else { *d = 10; // Spengo la cifra delle decine *u = 10; // Spengo la cifra delle unita *v = 0; // Azzero il valore Decimale } } /*********************** Visualizza 2 numeri BCD *********************** Questa procedura permette di visualizzare due numeri BCD che in decimale hanno 2 cifre. Es: BCD 153= Dec. 99 Durante lo scorrere del tempo, ad gni variazione di secondi accende o spegne il decimal point su DY2. Parametri: Ingresso : N1 as byte, valore primo numero N2 as byte, valore secondo numero Uscita : nulla ************************************************************************/ void vis_2num(unsigned char n1, unsigned char n2) { unsigned char v, d, u, d1, u1; v=n1; cifre(&v, &d, &u); // Scomposizione in due cifre BCD if(! d) // Spegne la cifra se vale 0 { d=10; } v=n2; cifre(&v, &d1, &u1); starti2c(); // Start sequence wri2c_byte(Wsaa1064); // Slave address rdi2c_bit(); // Controlla ACK su slave address+R wri2c_byte(Dig1); // Primo digit rdi2c_bit(); wri2c_byte(digit(d)); // Scrive in DY1 il nibble piu' significativo rdi2c_bit(); // Scrive in DY2 il nibble successivo e gestisce il punto decimale wri2c_byte(digit(u) | (d_dot ? 0x80 : 0x00)); rdi2c_bit(); // Scomposizione in 2 nibble della parte bassa wri2c_byte(digit(d1)); // Scrive in DY3 il nibble piu' significativ rdi2c_bit(); wri2c_byte(digit(u1)); // Scrive in DY4 il nibble successivo rdi2c_bit(); stopi2c(); // Stop sequence } // Attiva il buzzer per il tempo specificato, in millisecondi void buzzer(unsigned int t) { BUZ=0; ritardo(t); BUZ=1; } // Effettua un debouncing su T1 e ne restituisce lo stato: // 1 se e' premuto // 0 se non e' premuto unsigned char debounceT1(void) { unsigned char status; unsigned int steps; if(T1) // Se T1 non premuto... { return 0; // ...restituisci che non e' premuto } else { status=1; // Indica che e' premuto steps=20; // Controlla per 20 volte while(status) { if(T1) // Se T1 non e' premuto... { status=0; // ...esci dal loop } if(! steps--) // Se abbiamo fatto steps controlli... { break; // ...esci } ritardo(5); // Ritardo tra le verifiche } return status; // Restituisce lo stato di T1 } } // Effettua un debouncing su T2 e ne restituisce lo stato: // 1 se e' premuto // 0 se non e' premuto unsigned char debounceT2(void) { unsigned char status; unsigned int steps; if(T2) // Se T2 non premuto... { return 0; // ...restituisci che non e' premuto } else { status=1; // Indica che e' premuto steps=20; // Controlla per 20 volte while(status) { if(T2) // Se T2 non e' premuto... { status=0; // ...esci dal loop } if(! steps--) // Se abbiamo fatto steps controlli... { break; // ...esci } ritardo(5); // Ritardo tra le verifiche } return status; // Restituisce lo stato di T2 } } // Effettua un debouncing su T3 e ne restituisce lo stato: // 1 se e' premuto // 0 se non e' premuto unsigned char debounceT3(void) { unsigned char status; unsigned int steps; if(T3) // Se T3 non premuto... { return 0; // ...restituisci che non e' premuto } else { status=1; // Indica che e' premuto steps=20; // Controlla per 20 volte while(status) { if(T3) // Se T3 non e' premuto... { status=0; // ...esci dal loop } if(! steps--) // Se abbiamo fatto steps controlli... { break; // ...esci } ritardo(5); // Ritardo tra le verifiche } return status; // Restituisce lo stato di T3 } } void getrtc(unsigned char *ore,unsigned char *min,unsigned char *sec) /* Preleva data ed ora attuale dal RTC e le restituisce nei parametri della funzione */ { unsigned char dummy; rd_i2c(Wrtc,0x02,sec); // Legge secondi rd_i2c(Wrtc,0x03,min); // Legge minuti rd_i2c(Wrtc,0x04,ore); // Legge ore rd_i2c(Wrtc,0x05,&dummy); // Legge giorno,anno rd_i2c(Wrtc,0x06,&dummy); // Legge giorno settimana e mese } void setrtc(unsigned char ore,unsigned char min,unsigned char sec) /* Setta RTC con i dati passati nei parametri, nel formato a 24 ore ed abilita interrupt ogni secondo. */ { wr_i2c(Wrtc,0x00,0x84); // Setta stop wr_i2c(Wrtc,0x01,0x00); // Setta centinaia di secondi wr_i2c(Wrtc,0x02,bintobcd(sec)); // Setta secondi wr_i2c(Wrtc,0x03,bintobcd(min)); // Setta minuti wr_i2c(Wrtc,0x04,bintobcd(ore)); // Setta ORE,formato 24h wr_i2c(Wrtc,0x05,(bintobcd(1)|(1<<6))); // Setta giorno e anno wr_i2c(Wrtc,0x06,(bintobcd(1)|(1<<5))); // Setta mese,settimana wr_i2c(Wrtc,0x07,0x00); // Setta timer wr_i2c(Wrtc,0x00,0x00); // Setta start } void demo_init(void) { unsigned char i; // Attende accensione SAA1064 do { starti2c(); wri2c_byte(Rsaa1064); rdi2c_bit(); i = rdi2c_byte(); wri2c_byte(1); // Not Acknowledge stopi2c(); } while(i); // Invia la sequenza di inizializzazione dell'SAA1064 i2c_invia(Wsaa1064, saa1064_init, saa1064_init_length); } void main(void) { unsigned char ore,min,sec; // Variabili per RTC ritardo(2); iniser(19200); TI=0; RI=0; // Commentare se si usa la seriale in polling (SIOTYPE=p oppure k) ES=1; // Abilita interrupt porta seriale EA=1; // Abilitazione interrupt clrscr(); demo_init(); // Inizializzazione periferiche secondi_ora=1; d_dot=0; ore=12; min=35; sec=0; secondi=0; // Inizializzazione RTC setrtc(ore,min,sec); while(1) // Loop ifinito { if(debounceT1()) // Se T1 premuto { secondi_ora=!secondi_ora; // Passa dalle ore ai secondi e viceversa } if(debounceT2()) // Se T2 premuto { ore=bcdtobin(ore); min=bcdtobin(min); if(ore==23) { ore=0; } else { ore++; } setrtc(ore, min, 0); buzzer(50); // Il buzzer suona per 50 millisecondi sec=secondi=0; } if(debounceT3()) // Se T2 premuto { ore=bcdtobin(ore); min=bcdtobin(min); if(min==59) { min=0; } else { min++; } setrtc(ore, min, 0); buzzer(50); // Il buzzer suona per 50 millisecondi sec=secondi=0; } getrtc(&ore,&min,&sec); // Preleva RTC (valori in BCD) if(sec!=secondi) // Se siamo nel secondi successivo { d_dot=!d_dot; // Complementa il punto decimale secondi=sec; // Memorizza il secondo } if(secondi_ora) { vis_2num(ore,min); // Visualizza ore e minuti } else { vis_2num(154, sec); // Spegne le prime due cifre e // visualizza i secondi } } }