Orologio \ Sveglia DCF77 RTC 6 digit 7 segmenti

Buongiorno a tutti!
Spesso ho trovato orologi che mostrano soltanto le ore e i minuti, regolati a mano con un RTC. Io invece vorrei fare meglio, vorrei realizzare un orologio \ sveglia radiocontrollato.

Nello specifico, vorrei utilizzare:

  • ATMega 328p in modalità "standalone", estraendo i due pin Tx e Rx per avere la possibilità di aggiornare il firmware direttamente sulla scheda con un semplice convertitore FTDI USB \ Seriale TTL;

Display:

  • 6 Digit 7-segmenti blu da 0.56in di altezza;
  • 4 led 3 mm blu;
  • segmenti comandati da un HFE4543 BCD to 7 segmenti decoder, attraverso un ULN2803;
  • cifre e led comandati da un HFE4028 BCD to decimal decoder, attraverso un TD62783AP;

Keyboard:

  • 4 bottoni in serie con resistenza di pull-up;
  • 1 switch;
  • 1 led DCF77 Error;
  • 1 led sveglia inserita;

Altro:

  • LM7805 per alimentare la parte "logica";
  • LM7809 per alimentare la parte di "potenza" dei led;
  • Buzzer;
  • Batteria tampone per RTC;
  • Modulo DCF77 ancora da decidere;
  • Alimentazione esterna con un semplice trasformatore a 12 volt;

Questo è lo schema elettrico dal quale ho preso spunto, naturalmente è diverso perché devo inserirci dentro il modulo RTC e sistemare i pin per l'ATMega328p. Sto rifacendo lo schema elettrico con KiCad ma non avendo già i componenti ci metto un po'.

