DATA E ORA CON DS3231 E REGOLAZIONE

NE555 Schema con Microcontrollore PIC per Realizzare un Orologio con Data e Ora Con il Modulo DS3231 e la Possibilità di Regolazione…

 

 

 

 




 

INTRO

Il seguente schema va ad eseguire tutte le funzioni viste nel tutorial sul modulo DS3231 per realizzare un orologio con data e ora precise al secondo con la possibilità di regolare data e ora grazie a due pulsanti. Si utilizza un microcontrollore PIC16F877 per controllare il modulo RTC (Real Time Clock) e LCD che viene utilizzato per mostrare data e ora. La scelta del PIC16f877 è stata quasi obbligata visto che utilizzando un PIC18F252 quando su utilizzava il programma “FixHex” prima di andare a programmare il PIC il programma va a cambiare alcuni parti del codici creando problemi di funzionamento. Il codice funziona con tutti i microcontrollori che hanno internamente il modulo SPI, l’importante è usare un PIC16 se si ha a disposizione solo il programmatore K150.

Oltre a contare il tempo e visualizzarlo su display LCD è possibile regolare data e ora, funzione utile alla prima accensione oppure in caso di mancanza di tensione di alimentazione del modulo. In pratica vengono utilizzati due pulsanti, uno seleziona se regolare l’anno, il mese, il giorno, l’ora, i minuti o i secondi, mentre l’altro pulsante incrementa il valore di queste variabili , portandole al valore iniziale  automaticamente quando  si raggiunge il valore massimo per gli anni ad esempio 2099.

Il giorno (Lunedì, Martedì….) non viene visualizzato in quanto non è proprio necessario, ma con una piccola modifica è possibile visualizzare anche questa informazione. La regolazione massima per il giorno del mese è 31, il che non considera i mesi con meno giorni. Bisogna stare attenti a caricare il giorno giusto per il mese giusto, altrimenti il modulo DS3231 ritorna al valore 1 di giorno. Con una piccola modifica si può dare una intelligenza maggiore al sistema di regolazione.
 





 

CODICE

Il codice è il seguente:

