DCF77 Atomic clock using Nokia 6610 colour LCD display. This will continue to run with no signal and shows a graphic bit map display demonstrating the time build. Does not use the Mbed RTC so will run on the LPC11U24. The main signal timing is achieved by using a Ticker ISR that looks at the DCF signal input every 50mS this also produces the seconds on the local clock incase of signal errors or no signal. Many thanks to Lynton Towler for the idea of this part of the code and Wim who helped me convert it from an Arduino program. The Parity code was fromHans program that works.

Dependencies:   mbed

/media/uploads/star297/_scaled_20130306_221042.jpg

Re published due to error in title.

I have also updated the 'Leap' year print code as that was incorrect.

This program works on the both Mbed's (LPC1768 and LPC11U24)

main.cpp

Committer:
star297
Date:
2013-03-08
Revision:
0:89274444b840
Child:
2:31f5b5d56af6

File content as of revision 0:89274444b840:

#include "mbed.h"
#include "MobileLCD.h"

MobileLCD lcd(p5, p6, p7, p8, p9);
DigitalIn dcfSignalIn(p21);//conection to NON inverting output of dcf module
DigitalOut dcfLED (LED1);//received DCF signal

//variable
char paritycheck,paritym,parityu,paritydmy;
char testu,testm,testdmy;
char min,minh,minl;
char hourh,hourl;
char w_day;
char day,dayh,dayl;
char monthh,monthl;
char yearh,yearl;
char summertime;
int dcf_array[61];
  
//makro,s
void getbit();
void dcf_check();
void lcd_date_print();
void lcd_time_print();
void bit_map_draw();
void bit_print();
void parity_calc();
void reset_rtc();
void local_time();
void set_rtc();
void s_display();
void drawgraph();

Timer d;
Ticker Ticker10ms;

#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;

// Global variables
int in_sec_counter = 0;
int interrupt_counter = 0;
int hour = 12;
int minute = 0;
int second = 0;
int dayofweek = 6;
int dayofmonth = 1;
int month = 1;
int year = 0;
int nosignal;
int start;
int zero_bit;
int dcf_sec;
int s,x,y,r;
int bmd;
int sync;
int p_minute;
int p_hour;
int p_dayofweek;
int p_dayofmonth;
int p_month;
int p_year;
int dcf_error;
int status;
int insecond_counter;
int delay;
int start_delay;
typedef unsigned char byte;
void loop();
void oled_time_print();
void oled_date_print();
void drawgraph();
void bit_map_draw();
void set_time();
void restart();

// 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 statusText[5][17] = {"Waiting Sync    ","Reading dcf data","Checking Parity ","Last Minute Okay","Signal error    "};
char leapText[2][10] = {" ", "L"};
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

// Return the maximum day in month for a given month & year

byte maxDay(byte year, byte month)
{
    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;
}

void myISR()  //This is the interrupt service routine (ISR) that is called every 50ms 
{   
    interrupt_counter++;
     
    dcfLED = dcfSignalIn;  // Show dcfSignal state on LED
     
    if (interrupt_counter == 20) { // 50mS x 20 = 1000mS = 1 Second
        interrupt_counter = 0;
        second++;insecond_counter++;
        if (d.read()> 2) {d.reset();d.start();delay=1;}
        else {delay=0;}
        if (second > 2) {start_delay=0;}
        if (dcf_error==1) {status = 4;} 
       }
        if (dcf_sec==58) {status = 2;}
        if (dcf_sec==59) {dcf_check();}
        if (status == 3 && dcf_sec == 1) {set_time();}
           
    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 && start_delay==0) {zero_bit=1;sync=0;insecond_counter=0;start=1;}
                 else {zero_bit=0;}
               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 12: { // 600mS after start of second (signal error if true)
                if (dcfSignalIn) {dcf_error = 1;}
               break;}                                                   
          }           
        if (interrupt_counter==1)
           {
            if (dcfSignalIn){nosignal=1;}
            else nosignal++;              
            if (nosignal>5){nosignal=2; status = 0; restart();} 
            if (dcf_error == 1 && delay == 1) {dcf_error = 0; status = 0;restart();}                      
           }           
        if (interrupt_counter==3)
           {
             lcd.locate(0,4); lcd.foreground (CYAN);
             lcd.printf("Bit %d   ",dcf_status.sample150);           
             if (start==0) {lcd.foreground (RED);lcd.printf("No Sync ");}             
           if (start==1)
            {
                dcf_array[dcf_sec]=dcf_status.sample150;
                lcd.foreground (GREEN);
                lcd.printf("In Sync ");  
                    if (insecond_counter==1)
                    {
                        dcf_sec=0;x=0;y=5;dcf_array[58] = 0; bmd=0;        
                         if (r == 1){r=0;}
                            else {r=1;}
                        }        
            if (dcf_sec > 2) {status = 1;}                 
            if (dcf_sec > 20 && dcf_sec < 59 ) {bit_print();}
            if (dcf_sec > 60) {dcf_error = 1;bit_map_draw();status = 4;dcf_sec=0;dcf_error = 0;}  
            if (sync==1) {dcf_sec++;}                     
                }     
             }             
      if (sync==0) {
            if (dcfSignalIn) {interrupt_counter =0 ;sync=1;}                
        }                
} // End of ISR

int main()
{     
    lcd.locate (1,2);
    lcd.foreground (WHITE);
    lcd.printf("DCF77 Clock");
    lcd.locate (1,4);
    lcd.printf("Nokia 6610 LCD");
    lcd.locate (1,6);
    lcd.printf("Signal pin 21");
    wait (5);lcd.cls();r = 1;             
    bit_map_draw();dcf_sec=0;bmd=1;start=0;zero_bit=0;
    status = 0; start_delay=1;
    d.start();  
    if (dcfSignalIn) {interrupt_counter =0 ;sync=1;}                    
    Ticker10ms.attach(& myISR, .05 ); //Setup Ticker to call myISR every 50 ms
            
  while (true) {
    loop();                           // Continuously get dcf time and Display data interrupted by the Ticker every 50ms
   }    
}

void dcf_check()
{ 
        parity_calc();
        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 (min>59) paritycheck=1;       
        if (paritycheck) {        
        status = 4;       
        }
     else {
            status = 3;
        }       
}
void showClock()
{
   lcd.locate(0,0);lcd.foreground (WHITE);
   lcd.printf("%02d:%02d:%02d", hour, minute, second);
   lcd.locate(0,1);
   lcd.printf("%s %02d %s'20%02d", weekDayName[dayofweek], dayofmonth, monthName[month-1], year);
   lcd.locate(0,2);
   if (summertime) {lcd.foreground (RED);lcd.printf("Summer Time");}
   else {lcd.foreground (BLUE);lcd.printf("Winter Time");}
   if (dcf_status.is_leap){lcd.foreground (RED);lcd.printf(" %d"),leapText[dcf_status.is_leap];}
   if (status==0) {lcd.foreground (WHITE);}
   if (status==1) {lcd.foreground (CYAN);}
   if (status==2) {lcd.foreground (BLUE);}
   if (status==3) {lcd.foreground (GREEN);}
   if (status==4) {lcd.foreground (RED);}
   lcd.locate(0,15);
   lcd.printf(statusText[status]);
}

void bit_print()
{
    if (r==1) {lcd.foreground (MAGENTA);}    // alternate bit draw colour
    else {lcd.foreground (WHITE);}
    lcd.locate(x+3,(y+1));
    lcd.printf("%d",dcf_status.sample150);
    (y)++;
    if ((dcf_sec)==27){(y)=13;}   
    if ((dcf_sec)==28){(y)=5;(x)=(x)+2;lcd.printf("          ");}     
    if ((dcf_sec)==34){(y)=13;}       
    if ((dcf_sec)==35){(y)=5;(x)=(x)+2;}
    if ((dcf_sec)==41){(y)=5;(x)=(x)+2;}
    if ((dcf_sec)==44){(y)=5;(x)=(x)+2;}
    if ((dcf_sec)==49){(y)=5;(x)=(x)+2;}              
}

void bit_map_draw()
{
    lcd.foreground (YELLOW);(x)=0,(y)=5;
    lcd.locate(x,y);lcd.printf("   M H D d M Y"); 
    lcd.locate(x,y+1);lcd.printf(" 1 i o a a o e");
    lcd.locate(x,y+2);lcd.printf(" 2 n u t y n a"); 
    lcd.locate(x,y+3);lcd.printf(" 4 u r e   t r");
    lcd.locate(x,y+4);lcd.printf(" 8 t       h  "); 
    lcd.locate(x,y+5);lcd.printf("10 e          ");
    lcd.locate(x,y+6);lcd.printf("20            ");
    lcd.locate(x,y+7);lcd.printf("40            ");
    lcd.locate(x,y+8);lcd.printf("80            "); 
    lcd.locate(x,y+9);lcd.printf(" P arity      "); 
}

void loop()
{   
    if (interrupt_counter == 0) {
    showClock();   
    lcd.locate(10,12);lcd.foreground (WHITE);lcd.printf("%02d",dcf_sec);
    } 
  }
  
void set_time()
{
            second = dcf_sec;
            minute=p_minute;
            hour=p_hour;
            dayofweek=p_dayofweek;
            dayofmonth=p_dayofmonth;
            month=p_month;
            year=p_year;
}

void restart()
{
start=0;s=0;x=0;y=5;dcf_sec=0;dcf_error=0;
insecond_counter=0;zero_bit=0;start_delay=1;
}

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