(upload://4Rv2HkEbLVcwM83pv43de9NwSW.gif)]

Siccome sono alle prime esperienze serie con Arduino, volevo condividere con voi questo progetto, anche per avere dritte per quanto riguarda il codice. Per il momento ho già chiesto informazioni per quanto riguarda la gestione BCD e della temporizzazione attraverso millis() qui.

Nel prossimo post scriverò il codice che fino ad ora ho scritto anche grazie al vostro aiuto.

Mi scuso fin da subito per il mio livello di programmazione, ma spero anche grazie al vostro aiuto di imparare tanto.

Vi ringrazio tutti fin da subito!

Riccardo

IN ALLEGATO IL CODICE E IL PCB NON TERMINATI E LO SCHEMA ELETTRICO FINITO

DCF77_Clock_6_Digit_7-Segments.ino (46.6 KB)

Orologio - Sveglia DCF77 RTC 6 digit 7 segmenti.zip (171 KB)

Per il momento ho buttato giù questo codice:

/* ATMega 328P Standalone */
/* PORTD raggruppa in 1 byte di 8 bit i pin digitali da 0 a 7. Ricordiamo che i pin 0 e 1 sono rispettivamente Rx e Tx. L'ultimo bit corrisponde al pin digitale 0. */
/* PORTB raggruppa in 1 byte di 8 bit i pin digitali da 8 a 13. L'ultimo bit corrisponde al pin digitale 8. */
/* PORTC raggruppa in 1 byte di 8 bit i pin analogici da 0 a 5. L'ultimo bit corrisponde al pin analogico 0. */

#define LEDs 9                                    /* LED SEC 4 led blu che segnano i secondi. sono collegati al digit6 prima a sinistra delle ore. */
#define LEDd 10                                   /* LED DCF77 ERROR led rosso che si accende in caso di errore del DCF77 o dati incorretti. */
#define LEDa 11                                   /* LED ALARMled di inserimento dell'allarme. */
#define BUZ 12                                    /* BUZZER cicalina di allarme. */
#define LED_STATUS 13                             /* LED STATUS led di stato come su scheda ArduinoUNO. */
#define KEY A0                                    /* KEYBOARD pin analogico che legge i valori dei 4 bottoni */
#define SWa A1                                   /* SWITCH ALARM pin analogico che legge l'interruttore per la sveglia */


byte sLEDs = LOW;                                 /* variabile di stato LEDs */
#define intLEDshigh  250                          /* intervallo accensione 250 millisecondi LEDs */
#define intLEDslow 750                            /* intervallo spegnimento 750 millisecondi LEDs */
unsigned long pTempoLEDs = millis();              /* salva i millisecondi una volta all'accensione del processore per il loop dei LEDs, alla quale aggiungerò gli intervalli */
byte aBUZ = LOW;                                  /* variabile di stato buzzer attivata dall'allarme */
byte sBUZ = LOW;                                  /* variabile di stato buzzer */
#define intBUZhigh 750                            /* intervallo accensione 750 millisecondi Buzzer */
#define intBUZlow 250                             /* intervallo spegnimento 250 millisecondi Buzzer */
unsigned long pTempoBUZ = millis();               /* salva i millisecondi una volta all'accensione del processore per il loop del Buzzer, alla quale aggiungerò gli intervalli */


uint32_t pMultiplex = micros();                   /* salva i microsecondi una sola volta all'accensione del processore per il loop del multiplexing per il display 7 segmenti */

byte DATE[6] = {0, 0, 0, 0, 0, 0};                /* posizione 0 = decine giorno, posizione 5 = unità anno. */
byte TIME[6] = {0, 0, 0, 0, 0, 0};                /* posizione 0 = decine ore, posizione 5 = unità secondi. */
byte DIGIT = 0;                                   /* numero della cifra per il multiplexing, da 1 a 6, digit1 prima a destra dei secondi, digit6 prima a sinistra delle ore. */


void setup() {
  DDRD = DDRD | B11111100;                        /* richiamo il registro della porta D (pin digitali da 0 a 7) e setto i pin dal 2 al 7 in modalità output, senza cambiare i pin Tx e Rx per sicurezza. */
  DDRB = DDRB | B11111111;                        /* richiamo il registro della porta B (pin digitali da 8 a 13) e setto tutti i pin in modalità output. */
  DDRC = DDRC | B00000000;                        /* richiamo il registro della porta C (pin analogici da 0 a 5) e setto tutti i pin in modalità input. */
}

void multiplex() {
  if (++DIGIT > 6) DIGIT = 1;                     /* digit cicla da 1 a 6 */
  byte VALUE = TIME[6 - DIGIT];                   /* indice array cicla da 5 a 0 */
  PORTD = (PORTD & 3) | (DIGIT << 2) | (VALUE << 5);    /*  */
  PORTB = (PORTB & 0xFE) | (VALUE >> 3);          /*  */
}

void alarmBUZZER(){                               /* fa suonare il buzzer quando l'allarme è acceso */
  if(aBUZ=HIGH){                                  /* nel caso in cui la variabile aBUZ è alta, cioè se l'allarme è inserito */
    switch(sBUZ){                                 /* in base allo stado della variabile sBUZ ho i due casi */
      case HIGH:                                  /* caso in cui la variabile sBUZ è alta */ 
        if(millis() - pTempoBUZ >= intBUZhigh){   /* la variabile pTempoBUZ all'inizio è uguale a millis e la differenza è 0. Ciclando aumenta la differenza fino ad arrivare all'intervallo scelto */
          sBUZ = LOW;                             /* cambio lo stato della variabile */
          pTempoBUZ += intBUZhigh;                /* alla variabile pTempoBUZ aggiungo l'intervallo così da azzerare la differenza di prima */
          digitalWrite(BUZ, sBUZ);                /* porto bassa l'uscita */
        }
        break;                                    /* blocca qui l'if */
      case LOW:                                   /* caso in cui la variabile sBUZ è bassa */
        if(millis() - pTempoBUZ >= intBUZlow){    /* la variabile pTempoBUZ all'inizio è uguale a millis e la differenza è 0. Ciclando aumenta la differenza fino ad arrivare all'intervallo scelto */
          sBUZ = HIGH;                            /* cambio lo stato della variabile */
          pTempoBUZ += intBUZlow;                 /* alla variabile pTempoBUZ aggiungo l'intervallo così da azzerare la differenza di prima */
          digitalWrite(BUZ, sBUZ);                /* porto alta l'uscita */
        }
    }
  }
}

void blinkLEDs(){                                 /* lampeggia i 4 led 3 mm blu */
  switch(sLEDs){                                  /* in base allo stato della variabile sLEDs ho i due casi */
    case HIGH:                                    /* caso in cui la variabile sLEDs è alta */
      if(millis() - pTempoLEDs >= intLEDshigh){   /* la variabile pTempoLEDs all'inizio è uguale a millis e la differenza è 0. Ciclando aumenta la differenza fino ad arrivare all'intervallo scelto */
        sLEDs = LOW;                              /* cambio lo stato della variabile */
        pTempoLEDs += intLEDshigh;                /* alla variabile pTempoLEDs aggiungo l'intervallo così da azzerare la differenza di prima */
        digitalWrite(LEDs, sLEDs);                /* porto bassa l'uscita */
      }
      break;                                      /* blocca qui l'if */
    case LOW:                                     /* caso in cui la variabile sLEDs è bassa */
      if(millis() - pTempoLEDs >= intLEDslow){    /* la variabile pTempoLEDs all'inizio è uguale a millis e la differenza è 0. Ciclando aumenta la differenza fino ad arrivare all'intervallo scelto */
        sLEDs = HIGH;                             /* cambio lo stato della variabile */
        pTempoLEDs += intLEDslow;                 /* alla variabile pTempoLEDs aggiungo l'intervallo così da azzerare la differenza di prima */
        digitalWrite(LEDs, sLEDs);                /* porto alta l'uscita */
      }
  }
}

void loop() {

  if (micros() - pMultiplex > 3300) {             /* la variabile pMultiplex all'inizio è uguale a micro e la differenza è 0. Ciclando aumenta la differenza fino ad arrivare 33000 microsecondi */
    pMultiplex += 3300;                           /* alla variabile pMultiplex aggiungo 3300 microsecondi così da azzerare la differenza di prima */
    multiplex();                                  /* accende la prossima cifra */
  }
  alarmBUZZER();                                  /* fa suonare il buzzer quando l'allarme è acceso */
  blinkLEDs();                                    /* lampeggia i 4 led 3 mm blu */
}

Per leggere i bottoni ho pensato a questo codice, siccome vorrei dividere tutto il programma in funzioni, senza caricare tutto nella funzione loop.

#define KEY A0                                    /* KEYBOARD pin analogico dei 4 pulsanti */
#define SWa A1                                    /* SWITCH Alarm pin analogico dell'interruttore della sveglia */

int KEYBOARD = 0;                                 /* salva i valori della tastiera per poi confrontarli */
byte SWalarm = 0;                                 /* salva i valori dell'interruttore per poi confrontarli */

void readKEYBOARD(){                              /* legge i valori dei 4 bottoni e dell'interruttore */
  KEYBOARD = analogRead(KEY);                     /* salvo nella variabile KEYBOARD il valore del pin analogico 0, cioè dei 4 bottoni */
  if (KEYBOARD < 60) {return 1;}                  /* se minore di 60 ritorna il valore 1 */
  /* Time \ Date */
  else if (KEYBOARD < 200) {return 2;}            /* se nessuno dei casi precedenti e minore di 200 ritorna il valore 2 */
  /* Alarm */
  else if (KEYBOARD < 400) {return 3;}            /* se nessuno dei casi precedenti e minore di 400 ritorna il valore 3 */
  /* Snooze */
  else if (KEYBOARD < 600) {return 4;}            /* se nessuno dei casi precedenti e minore di 600 ritorna il valore 4 */
  /* Time Zone */
  return 0;                                       /* in ogni altro caso ritorna il valore 0 */       
  SWalarm = analogRead(SWa);                      /* salvo nella variabile SWalarm il valore del pin analogico 1, cioè dell'interruttore */
  if(SWalarm > 511) {return 5;}                   /* se l'interruttore è acceso fai questo */
  else return 6;                                  /* se l'interruttore è spento fai questo */
}

void loop()}{
  readKEYBOARD();                                 /* legge i valori dei 4 bottoni e dell'interruttore */
}

Naturalmente i valori per dividere i bottoni li deciderò quando proverò il circuito e al posto di ritornarmi i numeri vorrei dare delle istruzioni.

Volevo sapere se ci sono metodi migliori per questa gestione.

Inoltre volevo un parere su quale RTC utilizzare e su quale modulo DCF77 usare.
Siccome i PCB me li faccio io, per me non è un problema inserire il circuito elettronico dell'RTC direttamente sulla mia scheda, anche con il porta batteria. Mi basta avere uno schema elettrico e le sigle dei componenti per vedere i datasheet. Per quanto riguarda il modulo DCF77 invece credo che ci siano sei filtri dentro e preferirei prenderlo già fatto (e quindi funzionante) perché so che è delicato e il segnale poche volte si riceve. Magari una schedina che posso connettere con i connettori a strip.

Per quanto riguarda la gestione del DCF77 pensavo di usare l'apposita libreria DCF77 che però non ho capito se utilizza un pin specifico o meno, mentre per il modulo RTC lo vorrei usare via i2c. Non ho una libreria a portata di mano, ma ho visto già alcuni progetti con moduli RTC.

ricki158:
Per il momento ho buttato giù questo codice:

void alarmBUZZER(){

if(aBUZ=HIGH){

Doppio errore, una svista sintattica (l'assegnamento invece dell'uguaglianza), e una logica, portando a LOW aBUZ durante il suono del buzzer, il buzzer non smette più di suonare.

l'apposita libreria DCF77 che però non ho capito se utilizza un pin specifico o meno

Da quanto ho letto l'altra volta viene usato il pin digital 2 di Arduino, questo perché tale pin, e anche il 3, ha una gestione più completa degli interrupt. Quella libreria per non essere bloccante funziona ad interrupt attivati dai cambiamenti dello stato del pin, ma volendo si potrebbe anche non usarla, e scrivere un grosso switch (attivato alla frequenza di multiplex) che decodifica "a mano" la trama un bit per volta, in tal caso si potrebbe usare il pin di ingresso che si preferisce.

Per quanto riguarda l'RTC (consigliato un modulo con DS3231) basta la sola libreria Wire.h per dialogare con il 3231 tramite bus i2c.

Claudio_F:
Doppio errore, una svista sintattica (l'assegnamento invece dell'uguaglianza), e una logica, portando a LOW aBUZ durante il suono del buzzer, il buzzer non smette più di suonare.

Hai ragione! Io ho comunque collegato un led all'uscita e lampeggiava esattamente per i secondi che imponevo io. Ho inserito la variabile perché se SWalarm (cioè la variabile dell'interruttore per innescare l'allarme) è alto (o basso, dipende dal circuito) e l'orario coincide con quello impostato, la variabile passa su HIGH e parte il ciclo switch... case.

Da quanto ho letto l'altra volta viene usato il pin digital 2 di Arduino, questo perché tale pin, e anche il 3, ha una gestione più completa degli interrupt. Quella libreria per non essere bloccante funziona ad interrupt attivati dai cambiamenti dello stato del pin, ma volendo si potrebbe anche non usarla, e scrivere un grosso switch (attivato alla frequenza di multiplex) che decodifica "a mano" la trama un bit per volta, in tal caso si potrebbe usare il pin di ingresso che si preferisce.

Farlo "a mano" significa avere un buon numero di conoscenze che posso acquisire solo con il vostro e tuo aiuto. Ho letto un po' questo topic dove dovrebbe appunto farlo "a mano". Ma devo appunto finire di leggerlo.

Per quanto riguarda l'RTC (consigliato un modulo con DS3231) basta la sola libreria Wire.h per dialogare con il 3231 tramite bus i2c.

Si questo me lo avevi detto in un altro topic. Volevo chiedere poi quali altri componenti devo mettere vicino al DS3231. Il modulo non vorrei prenderlo e, siccome ho spazio nella scheda vicino al processore ATMega, vorrei utilizzare componenti Through Hole su un circuito che mi realizzo io, con tanto di porta batteria tampone.

Mentre invece quale modulo DCF77 posso utilizzare? Avresti qualche link? Su eBay trovo tante cose, con antenne di dimensione diversa e non so quale andrebbe bene. Nel mio negozio di elettronica i moduli DCF77 non li vendono più, non li trattano neanche su ordinazione.

ricki158:
Per leggere i bottoni ho pensato a questo codice, siccome vorrei dividere tutto il programma in funzioni, senza caricare tutto nella funzione loop.

#define KEY A0                                    /* KEYBOARD pin analogico dei 4 pulsanti */

readKEYBOARD();                                /* legge i valori dei 4 bottoni e dell'interruttore */
}




Naturalmente i valori per dividere i bottoni li deciderò quando proverò il circuito e al posto di ritornarmi i numeri vorrei dare delle istruzioni.

Volevo sapere se ci sono metodi migliori per questa gestione.

fare sketch senza qualche specie di hardware collegato (anche solo un accrocchio di breadboard)
è una gran perdita di tempo perché non si sa mai quello che si è scritto veramente.

con un hardware collegato ci sono alte probabilità di trovare subito le "sviste" o peggio, man mano che lo completi.

fare un circuito stampato funzionante con 4-5 integrati per chi è all' inizio potrebbe essere uno scoglio insormontabile

Se hai un RTC, il DCF77 non lo vedo una comodità.

Ho una shield con LCD + 6 pulsanti dove gira un codice che funziona. Questo codice deriva da quello, solo che ho voluto fare tutto in una funzione, per cui alla peggio utilizzo quel codice. Posso anche provare questo codice su quella shield.

L'unica comodità del DCF77 è che non devo impostare l'ora.

Come antenna + modulo DCF77 prenderò questo.
Alla fine mi sono deciso a prendere il modulo RTC DS3231 anziché farmelo da me, anche perché l'integrato non esiste in Through Hole.
C'è qualche modulo DCF77 migliore?

ricki158:
Come antenna + modulo DCF77 prenderò questo

Sembra interessante, sul datasheet però non dice che tipo di uscita è quella dati (open collector, push-pull ecc) ne che tensione sopporta, sarà da fare qualche prova per vedere come collegarsi senza danni (il modulo funziona a 3.3V).

Migliori? Boh. Non è un genere molto trattato e sicuramente non si trova in negozio... Mi sembra solo un po'strana l'assenza del condensatore di sintonia a ridosso della bobina (serve per aumentare il Q) che si vede in altre foto.

Interessa anche a me come possibile sostituto/rifacimento del mio ricevitore realizzato interamente "a mano" nel 2003 a partire dall'antenna avvolta con il filo di una bobina di relé.

EDIT: anzi, su questo datasheet è specificato che si tratta di un'uscita push-pull, alta in presenza degli impulsi (corrispondenti alla riduzione dell'ampiezza della portante radio), in grado di erogare o assorbire circa 5µA... quindi bisogna interporre un buffer adattatore di livello (un paio di transistor, o un operazionale).

Intanto ho fatto lo schema elettrico per l'ATMega 328 P che allego qui di seguito.

Ho preso quel modulo allora, ma ti chiederò uno schema elettrico per il buffer adattatore di livello.

DCF77 Clock.pdf (123 KB)

Ma tal modulo DCF77, in certe regioni italiane è facile che fallisce più delle volte che funziona. Diciamo dagli Appennini in giù.

E beh, vediamo quando gli arriva se riusciamo a tirare fuori un LED pulsante in modo corretto :wink:

Intanto sullo schema vedo già un problema di alimentazione, quel modulo dcf funziona a 3.3V, e temo che anche la sua uscita non si possa collegare direttamente ad un pin di Arduino.

Per l'alimentazione potrebbe bastare resistenza da 330 Ω e zener da 3.3V

Non ho capito la funzione di tutti quei pulsanti.

Il LED dcf error (che lampeggerebbe quasi sempre) mi sembra meno utile di un LED sync ok (che invece indicherebbe ora sincronizzata e ricezione perfetta)... o meglio, per completezza, cioè capire sia quando le cose non vanno bene, sia quando vanno, servirebbero tutti e due. Più uno per gli impulsi grezzi emessi dal modulo... così si vede visivamente lo stato della ricezione e dell'orientamento dell'antenna.

Per quanto riguarda leggere la trama DCF77 a mano mi sono divertito a riscrivere in C più o meno il procedimento di quello che quattordici anni fa ho scritto in assembly per PIC16F628 (Arduino ancora non esisteva :'(). Non c'è niente di testato... meglio non quotare il codice visto che sicuramente sarà da correggere.

La trama di 59 bit (ricevuta domenica 7 settembre 2003 alle 12:37) è fatta così :

I dati sono inviati in formato BCD compatto, i bit meno significativi vengono trasmessi per primi. I bit rossi sono il controllo di parità pari (in pratica facendo lo xor dei bit ricevuti, compresa la parità, il risultato deve sempre essere zero). Il primo bit è sempre 0 (forse), il ventunesimo sempre 1 (sicuro).

void dcf()
{
    byte pulse = digitalRead(dcfPin);
    switch (dcfStat)
    {
        case 0: // restart processo
            dcfCnt = 0;
            dcfStat = 1;
            nBit = 1;
            bitXor = 0;
            break;

        case 1: // attesa secondo di sync
            if (pulse) { dcfStat = 0; break; }
            if (++dcfCnt == 400) dcfStat = 2;
            break;

        case 2: // attesa impulso
            if (pulse) { dcfCnt = 0; dcfStat = 3; }
            break;

        case 3: // misura impulso
            ++dcfCnt;
            if (!pulse)
            {
                if (dcfCnt < 16)     { dcfStat = 8; break; } // errore corto
                if (dcfCnt < 60)     bitRx = 0;
                else if (dcfCnt<104) bitRx = 1;
                else                 { dcfStat = 8; break; } // errore lungo
                dcfStat = 4;
            }
            break;

        case 4: // elabora bit
            if (nBit == 1 && bitRx)   { dcfStat = 8; break; } // errore start0
            if (nBit == 21 && !bitRx) { dcfStat = 8; break; } // errore start1
            bitXor ^= bitRx;
            if (nBit == 29 && bitXor) { dcfStat = 8; break; } // errore parita`
            if (nBit == 36 && bitXor) { dcfStat = 8; break; } // errore parita`
            rxData = (rxData >> 1) | (unsigned int)(bitRx << 15);
            dcfCnt = 0;
            if (nBit == 36)  // fine sezione ore minuti
            {
                rxData >>= 1;
                rxMinute = rxData & 0x0F;
                rxData >>= 4;
                rxMinute = 10*(rxData & 7) + rxMinute;
                rxData >>= 4;
                rxHour = rxData & 0x0F;
                rxData >>= 4;
                rxHour = 10*(rxData & 3) + rxHour;
                if (rxMinute > 59 
                    || rxHour > 23) { dcfStat = 8; break; } // errore valori
                dcfStat = 6 ;
                break; 
            }
            dcfStat = 5;
            break;

        case 5: // attesa 600 ms
            if (++dcfCnt == 240) { ++nBit; dcfStat = 2; }
            break;

        case 6: // attesa 24 sec
            if (++dcfCnt == 9600)
            {
                if (rxMinute == (priorRxMinute + 1) % 60
                    && rxHour == priorRxHour) rxOK = true;
                priorRxMinute = rxMinute;
                priorRxHour = rxHour;
                dcfCnt = 0;
                nBit = 1;
                bitXor = 0;
                dcfStat = 2;
            }
            break;

        case 8: // errore generale di ricezione
            rxError = true;
            dcfStat = 0;
    }

}

La funzione dcf va chiamata alla frequenza di multiplex (2.5ms in base a quanto scritto in questo post), legge il pin dcfPin (può essere un pin qualsiasi) e imposta a true due flag rxOK o rxError. Scrive l'ora ricevuta nelle variabili rxMinute (da 0 a 59) e rxHour (da 0 a 23). Effettua controlli su:

  • durata singoli impulsi
  • valore corretto dei bit di start e parità
  • valore orario sensato
  • da per buona l'ora (rxOK=true) quando si ricevono due trame consecutive OK con differenza di un solo minuto

Nota: ricava solo l'orario, fino al 36esimo bit, e ignora i successivi 23.

Per funzionare usa il seguente gruppo di definizioni/variabili globali:

#define       dcfPin A2          // pin Arduino input impulsi dcf
byte          nBit;              // numero del bit letto 1..60
byte          dcfStat = 0;       // stato processo dcf
unsigned int  dcfCnt;            // contatore 16 bit processo dcf
byte          bitRx;             // valore bit appena ricevuto
byte          bitXor;            // xor di tutti i bit ricevuti
unsigned int  rxData;            // scorrimento bit ricevuti in campo 16 bit
byte          rxMinute;          // minuto appena ricevuto
byte          rxHour;            // ora appena ricevuta
byte          priorRxMinute = 0; // minuto ricevuto precedente
byte          priorRxHour = 0;   // ora ricevuta precedente
bool          rxOK = false;      // true quando ricevuti dati OK
bool          rxError = false;   // true quando errore qualsiasi

Intanto ti ringrazio, dedichi tempo ad una tecnologia ormai praticamente obsoleta!

Per quanto riguarda le funzioni dei tasti e dei LED dico subito che il progetto è riciclato dallo schema elettrico che ho messo nei primi post, ma nulla mi vieta di cambiarlo un attimo. La cosa che mi interessava imparare di più è la gestione del display in quella maniera, tramite BCD.

Per l'alimentazione posso fare grezzamente un partitore di tensione ed ho risolto.

Per quanto riguarda il tuo codice devo analizzarlo un po' e capirlo.

Grazie intanto per il tuo prezioso contributo!

Ho dato un occhio al tuo codice. Dovrei tutto commentarlo bene per capire esattamente. Certe cose le capisco anche io che sono nuovo, però dovrei scrivere i commenti dettagliati per capire perfettamente e usare queste nozioni in futuro.

Penso che si possa fare la stessa cosa fatta in case 4 anche per la data senza problemi.

Per quanto riguarda i bit nominati come GS? Che cosa sarebbero?

Quindi se non so i secondi, dovrei aggiungere 1 fino a 60 nei secondi dell'RTC e azzerare i secondi e segnarmi le ore e i minuti quando cambiano i minuti rxMinute e rxOK è true.

Effettivamente con quelle due flag sarebbe bello avere i due led, magari uno verde e uno rosso. Oppure magari tenerlo acceso fisso se rxOK = true e rxErr = false e farlo blinkare se rf = false e rxErr = true.

p.s.: ma è vero che dovrebbero esserci dei bit codificati che danno informazioni sul meteo? E come potrebbero essere decifrati? Solo per sapere come funziona, non tanto per inserirlo in questo progetto.

ricki158:
Penso che si possa fare la stessa cosa fatta in case 4 anche per la data senza problemi.

Si, però va rivista l'ampiezza del campo di input (rxData) in cui scorrono i vari bit ricevuti. Nel caso dell'ora in 16 bit troviamo tutto, la data però è più lunga.

I bit GS sono il giorno della settimana.

Quindi se non so i secondi, dovrei aggiungere 1 fino a 60 nei secondi dell'RTC e azzerare i secondi e segnarmi le ore e i minuti quando cambiano i minuti rxMinute e rxOK è true.

L'RTC ha i secondi, quindi basta leggerli e visualizzarli. Nel momento in cui rxOK diventa true si regola l'RTC con ora/minuti ricevuti e zero per i secondi (che comunque quando tutto è sincronizzato dovrebbero già diventare zero per conto loro).

Effettivamente con quelle due flag sarebbe bello avere i due led, magari uno verde e uno rosso. Oppure magari tenerlo acceso fisso se rxOK = true e rxErr = false e farlo blinkare se rf = false e rxErr = true.

Nota che la funzione dcf le setta solamente. A resettarle (rimetterle subito a false) ci deve pensare la parte di programma esterna che si incaricherà di regolare l'RTC e comandare il/i LED opportuni. Sono cioè due variabili "di evento" (o se vuoi messaggi dalla funzione dcf al resto del programma) che segnalano il momento in cui si verifica qualcosa, e che vanno subito ripristinate.

La logica può essere questa: se trovo rxOK true aggiorno RTC e accendo LED sync, se invece trovo rxError true spengo LED sync e flash su LED error. In ogni caso rimetto subito a false le variabili di segnalazione.

Anche in questo caso le temporizzazioni vanno fatte con millis o conteggiando i cicli di multiplex (come fa la funzione dcf), rimane inviolabile il tempo di multiplex di 2.5ms entro cui devono essere eseguite e terminare tutte le funzioni del programma (indicativamente la lettura o scrittura sull'RTC impiega 0.6ms).

I bit meteo sono quelli indicati con riservati. Non so cosa indicano, bisogna goglare con dcf77 decode o qualcosa del genere.

So che dovrebbero essere criptati e che per poter decriptarli dovresti pagare una licenza. Hai presente le stazioni barometriche con orologio / sveglia tipo Oriegon Scientific? Che mostrano anche il tempo. Secondo me è una cosa di quel genere lì.

Sul discorso temporizzazioni sono ancora un po' spaesato ma ci sto capendo qualcosa. Però non capisco come mai mi dici di fare una temporizzazione di 2.5 mS come quella della funzione che accende il display se, usando millis() per temporizzare, il programma non mi si blocca e non ho alcun problema. Perché sono obbligato a usare la stessa temporizzazione oppure un multiplo?

Per quanto riguarda il LED, siccome non credo di poterne inserire un altro, o meglio attualmente non ho lo spazio, vorrebbe dire rifare il PCB, volevo usare se è OK luce fissa, se c'è un errore un blink dello stesso LED, ma se al termine di una lettura del codice dcf le due flag mi tornano a false, non riesco a blinkare il LED.

ricki158:
Perché sono obbligato a usare la stessa temporizzazione oppure un multiplo?

Non sei obbligato, è una possibilità. Nella funzione dcf, visto che viene chiamata ogni 2.5ms so già che ad ogni ciclo sono passati 2.5ms senza bisogno di interrogare millis. L'unico obbligo è che l'intero programma (cioè la somma della durata di tutte le funzioni chiamate) deve ciclare entro 2.5ms per non avere il display sfarfallante.

ma se al termine di una lettura del codice dcf le due flag mi tornano a false, non riesco a blinkare il LED

I due flag servono per dare il via ad un'operazione. Anche se vengono subito riportati a zero l'operazione può poi continuare per il tempo che serve. In sostanza nel mainloop si può scrivere qualcosa del genere:

void loop()
{
    if (micros() - loopTime >= 2500)
    {
        loopTime += 2500;
        multiplex();
        dcf();
    }

    if (rxOK)         { rxOK = false; writeRTC(); ledStat = 0; }
    else if (rxError) { rxError = false; ledStat = 1; }    

    gestLED();
}

dove ledStat non è lo stato acceso o spento del LED ma lo stato di un processo di controllo del LED che va avanti per i fatti suoi ad esempio così:

void gestLED()
{
    switch(ledStat)
    {
        case 0:  // sync ok, accende LED
            digitalWrite(dcfLedPin, 1);
            ledStat = 3;
            break;

        case 1:  // error, blink 200 ms
            digitalWrite(dcfLedPin, 1);
            ledTime = millis();            
            ledStat = 2;
            break;

        case 2:  // attende 200 ms
            if (millis() - ledTime > 200)
            {
                digitalWrite(dcfLedPin, 0);
                ledStat = 3;
            }
            break;

        case 3:  // nessuna operazione, attesa cambio stato
            break;
    }
}

Piuttosto che micros() userei timer1 con interrupt a 2,5 millisecondi che mi chiama la funzione.

Grazie per le risposte! In questi ultimi giorni ero preso dalla sessione d'esami e non ho guardato molto il codice. Comunque mi deve arrivare ancora il modulo DCF77. Il modulo RTC mi è appena arrivato invece e non vedo l'ora di provarlo.