sbit LCD_RS at RB2_bit; //Connessioni LCD sbit LCD_EN at RB3_bit; sbit LCD_D4 at RB4_bit; sbit LCD_D5 at RB5_bit; sbit LCD_D6 at RB6_bit; sbit LCD_D7 at RB7_bit; sbit LCD_RS_Direction at TRISB2_bit; sbit LCD_EN_Direction at TRISB3_bit; sbit LCD_D4_Direction at TRISB4_bit; sbit LCD_D5_Direction at TRISB5_bit; sbit LCD_D6_Direction at TRISB6_bit; sbit LCD_D7_Direction at TRISB7_bit; unsigned short sec absolute 0x0020; //Variabili data e ora unsigned short minute absolute 0x0021; unsigned short hour absolute 0x0022; unsigned short day absolute 0x0024; unsigned short month absolute 0x0025; unsigned short year absolute 0x0026; unsigned short dato absolute 0x0023; unsigned char regolazione = 0; //Variabile regolazione unsigned char time[9]; //Stringhe di caratteri unsigned char ddate[11]; char text[4]; const unsigned char SECONDS_ADDRES=0x00; //Indirizzi modulo DS3231 const unsigned char MINUTES_ADDRES=0x01; const unsigned char HOURS_ADDRES =0x02; const unsigned char WEEKDAY_ADDRES=0x03; const unsigned char DAY_ADDRES =0x04; const unsigned char MONTHS_ADDRES =0x05; const unsigned char YEAR_ADDRES =0x06; void Write_CMD_DS3231(unsigned char address,unsigned char dato){ //Sottoprogramma scrittura comandi  if(address == 0x0E || address == 0x0F){ //Se l'indirizzo è 0x0E o 0x0F  I2C1_Start(); //Inizializza I2C Scrivi 0xD0  I2C1_Wr(0xD0); //Scrivi indirizzo  I2C1_Wr(address); //Scrivi il dato  I2C1_Wr(dato); //Ferma I2C  I2C1_Stop();  } } unsigned char Read_TIMER_DS3231(unsigned char address){ //Sottoprogramma lettura data e ora  unsigned char value;  if((address>=0x00)&&(address<=0x06)){ //Se l'indirizzo è compreso  I2C1_Start(); //tra quelli di data e ora inizializza I2C  I2C1_Wr(0xD0); //Scrivi 0xD0  I2C1_Wr(address); //Scrivi indirizzo  I2C1_Repeated_Start(); //Se vi sono problemi ripeti lo start  I2C1_Wr(0xD1); //Scrivi 0xD1  value = I2C1_Rd(0); //Leggi I2C  I2C1_Stop(); //Stop I2C  return(value);  } } unsigned char BCDUpperPart(unsigned char bcd_var){ //Sottoprogramma BCD to decimale  return (((bcd_var & 0xF0)>>4)|0x30); } unsigned char BCDLowerPart(unsigned char bcd_var){ //Sottoprogramma BCD to Decimale  return ((bcd_var & 0x0F)|0x30); } unsigned char IntToBCD (unsigned char int_var ){ //Sottoprogramma da decimale a BCD  return Dec2Bcd(int_var); } void Write_TIMER_DS3231(unsigned char address,unsigned char dato){ //Sottoprogramma scrittura data e ora  if((address>=0x00)&&(address<=0x06)){ //Se l'indirizzo è quello di data e ora  I2C1_Start(); //Scrivi indirizzo e dato  I2C1_Wr(0xD0);  I2C1_Wr(address);  I2C1_Wr(dato);  I2C1_Stop();  } } void Write_RAM_DS3231(unsigned char address,unsigned char dato){ //Sottoprogramma scrittura EEprom  if((address>=0x14)&&(address<=0xFF)){ //Se l'indirizzo è quello della EEProm  I2C1_Start(); //Avvia scrittura  I2C1_Wr(0xD0);  I2C1_Wr(address);  I2C1_Wr(dato);  I2C1_Stop();  } } unsigned char Read_RAM_DS3231(unsigned char address){ //Sottoprogramma lettura EEprom  unsigned char value;  if((address>=0x14)&&(address<=0xFF)){ //Se l'indirizzo è quello della EEProm  I2C1_Start(); //Avvia lettura  I2C1_Wr(0xD0);  I2C1_Wr(address);  I2C1_Repeated_Start();  I2C1_Wr(0xD1);  value = I2C1_Rd(0);  I2C1_Stop();  return(value);  } } void Display_Lcd_Time(unsigned char time_vect){ //Scrittura ora su LCD  time[0] = BCDUpperPart(hour); //Scrivi ora  time[1] = BCDLowerPart(hour);  time[2] = ':'; //scrivi :  time[3] = BCDUpperPart(minute); //Scrivi minuti  time[4] = BCDLowerPart(minute);  time[5] = ':'; //Scrivi :  time[6] = BCDUpperPart(sec); //Scrivi secondi  time[7] = BCDLowerPart(sec);  time[8] = '\0';  Lcd_Out(1,7,time); } void Display_Lcd_Date(unsigned char ddate_vect){ //Scrittura Data su LCD  ddate[0] = BCDUpperPart(day); //Scrivi giorno  ddate[1] = BCDLowerPart(day);  ddate[2] ='.'; //Scrivi .  ddate[3] = BCDUpperPart(month); //Scrivi mese  ddate[4] = BCDLowerPart(month);  ddate[5] ='.'; //Scrivi .  ddate[6] = '2'; //Scrivi 20  ddate[7] = '0';  ddate[8] = BCDUpperPart(year); //Scrivi anno  ddate[9] = BCDLowerPart(year);  ddate[10] = '\0';  Lcd_Out(2,7,ddate); } void EXT_INT() iv 0x0004 ics ICS_AUTO { //Interrupt  Delay_ms(300);  regolazione ++; //Cambia regolazione  INTCON.INTF = 0; //reset del flag } void main(){ //PORTB0 e 1 Ingressi  TRISB.F0 = 1;  TRISB.F1 = 1;  INTCON.GIE = 1; //attivo gli interrupt  OPTION_REG.INTEDG = 1; //interrupt sul fronte di salita  INTCON.INTE = 1;  Lcd_Init(); // Initializza LCD  Lcd_Cmd(_LCD_CLEAR); //Puliscilo  Lcd_Cmd(_LCD_CURSOR_OFF); //Spegni cursore  Lcd_Out(1,3," www.NE555.IT"); //Scrivi sulla prima riga  Delay_ms(1000);  I2C1_Init(100000); //I2C a 100KHz  Write_CMD_DS3231(0x0E,0x00); //Configura modulo ds3231  Write_CMD_DS3231(0x0F,0xF8);  sec =0x00; //Inizializza data e ora  minute =0x00;  hour =0x00;  day =0x01;  month =0x01;  year =0x17;  Write_TIMER_DS3231(SECONDS_ADDRES,sec); //Scrivi nel modulod data e ora  Write_TIMER_DS3231(MINUTES_ADDRES,minute);  Write_TIMER_DS3231(HOURS_ADDRES,hour);  Write_TIMER_DS3231(DAY_ADDRES,day);  Write_TIMER_DS3231(MONTHS_ADDRES,month);  Write_TIMER_DS3231(YEAR_ADDRES,year);  while(1){ //Ciclo infinito  Lcd_Cmd(_LCD_CLEAR); //Scrivi data e ora  Lcd_Out (1, 1, "TIME:");  Lcd_Out (2, 1, "DATE:");  sec = Read_TIMER_DS3231(SECONDS_ADDRES); //Leggi data e ora e aggiorna  minute = Read_TIMER_DS3231(MINUTES_ADDRES);  hour = Read_TIMER_DS3231(HOURS_ADDRES);  day = Read_TIMER_DS3231(DAY_ADDRES);  month = Read_TIMER_DS3231(MONTHS_ADDRES);  year = Read_TIMER_DS3231(YEAR_ADDRES);  Display_Lcd_Time(time[9]); //Visualizza data e ora  Display_Lcd_Date(ddate[11]);  Delay_ms(200); //Aggiorna ogni 0.2secondi  while (regolazione == 1){ //Se regolazione = 1  Lcd_Out (1, 1, "ANNO:"); //mostra valore anno corrente anno  Lcd_Out_Cp("20");  ByteToStr(year, text);  Lcd_Out_Cp(text);  Lcd_Out_Cp(" "); //usa stringhe vuote per pulire spazi  Lcd_Out (2, 1, " ");  if(PORTB.F1 == 1){ //Se il tasto di incremento = 1  Lcd_Cmd(_LCD_CLEAR); //Pulisci LCD  year ++; //Incrementa anno  if(year == 100) year = 0; //Se anno = 100 ritorna il valore a 0  Delay_ms(200); //ritardo anti rimbalzo  }  }  while (regolazione == 2){ //Se regolazione = 2  Lcd_Out (1, 1, "MESE: "); //Regola mese  ByteToStr(month, text);  Lcd_Out_Cp(text);  Lcd_Out_Cp(" ");  if(PORTB.F1 == 1){ //Se incremento premuto  Lcd_Cmd(_LCD_CLEAR); //incrementa mese fino a 12  month ++;  if(month == 13) month = 1; //ritorna a 1 se >12  Delay_ms(200);  }  }  while (regolazione == 3){ //Se regolazione = 3  Lcd_Out (1, 1, "GIORNO: "); //Regola giorno  ByteToStr(day, text);  Lcd_Out_Cp(text);  Lcd_Out_Cp(" ");  if(PORTB.F1 == 1){ //se incremento premuto  Lcd_Cmd(_LCD_CLEAR); //Incrementa giorno  day ++;  if(day == 32) day = 1; //Attenzione, limite 31 per tutti i mesi  Delay_ms(200);  }  }  while (regolazione == 4){ //Se regolazione = 4  Lcd_Out (1, 1, "ORA: "); //Regola ora  ByteToStr(hour, text);  Lcd_Out_Cp(text);  Lcd_Out_Cp(" ");  if(PORTB.F1 == 1){  Lcd_Cmd(_LCD_CLEAR);  hour ++;  if(hour == 25) hour = 0;  Delay_ms(200);  }  }  while (regolazione == 5){ //Regolazione = 5 regola minuti  Lcd_Out (1, 1, "MINUTI: ");  ByteToStr(minute, text);  Lcd_Out_Cp(text);  Lcd_Out_Cp(" ");  if(PORTB.F1 == 1){  Lcd_Cmd(_LCD_CLEAR);  minute ++;  if(minute == 60) minute = 0;  Delay_ms(200);  }  }  while (regolazione == 6){ //Regolazione = 6 regola secondi  Lcd_Out (1, 1, "SECONDI: ");  ByteToStr(sec, text);  Lcd_Out_Cp(text);  Lcd_Out_Cp(" ");  if(PORTB.F1 == 1){  Lcd_Cmd(_LCD_CLEAR);  sec ++;  if(sec == 60) sec = 0;  Delay_ms(200);  }  }  if(regolazione == 7){ //Se regolazione = 7  regolazione = 0; //Resetta regolazione  Lcd_Cmd(_LCD_CLEAR);  sec = IntToBCD (sec); //Converti da decimali a BCD  minute = IntToBCD (minute);  hour = IntToBCD (hour);  day = IntToBCD (day);  month = IntToBCD (month);  year = IntToBCD (year);  Write_TIMER_DS3231(SECONDS_ADDRES,sec); //Inserisci i valori nel modulo DS3231  Write_TIMER_DS3231(MINUTES_ADDRES,minute);  Write_TIMER_DS3231(HOURS_ADDRES,hour);  Write_TIMER_DS3231(DAY_ADDRES,day);  Write_TIMER_DS3231(MONTHS_ADDRES,month);  Write_TIMER_DS3231(YEAR_ADDRES,year);  }  } } 

