DCF77 atomic clock using 1602 type LCD, sets MCU RTC and goes into Deep Sleep, waking every second to update LCD time. Re acquires DCF time every 24 hours, tests for available signal and indicates DCF pulse width in millisecond's. Time zone can be changed at the beginning of the code. If no signal detected, the program sleeps updating the display with the current RTC time then try's again in 1 hour. The RTC will remain accurate until power is cycled where it will reset the RTC registers. Hard reset will not effect RTC registers. Tested using a project MCU board, MCU Deep Sleep power is 3uA, 1602 LCD is 900uA. Wake up takes around 12mS to update display time every second. The current FRDM board has a PCB track issue that does not allow the power to be monitored.

Dependencies:   TextLCD WakeUp mbed

main.cpp

Committer:
star297
Date:
2014-02-26
Revision:
0:f40db885ccfb

File content as of revision 0:f40db885ccfb:

#include "mbed.h"
#include "TextLCD.h"
#include "WakeUp.h"

TextLCD lcd(PTB6, PTA12, PTB5, PTA11, PTB1, PTB2, TextLCD::LCD20x4); // rs, e, d4, d5, d6, d7
InterruptIn dcfSignalIn(PTB0); //DCF signal input,conection of output non-inverting module

// Include the definition for required Time Zone
#define TimeZone 1    //GMT
//#define TimeZine 2    //CET
//#define TimeZone 3    //EET

DigitalOut dcfLED(LED1);
DigitalOut WakeLED(LED3);

//variable
char paritycheck,paritym,parityu,paritydmy;
char testu,testm,testdmy;
char minh,minl;
char hourh,hourl;
char day,dayh,dayl;
char monthh,monthl;
char yearh,yearl;
char summertime;
char buffer[32];
int interrupt_counter = 0;
int hour = 0;
int minute = 0;
int second = 0;
int dayofweek = 6;
int dayofmonth = 1;
int month = 1;
int year = 0;
int nosignal,signal_error,start,dcf_sec,sync,RTCset,getDCF;
int p_minute,p_hour,p_dayofweek,p_dayofmonth,p_month,p_year; 
int dcf_error,error_count,dcf_good,signal_good,ISRlock;
int n,loop;
int dcf_array[61];

typedef unsigned char byte;
  
//makro,s
void dcfISR(),dcf_check(),checkSIGNAL(),signal();
void parity_calc();
void RTC_reset(),RTC_set();
void Displayloop();
void setTIME();
void restart();
void dcfIRQ();

Ticker Ticker50ms;
Timer check;
#define ZERO 1e-10
#define isBetween(A, B, C) (((A-B) > -ZERO) && ((A-C) < ZERO))

struct status {
    bool is_leap;           // Leap year flag (NOT actually transmitted but calculated)
    bool sample50;          // dcf sample at 50mS into the start of the second
    bool sample150;         // dcf sample at 150mS into the start of the second
    int  second;            // dcf second (NOT actually transmitted but calculated)
} 
dcf_status;

struct dcf {
    char dut1;               // DUT1 (0.1 - 0.8)
    char dut2;               // DUT2 (-0.1 - -0.8)
    char year;               // Year (00 - 99)
    char month;              // Month (01 - 12)
    char dayofmonth;         // Day of month (01 - 31)
    char dayofweek;          // Day of week (Sunday=0 Saturday=6)
    char hour;               // Hour (00 - 23)
    char minute;             // Minute (00 - 59)
} dcf_time;

struct tm t;

// Various text strings used in display
char weekDayName[7][4] = {"Sun","Mon","Tue","Wed","Thu", "Fri","Sat"};
char monthName[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
char MAX_DAY[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};  // Max days in a month for a non-leap year
char leapText[2][4] = {" ", "L"};
char dstSymbol[2][3] = {"WT", "ST"};

byte maxDay(byte year, byte month)  // Return the maximum day in month for a given month & year
{
    byte lastday, leap;
    leap = year%4 == 0 && year%100 !=0 || year%400 == 0;
    lastday = MAX_DAY[month - 1];
    dcf_status.is_leap = leap > 0 ? 1 : 0;
    if ((leap > 0) && (month == 2))
        lastday++;
    return lastday;
}

int dayOfWeek(int y, int m, int d)   // 0 = Sunday
{
    static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
    y -= m < 3;
    return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}

int main()
{        
    lcd.printf("DCF77 clock\n"); 
    lcd.printf("Checking Signal");
    WakeLED=1;dcfLED=1;
    WakeUp::calibrate();
    wait(1);
    checkSIGNAL();dcfSignalIn.rise(NULL);lcd.cls();
    if (signal_good==0) {
        lcd.printf("No Signal!!\n"); 
        lcd.printf("Retry in 1 hour");
        wait(2);lcd.cls();n=3600;
        getDCF=0;
    }
        else{
            lcd.printf("DCF detected\n"); 
            lcd.printf("Setting RTC");
            wait(2);lcd.cls();
            restart();lcd.cls();
            }   
                
    while (true){
    
        while (getDCF==1) {     // Get dcf time and set cpu RTC
            if (loop==0) {
              Displayloop();    // update display every 50mS    
                loop=1;       
            }
        }            

        while (getDCF==0) {     // Deep Sleep mode, Wake and update display time every second
            WakeLED=0;
            time_t seconds = time(NULL);
            strftime(buffer, 32, "%I:%M:%S %p", localtime(&seconds));
            lcd.locate(0,0);lcd.printf("RTC %s", buffer);
            strftime(buffer, 32, "%a %d %b %Y ", localtime(&seconds));
            lcd.locate(0,1);lcd.printf("%s", buffer);
            WakeLED=1;
            n--;if (n==0) {getDCF=1;} // Restart and Synchronise DCF every 1 or 24 hours
            WakeUp::set(1);
            deepsleep();
        } 
       checkSIGNAL();dcfSignalIn.rise(NULL);lcd.cls();
        if (signal_good==1) {restart();}
            else {n=3600;getDCF=0;}     
    }
}    

void Displayloop()
{   
   if (interrupt_counter == 0) {
        if (dcf_good == 1 && dcf_sec == 2) {
            RTC_set();      // set cpu RTC if good data        
        }        
    time_t seconds = time(NULL);
    strftime(buffer, 32, "%a %d %H:%M:%S", localtime(&seconds));
    lcd.locate(0,1);lcd.printf("%s", buffer);                                  
  }
  
  if (interrupt_counter==3){   
        if (start==1){
            dcf_array[dcf_sec]=dcf_status.sample150;
                if (dcf_sec==0){
                    dcf_array[58] = 0;
                }                                                                            
        }
  }                    
             
  if (interrupt_counter==5) {       
        if (signal_error==1){
            Ticker50ms.detach();dcfSignalIn.rise(NULL);getDCF=0;lcd.cls();
            lcd.printf("10 Signal errors\n"); 
            lcd.printf("detected,restart");
            wait(2);            
            lcd.cls();restart();}    // Restart DCF ISR if signal 10 errors detected              
  }   
}

void setTIME() // Set time variables with Partity data
{
            second = dcf_sec;
            minute=p_minute;
            hour=p_hour;
            dayofweek=p_dayofweek;
            dayofmonth=p_dayofmonth;
            month=p_month;
            year=p_year;
}

void checkSIGNAL()
{
   n=10;dcfSignalIn.rise(signal);
   while(n>0){        
    if (ISRlock==0){
        lcd.locate(0,0);
        if (signal_good==1) {lcd.printf ("Signal Good   %d ",n);}
         else {lcd.printf("No Signal     %d ",n);}
        signal_good=0;
    }
    wait(1);
    n--;
   } 
}    
void signal()
{
check.start();ISRlock=1,signal_good=1; 
while (dcfSignalIn==1){};
check.stop();
lcd.locate(0,1);lcd.printf ("DCF pulse %3.f mS",(check.read()*1000));
check.reset();ISRlock=0;    
}

void restart()
{
hour = 0;minute = 0,second = 0,dayofweek = 6;dayofmonth = 1;month = 1;year = 0;    
signal_error=0;sync=0;getDCF=1;RTCset=0;error_count=0;
start=0;dcf_sec=0;dcf_good=0;
dcfSignalIn.rise(dcfIRQ); //Trigger dcfISR on rising edge of DCF pulse
while(dcfSignalIn == 1){}
while(dcfSignalIn == 0){}
interrupt_counter = 0;sync=1;dcf_sec=0;
}

void dcfIRQ(void)   // DCF Signal Interrupt to synchronise Ticker ISR
{    
    if (sync==0){
            Ticker50ms.attach(& dcfISR, .05);            
            interrupt_counter = 0;sync=1;dcf_sec=0;
    }   
}

void dcfISR()  //Interrupt service routine (ISR) that is called every 50ms 
{   
    interrupt_counter++;loop=0;
     
    dcfLED = !dcfSignalIn;  // Show dcf Signal state on LED    
     
    if (interrupt_counter == 20) { // 50mS x 20 = 1000mS = 1 Second
        interrupt_counter = 0; second++;
        if (start==1) {dcf_sec++;}              
    }
    lcd.locate(0,0); lcd.printf("%2d:%02d:%02d.%d Dcf%02d",hour,minute,second,interrupt_counter*5/10,dcf_sec); 
      
        if (dcf_sec==59) {dcf_check();}
        if (dcf_good==1 && dcf_sec==1) {setTIME();}           
        if (second >= 60)   {++minute;second -=60;}
        if (minute >= 60)   {++hour;minute-=60;}
        if (hour >= 24)     {hour -=24;++dayofweek;++dayofmonth;}
        if (dayofweek > 6)  dayofweek = 0;
        if (dayofmonth >    maxDay(year, month)) {dayofmonth = 1;month++;}
        if (month > 12)     {month = 1;year++;}
        if (year > 99)      year = 1;           
      switch (interrupt_counter) {          
            case 1:{  // 50mS after start of second pulse
                dcf_status.sample50 = (dcfSignalIn);
               break;}
            case 3: { // 150mS after start of second pulse (bit "1" dcf Data)
                dcf_status.sample150 = (dcfSignalIn);                     
                 if (!dcf_status.sample150 && !dcf_status.sample50) {sync=0;start=1;}   // Reset Ticker ISR to line up with 59th second pulse 
               break;}
            case 6: { // 300mS after start of second (signal error if true)
                 if (dcfSignalIn) {dcf_error = 1;}
               break;}                
            case 10: { // 500mS after start of second (signal error if true)
                if (dcfSignalIn) {dcf_error = 1;}
               break;}
            case 15: { // 750mS after start of second (signal error if true)
                if (dcfSignalIn) {dcf_error = 1;}
               break;}                                                   
       } 
        if (dcf_error==1) {error_count++;dcf_error=0;}         
        if (error_count > 9 && sync==1) {error_count = 0;signal_error=1;}                                                                   
} // End of ISR

void dcf_check()
{ 
        parity_calc();  // Get Parity data and test if correct
        paritycheck= testu or testm or testdmy;        
        if (year>99) paritycheck=1;
        if (month>12) paritycheck=1;
        if (day>31) paritycheck=1;
        if (hour>23) paritycheck=1;
        if (minute>59) paritycheck=1;       
        if (paritycheck) {
        dcf_good = 0;       
        }
     else {
            dcf_good = 1;
        }           
}

void RTC_set()      // Set cpu RTC to required Time Zone
{    
            t.tm_sec = (second);        // 0-59
            t.tm_min = (minute);        // 0-59
            t.tm_hour = (hour);         // 0-23
            t.tm_mday = (dayofmonth);   // 1-31
            t.tm_mon = (month-1);       // 0-11 DCF "0" = Jan, -1 added for Mbed RCT clock format
            t.tm_year = ((year)+100);   // year since 1900,  current DCF year + 100 + 1900 = correct year           
            #if (TimeZone == 1)            
            set_time (mktime(&t)-3600); // set CPU RTC clock to DCF time zone -3600 for GMT        
            #elif (TimeZone==2)
            set_time (mktime(&t));      // set CPU RTC clock to DCF time zone
            #elif (TimeZone==3)
            set_time (mktime(&t)+3600); // set CPU RTC clock to DCF time zone +3600 for EET   
            #endif    
            RTCset=1;getDCF=0;Ticker50ms.detach();dcfSignalIn.rise(NULL);dcfLED=1;lcd.cls();            
            lcd.printf("RTC set, run\n"); 
            lcd.printf("Deep Sleep Mode");
            n=3600*24;
            wait(3);                                           
}

void parity_calc()
{
//calculate summer/winter time----------------------------------------------------------------------
    summertime = dcf_array[17] & 1;
//calculate hour--------------------------------------------------------------------------------------
    hourh = dcf_array[34] * 20 + dcf_array[33] * 10;
    hourl = dcf_array[32] * 8 + dcf_array[31] * 4 + dcf_array[30] * 2 + dcf_array[29] * 1;
    p_hour = hourh + hourl;
//calculate minutes------------------------------------------------------------------------------------
    minl = dcf_array[24] * 8 + dcf_array[23] * 4 + dcf_array[22] * 2 + dcf_array[21] * 1;
    minh = dcf_array[27] * 40 + dcf_array[26] * 20 +dcf_array[25] * 10;
    p_minute = minh + minl;
//calculate day of week--------------------------------------------------------------------------------
    p_dayofweek = dcf_array[44] * 4 +dcf_array[43] * 2 + dcf_array[42] * 1;
//calculate day----------------------------------------------------------------------------------------
    dayl = dcf_array[39] * 8 + dcf_array[38] * 4 + dcf_array[37] * 2 + dcf_array[36] * 1;
    dayh = dcf_array[41] * 20 + dcf_array[40] * 10;
    p_dayofmonth=dayh+dayl;
//calculate month--------------------------------------------------------------------------------------
    monthh = dcf_array[49] * 10;
    monthl = dcf_array[48] * 8 + dcf_array[47] * 4 + dcf_array[46] * 2 + dcf_array[45] * 1;
    p_month = monthh +monthl;
//calculate year---------------------------------------------------------------------------------------
    yearh = dcf_array[57] * 80 + dcf_array[56] * 40 + dcf_array[55] * 20 + dcf_array[54] * 10;
    yearl = dcf_array[53] * 8 +dcf_array[52] * 4 + dcf_array[51] * 2 + dcf_array[50] * 1;
    p_year = yearh+yearl;
//calculate parity
    paritym = dcf_array[21] + dcf_array[22] + dcf_array[23] + dcf_array[24] + dcf_array[25] + dcf_array[26] +dcf_array[27] +dcf_array [28];
    parityu =dcf_array[29] + dcf_array[30] + dcf_array[31] + dcf_array[32] + dcf_array[33] + dcf_array[34] + dcf_array[35];
    paritydmy =dcf_array[36] + dcf_array [37] + dcf_array [38] + dcf_array [39] + dcf_array[40] + dcf_array[41] + dcf_array [42] + dcf_array [43] + dcf_array[44] + dcf_array [45] + dcf_array[46] + dcf_array [47] + dcf_array[48] + dcf_array[49] + dcf_array[50] + dcf_array[51] + dcf_array [52] + dcf_array[53] + dcf_array[54] + dcf_array[55] + dcf_array[56] + dcf_array[57] + dcf_array[58];
//test parity------------------------------
    testu=parityu & 1;
    testm=paritym & 1;
    testdmy=paritydmy & 1;
  }