TEMPORIZZATORE 24 ORE CON DS3231

Schema con PIC18F252 e DS3231 per Realizzare un Temporizzatore per 24 Ore con Regolazione da Display degli Orari…

 

 

 

 




 

INTRO

Lo schema seguente sfrutta un RTC (ovvero real time clock) ovvero un vero e proprio orologio digitale in grado di inviare informazioni a microcontrollori tramite protocollo SPI per realizzare un timer ciclico 24 ore di precisione.

Con il seguente schema è possibile impostare l’ora e poi impostare il momento dell’accensione e dello spegnimento, in particolare è possibile impostare l’ora e i minuti di accensione e spegnimento, i secondi sono stati esclusi per semplicità, ma con una piccola modifica del codice è possibile inserirli.

Per quanto riguarda l’orario invece è possibile impostare ore, minuti e secondi, quindi il sistema funge da orologio e anche in questo caso con una piccola modifica è possibile inserire giorno, mese e anno, trascurati visto che non servono per l’applicazione.

I tempi sono comodamente regolabili grazie ad un display LCD e due pulsanti. Il primo, chiamato “regola” permette di entrare nel menù di regolazione e cambiare cosa si va a regolare, mentre il secondo pulsante chiamato “modifica valori” permette di incrementare i valori di ore, minuti, secondi e i tempi di accensione e spegnimento.

Essendo un timer 24h può anche essere impostato come accensione l’ora 8:00 e come spegnimento l’ora 07:00 facendo sì che rimanga acceso per 23 ore. L’uscita può essere un semplice LED con resistenza di protezione oppure un carico a tensione maggiore di 5V, inoltre si può anche collegare un relè e avere il controllo su tensioni a 230V.

Lo schema è realizzato con un PIC18F252 ma può essere realizzato con un qualsiasi microcontrollore che dispone di una comunicazione SPI. Per la programmazione dei PIC18 non si può usare il programmatore K150, ma usando i PIC16 questo ostacolo può essere superato.

 




 

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 necessarie
unsigned short minute  absolute 0x0021;
unsigned short hour    absolute 0x0022;
unsigned short dato    absolute 0x0023;
unsigned short ora_accensione, minuti_accensione;
unsigned short ora_spegnimento, minuti_spegnimento;
unsigned accensione, spegnimento, ora_min;
unsigned char regolazione = 0;                           //Variabile regolazione
unsigned char time[9];                                   //Stringhe di caratteri
char text[4];
const  unsigned char  SECONDS_ADDRES=0x00;               //Indirizzi modulo DS3231
const  unsigned char  MINUTES_ADDRES=0x01;
const  unsigned char  HOURS_ADDRES  =0x02;



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));
}

unsigned char BCDLowerPart(unsigned char bcd_var){                    //Sottoprogramma BCD to Decimale
   return ((bcd_var & 0x0F));
}

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);
  }
}

int Display_Lcd_Time(unsigned char time_vect){                     //Scrittura ora su LCD
  unsigned int or_mi;
  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';
  hour = (time[0]*10) + time[1];                                   //Ottieni valore ora e min
  minute = (time[3]*10)+ time[4];
  sec = (time[6]*10)+ time[7];
  time[0] += 48;                                                   //Trasforma in caratteri
  time[1] += 48;
  time[3] += 48;
  time[4] += 48;
  time[6] += 48;
  time[7] += 48;
  Lcd_Out(1,7,time);                                               //Visualizza su LCD
  or_mi = (hour * 100) + minute;
  return or_mi;                                                    //ritorna il valore di ore e minuti
}

void EXT_INT() iv 0x0008 ics ICS_AUTO {                            //Interrupt
  Delay_ms(300);
  regolazione ++;                                                  //Cambia regolazione
  INTCON.INT0IF = 0;                                               //reset del flag
}

void main(){                                                     //PORTB0 e 1 Ingressi
  TRISB.F0 = 1;
  TRISB.F1 = 1;                                                  //PORTC uscite
  TRISC.F0 = 0;

  INTCON.GIE = 1;                                                //attivo gli interrupt
  INTCON2.INTEDG0 = 1;                                           //interrupt sul fronte di salita
  INTCON.INT0IE = 1;

  Lcd_Init();                                                    //Initializza LCD
  Lcd_Cmd(_LCD_CLEAR);                                           //Pulisci LCD
  Lcd_Cmd(_LCD_CURSOR_OFF);                                      //Spegni cursore
  Delay_ms(1000);

  I2C1_Init(100000);                                             //I2C a 100KHz

  Write_CMD_DS3231(0x0E,0x00);                                   //Configura modulo ds3231
  Write_CMD_DS3231(0x0F,0xF8);

  ora_accensione = EEPROM_Read(0x00);                            //Leggi i valori memorizzati
  minuti_accensione = EEPROM_Read(0x01);                         //Di accensione e spegnimento
  ora_spegnimento = EEPROM_Read(0x02);
  minuti_spegnimento = EEPROM_Read(0x03);
  
  while(1){                                                      //Ciclo infinito
    while(regolazione == 0){
      Lcd_Cmd(_LCD_CLEAR);                                       //Scrivi data e ora
      Lcd_Out (1, 1, "TIME:");
      sec    = Read_TIMER_DS3231(SECONDS_ADDRES);                //Leggi data e ora e aggiorna
      minute = Read_TIMER_DS3231(MINUTES_ADDRES);
      hour   = Read_TIMER_DS3231(HOURS_ADDRES);
      ora_min = Display_Lcd_Time(time[9]);                       //Visualizza data e ora
      Delay_ms(500);
//Controlla se è il momento di accendere o spegnegnere il dispositivo sull'uscita
      if(accensione <= spegnimento){
        if((ora_min >= accensione) && (ora_min < spegnimento)) PORTC.F0 = 1;
        else PORTC.F0 = 0;
      }
      else {
        if(ora_min >= accensione) PORTC.F0 = 1;
        else if((ora_min >= 0) && (ora_min < spegnimento)) PORTC.F0 = 1;
        else PORTC.F0 = 0;
      }
    }


  while (regolazione == 1){                                    //Se regolazione = 1
    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 > 23) hour = 0;
      Delay_ms(200);
    }
  }

  while (regolazione == 2){                                   //Regolazione = 2 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 > 59) minute = 0;
      Delay_ms(200);
    }
  }

  while (regolazione == 3){                                  //Regolazione = 3 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 > 59) sec = 0;
      Delay_ms(200);
    }
  }

  if(regolazione == 4){                                     //Se regolazione = 4
    Lcd_Cmd(_LCD_CLEAR);
    sec    = IntToBCD (sec);                                //Converti da decimali a BCD
    minute = IntToBCD (minute);
    hour   = IntToBCD (hour);
    Write_TIMER_DS3231(SECONDS_ADDRES,sec);                 //Inserisci i valori nel modulo DS3231
    Write_TIMER_DS3231(MINUTES_ADDRES,minute);
    Write_TIMER_DS3231(HOURS_ADDRES,hour);
    regolazione ++;
  }
  while (regolazione == 5){                                 //Se regolazione = 5
    Lcd_Out (1, 1, "ORA ON:  ");                            //Regola ora accensione
    ByteToStr(ora_accensione, text);
    Lcd_Out_Cp(text);
    Lcd_Out_Cp("   ");
    if(PORTB.F1 == 1){
      Lcd_Cmd(_LCD_CLEAR);
      ora_accensione ++;
      EEPROM_Write(0x00, ora_accensione);
      if(ora_accensione == 25) ora_accensione = 0;
      Delay_ms(200);
    }
  }

  while (regolazione == 6){                                  //Regolazione = 6
    Lcd_Out (1, 1, "MINUTI ON: ");                           //Regola minuti accensione
    ByteToStr(minuti_accensione, text);
    Lcd_Out_Cp(text);
    Lcd_Out_Cp("   ");
    if(PORTB.F1 == 1){
      Lcd_Cmd(_LCD_CLEAR);
      minuti_accensione ++;
      EEPROM_Write(0x01, minuti_accensione);
      if(minuti_accensione == 60) minuti_accensione = 0;
      Delay_ms(200);
    }
  }
  while (regolazione == 7){                                  //Se regolazione = 7
    Lcd_Out (1, 1, "ORA OFF:  ");                            //Regola ora spegnimento
    ByteToStr(ora_spegnimento, text);
    Lcd_Out_Cp(text);
    Lcd_Out_Cp("   ");
    if(PORTB.F1 == 1){
      Lcd_Cmd(_LCD_CLEAR);
      ora_spegnimento ++;
      EEPROM_Write(0x02, ora_spegnimento);
      if(ora_spegnimento == 25) ora_spegnimento = 0;
      Delay_ms(200);
    }
  }

  while (regolazione == 8){                                   //Regolazione = 8
    Lcd_Out (1, 1, "MINUTI OFF: ");                           //Regola minuti spegnimento
    ByteToStr(minuti_spegnimento, text);
    Lcd_Out_Cp(text);
    Lcd_Out_Cp("   ");
    if(PORTB.F1 == 1){
      Lcd_Cmd(_LCD_CLEAR);
      minuti_spegnimento ++;
      EEPROM_Write(0x03, minuti_spegnimento);
      if(minuti_spegnimento == 60) minuti_spegnimento = 0;
      Delay_ms(200);
    }
  }

  if (regolazione == 9){                                           //se regolazione = 9
    accensione = (ora_accensione * 100) + minuti_accensione;       //Converti in decimale l'ora accensione
    spegnimento = (ora_spegnimento * 100) + minuti_spegnimento;    //Converti in decimale l'ora spegnimento
    Lcd_Out (1, 1, "Tempi impostati");                             //Mostra questa stringa
    Delay_ms(1000);
    regolazione = 0;
  }
 }
}

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, e inoltre vengono inizializzate tutte le variabili utili.

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 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 parte dell’ora 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 legge dalla memoria i valori di accensione e spegnimento poi si scrive l’ora e si aggiorna il valore ogni 500ms, inoltre si controlla se è il momento di accendere l’uscita (ovvero PORTC0) oppure mantenerla spenta in base all’ora e all’ora di accensione e spegnimento.

Per la regolazione si usa la variabile regolazione, se 1 si regola l’ora, 2 i minuti, 3 i secondi, 4 si scrivono questi valori nel modulo RTC mentre con i valori successivi si modificano i valori come visto prima dell’ora e minuti di accensione e spegnimento.

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 il tasto “modifica valore” è a livello logico alto allora si incrementa il valore da regolare ogni 200ms e se supera il massimo del valore consentito viene portato a 0.

La configurazione dei fuses è la seguente è quella standard iniziale di MIKROC.

 

SCHEMA

Lo schema del sistema escludendo lo stadio di uscita è 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 PIC18F252 è alimentato con il terminale positivo dei 5V connesso al pin 20 e la massa connessa ai pin 19 e 8.

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 4MHz è formato dal cristallo di quarzo X1 da 4MHz e da C1 e C2.

Il pulsante “regola” 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 “modifica valori”, 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 in alcuni o quasi tutti i moduli DS3231, quindi possono anche essere eliminate.

Gli schemi delle varie possibili uscite sono i seguenti:

Nel caso di un carico DC a bassa tensione e basse correnti (fino a 30-60V dipendentemente dalla VDS o VCE massima dei dispositivi e con un carico che assorbe una corrente opportuna per i dispositivi) è possibile usare un semplice mosfet come nella prima immagine da sinistra o come nella seconda. Nel caso con il BJT però si avrà una dissipazione di potenza maggiore che dipende dalla corrente.

Nel terzo e quarto caso vi è un relè la cui bobina viene eccitata dalla corrente che fluisce grazie al mosfet Q3 o al transistor Q4. Anche in questo caso VCc può essere maggiore di 5V e deve essere uguale alla tensione di funzionamento del relè. Nel caso del transistor BJT anche con il relè vi sarà una dissipazione di potenza maggiore rispetto al caso con relè e mosfet.

Lo schema (considerando come uscita un LED con resistenza di protezione) montato su breadboard è il seguente:

 

DOWNLOAD

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




 

[Voti Totali: 0 Media Voti: 0]
Segui la Nostra Pagina Facebook: Facebook

Lascia un commento

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