Prima di tutto si inizializzano le connessioni con il display LCD, indicando a quali pin esso è connesso. Successivamente vengono inizializzati i registri che contengono le variabili utili e le costanti dei registri per i comandi del modulo DS3231.

Il primo sottoprogramma “Write_CMD_DS3231” serve per scrivere un comando nel modulo DS3231 e si va ad inizializzare la comunicazione I2C, poi a scrivere l’indirizzo e successivamente il dato spegnendo in fine la comunicazione.

Il secondo sottoprogramma “Read_TIMER_DS3231” viene usato per leggere i dati dal modulo DS3231 e si va a scrivere l’indirizzo del registro da leggere e poi si legge la risposta.

I tre sottoprogrammi successivi vengono usati per convertire i dati da interi a BCD e da BCD a interi.

“Write_TIMER_DS3231” viene usato per scrivere nel registro ore, minuti, secondi, anno, mese e giorno e si va a inizializzare la comunicazione, scrivere il valore del registro da modificare e si scrive il dato.

“Write_RAM_DS3231” e “Read_RAM_DS3231” servono per scrivere e leggere dati dalla memoria sul modulo memoria.

Il sottoprogramma “Display_Lcd_Time” è utilizzato per mostrare a schermo l’ora e va a scomporre i tre elementi dell’orario in decine e unità e li mostra uno per volta con i due punti come separatore. Il programma successivo funziona alla stesso modo ma viene usato per la data utilizzando punto e virgola come separatore.

