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!!!
Boa tarde meu amigo gostei do sei código
gostaria da ajuda do amigo
como faço para ter mas temporizador como temporizado1 temporizado 2.. e assim por diantes