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

Files at this revision

API Documentation at this revision

Comitter:
star297
Date:
Wed Feb 26 21:39:57 2014 +0000
Commit message:
Initial version 1.0

Changed in this revision

TextLCD.lib Show annotated file Show diff for this revision Revisions of this file
WakeUp.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r f40db885ccfb TextLCD.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TextLCD.lib	Wed Feb 26 21:39:57 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/simon/code/TextLCD/#44f34c09bd37
diff -r 000000000000 -r f40db885ccfb WakeUp.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WakeUp.lib	Wed Feb 26 21:39:57 2014 +0000
@@ -0,0 +1,1 @@
+https://mbed.org/users/Sissors/code/WakeUp/#89dae784c38f
diff -r 000000000000 -r f40db885ccfb main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Feb 26 21:39:57 2014 +0000
@@ -0,0 +1,350 @@
+#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;
+  }
+              
diff -r 000000000000 -r f40db885ccfb mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Feb 26 21:39:57 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/8e73be2a2ac1
\ No newline at end of file