Il ciclo di interrupt “EXT_INT” è utilizzato per rilevare la pressione del pulsante di regolazione e va a incrementare una variabile chiamata regolazione che indica quale variabile andare ad aggiustare.

Nel programma principale si vanno ad inizializzare gli I/O prima di tutto, poi gli interrupt e successivamente il display e la comunicazione I2C con velocità 100KHz. Infine prima del ciclo infinito si va ad inizializzare il modulo DS3231.

Nel ciclo infinito si pulisce il display, si scrive data e ora e poi si leggono questi valori mostrandoli a schermo ogni 200ms. Per la regolazione si usa la variabile regolazione, se 1 si regola l’anno, 2 il mese, 3 il giorno, 4 l’ora, 5 i minuti, 6 i secondi e infine se regolazione=7  si va a memorizzare nel modulo data e ora regolata. Se regolazione = 7 si resetta la regolazione, si convertono i dati da interi a BCD e si vanno a scrivere con la funzione “Write_TIMER_DS3231”.

In ogni singolo “while” che controlla la regolazione viene visualizzata su LCD cosa si va a regolare e si va a visualizzare anche il valore. Se l’altro tasto è a livello logico alto allora si incrementa il valore da regolare ogni 200ms e se supera il massimo del valore consentito viene portato a livello iniziale. Tutti e sei i while svolgono le stesse funzioni.

La configurazione dei fuses è la seguente:

 

SCHEMA

Lo schema è il seguente:

La tensione di alimentazione è 5V continui e stabilizzati. Sia il modulo DS3231 che il microcontrollore sono alimentati da questa tensione a 5V inoltre il modulo ha la batteria al litio da 3V a bottone per far si che non si stoppi il conteggio anche se la tensione di alimentazione non è presente. Il PIC16F877A è alimentato tramite i pin 11 e 32 connessi alla tensione positiva e i pin 12 e 31 connessi alla massa dell’alimentazione. Anche il display LCD è connesso alla tensione di 5V con la retroilluminazione del display connessa tra il positivo e massa tramite una resistenza da 470 Ohm. C5 serve per filtrare la tensione.

Il microcontrollore viene mantenuto in stato di funzionamento grazie alla resistenza di pull-up R1 mentre il circuito di oscillazione da 8MHz è formato dal cristallo di quarzo X1 da 8MHz e da C1 e C2.

Il pulsante “REG” porta una tensione alta sul pin 33 quando premuto mentre R3 lo mantiene a livello basso. C4 viene usato come condensatore antirimbalzo. La stessa cosa vale per il pulsante “INC”, R2 e C3.

Il modulo DS3231 è connesso al microcontrollore tramite le linee SCL e SDA. Teoricamente servirebbe una resistenza di pull-up su queste linee ma esse sono già presenti nel modulo DS3231.

Lo schema montato su breadboard è il seguente:

 

DOWNLOAD

Potete scaricare la simulazione del circuito con PROTEUS e il codice compilato con MIKROC al seguente LINK!!!




 


2649 Visite totali 32 Visite di oggi
DATA E ORA CON DS3231 E REGOLAZIONE ultima modifica: 2017-06-12T14:24:14+00:00 da ne555

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *