Freescale KL25Z DCF77 Atomic clock using 4D Systems uOLED160 OLED display. Displays a graphical display of DCF time signal bit build and sets the local clock time. 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 from Hans program that works. This dsiplay is considerably better that the nokia LCD.

Dependencies:   mbed

/media/uploads/star297/20130308_220041.jpg

Added RTC clock to top of display that is synchronised to the DCF clock on the middle right of display. Both clocks will continue to run with no signal and will synchronise to DCF when signal returns. The RTC clock is set to the DCF time less 3600 seconds that gives UK (GMT) time. Any multiples of 3600 seconds (1 hour) can be added or subtracted to give the correct RTC time depending on european location (see code line 349). The DCF clock will reset on power down however if the back up cell is fitted the RTC clock should continue to keep time and will display correct time when powered up again, I have not checked this yet. The DCF clock will reset and will synchronise within 2 minutes when power is supplied.

/media/uploads/star297/20130308_220102.jpg

The DCF signal is sampled every 50mS each second that is called by the Interrupt Sub Routine (myISR). All code must be able to complete within 50mS otherwise it may cause the processor to 'hang' particularly durring Oled print functions that tend to be slow (even at 256k baud). So the trck here is to run parts of the code at different 50mS segments of the interrupt counter.

The program starts by looking for the 59th second pulse that is not transmitted, sample50 and sample150 will be zero at this point. When this arrives the interrupt counter is synchonised and the sample150 pulses are counted as '1's and '0's. The time and DCF bit map start on the 21st pulse with the last pulse counted at 58 seconds when partity is checked. If the parity is correct and the time variables are within limits as set in the partity_check subroutine, the DCF clock is set and then the RTC the process then repeats continuously alternating the bit map colour every DCF frame. If there is an error in the signal caused by a high level of noise or missing 'bits' the counters will restart looking for the 59th pulse.

This method of sampling has proved to be far more stable against noise in the DCF signal and will continue to run where as pulse width measuring method I have tried from other program examples requires an almost noise free signal.

I'm affraid the code is pretty poor and sure it could be much improved but it does work rather well.

I have made further checks on this board and have found that unlike the Mbed LPC1768 the coin cell is not for RTC back up. This battery is to run the CPU, RGB led and accelerometer and consumes around 15mA.

The other issue becomes apparent when the board is run from the coin cell and without power from the USB connector. In this case the SDA CPU is in 'sleep' mode that means the RTC clock signal is not generated and fed to the main CPU that results in the RTC freezing.

In conclusion the KL25Z would not be viable for general low power RTC clock functions that require the RTC to keep time when shut down.

main.cpp

Committer:
star297
Date:
2013-03-08
Revision:
1:4f254cca2bac
Parent:
0:e3f7b0fd072e

File content as of revision 1:4f254cca2bac:

#include "mbed.h"
#include "OLED160G1.h"

OLED160G1 oled(PTD3,PTD2,PTD1);// Oled Display tx, rx, rs
DigitalIn dcfSignalIn(PTE20);//conection to NON inverting output of dcf module

DigitalOut dcfLED (LED1);// dcf signal Pulse

//variable
char paritycheck,paritym,parityu,paritydmy;
char testu,testm,testdmy;
char bit;
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 dcf_check();
void showRtcDate();
void oled_time_print();
void bit_map_draw();
void bit_print();
void parity_calc();
void drawgraph();
void showRTC();
void showRtcDate();
void setRTC();

Timer d;
Ticker Ticker10ms;

#define ZERO 1e-10
#define isBetween(A, B, C) (((A-B) > -ZERO) && ((A-C) < ZERO))

struct status {
   // bool status;       // Last minute received OK counts parity test results
    bool is_leap;           // Leap year flag (NOT actually transmitted but calculated)
    bool sample50;          // dcf sample at 70mS into the start of the second
    bool sample150;         // dcf sample at 170mS 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 showRtcDate();
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] = {"         ", "Leap Year"};
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();} // check parity
        if (status == 3 && dcf_sec == 1) {set_time();} // set DCF clock if good data
                 
    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 (status == 3 && dcf_sec == 1) {setRTC();} // set RTC clock if good data
                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)
           {
             oled.locate(18,4);oled.setFontColor(oled.toRGB(0,0,255));
             oled.printf("Bit %d",dcf_status.sample150); 
             if (start==0) {oled.drawText(18,13,(OLED_FONT5X7),"No Sync ",oled.toRGB(255,0,0));}
             
           if (start==1)
            {
                dcf_array[dcf_sec]=dcf_status.sample150;
                oled.drawText(18,13,(OLED_FONT5X7),"In Sync ",oled.toRGB(0,255,0));
                    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;} // synchronise interrupt counter to DCF sync pulse               
        }         
} // End of ISR

int main()
{     
    oled.init();
    oled.eraseScreen();
    oled.setTextBackgroundType(OLED_SET_TEXT_OPAQUE);
    oled.drawText(1,1,(OLED_FONT8X12),"DCF77 Atomic Clock",oled.toRGB(255,0,0));
    oled.drawText(1,3,(OLED_FONT8X12)," Freescale KL25Z",oled.toRGB(0,255,0));
    oled.drawText(1,5,(OLED_FONT8X12),"   4D Systems",oled.toRGB(0,0,255));
    oled.drawText(1,7,(OLED_FONT8X12)," Oled160 Display",oled.toRGB(255,255,0));    
    wait(1);
    oled.eraseScreen();bit_map_draw();
    oled.drawText(17,6,(OLED_FONT5X7),"DCF Time",oled.toRGB(255,255,0));
    dcf_sec=0;bmd=1;start=0;zero_bit=0;r = 1;
    status = 0; start_delay=1;
    d.start();showRtcDate();  
    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; // bad parity      
        }
     else {
            status = 3; // good parity
        }       
}

void bit_print()
{
    if (r==1) {oled.setFontColor(oled.toRGB(255,0,255));}    // alternate bit draw colour
    else {oled.setFontColor(oled.toRGB(255,255,255));}
    oled.locate(x+3,(y+1));
    oled.printf("%d",dcf_status.sample150);
    (y)++;
    if ((dcf_sec)==27){(y)=13;}   
    if ((dcf_sec)==28){(y)=5;(x)=(x)+2;oled.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()
{
    (x)=0,(y)=4;
      oled.drawText(x,y,(OLED_FONT5X7),"   M H D d M Y",oled.toRGB(255,255,0));
    y++;   
    oled.drawText(x,y+1,(OLED_FONT5X7)," 1 i o a a o e",oled.toRGB(255,255,0));    
    oled.drawText(x,y+2,(OLED_FONT5X7)," 2 n u t y n a",oled.toRGB(255,255,0));    
    oled.drawText(x,y+3,(OLED_FONT5X7)," 4 u r e   t r",oled.toRGB(255,255,0));    
    oled.drawText(x,y+4,(OLED_FONT5X7)," 8 t       h  ",oled.toRGB(255,255,0));    
    oled.drawText(x,y+5,(OLED_FONT5X7),"10 e          ",oled.toRGB(255,255,0));    
    oled.drawText(x,y+6,(OLED_FONT5X7),"20            ",oled.toRGB(255,255,0));    
    oled.drawText(x,y+7,(OLED_FONT5X7),"40            ",oled.toRGB(255,255,0));   
    oled.drawText(x,y+8,(OLED_FONT5X7),"80            ",oled.toRGB(255,255,0));     
    oled.drawText(x,y+9,(OLED_FONT5X7)," P arity       ",oled.toRGB(255,255,0));   
    oled.drawLine(2,43,85,43, oled.toRGB(255, 0, 0));
    oled.drawLine(14,33,14,118, oled.toRGB(255, 0, 0)); 
}
void showClock()
{
   oled.setFontSize(OLED_FONT5X7);
   oled.locate(17,8);oled.setFontColor(oled.toRGB(255,255,255));
   oled.printf("%02d:%02d:%02d", hour, minute, second);
   oled.locate(17,9);
   oled.printf("%02d/%02d/%02d",dayofmonth,month,year);
   if (summertime) {oled.drawText(17,10,(OLED_FONT5X7),"Summer",oled.toRGB(255,100,100));}               
   else {oled.drawText(17,10,(OLED_FONT5X7),"Winter",oled.toRGB(0,255,255));} 
   oled.drawText(17,11,(OLED_FONT5X7),leapText[dcf_status.is_leap],oled.toRGB(0,255,160)); 
   if (status==0) {oled.drawText(5,15,(OLED_FONT5X7),statusText[status],oled.toRGB(255,255,255));}
   if (status==1) {oled.drawText(5,15,(OLED_FONT5X7),statusText[status],oled.toRGB(0,255,255));}
   if (status==2) {oled.drawText(5,15,(OLED_FONT5X7),statusText[status],oled.toRGB(0,0,255));}
   if (status==3) {oled.drawText(5,15,(OLED_FONT5X7),statusText[status],oled.toRGB(0,255,0));}
   if (status==4) {oled.drawText(5,15,(OLED_FONT5X7),statusText[status],oled.toRGB(255,0,0));}
} 
void loop()
{   
    if (interrupt_counter == 0) {  // refresh local time at the first 50mS of every second
    showClock(); 
    showRTC(); 
    oled.setFontSize(OLED_FONT5X7); 
    oled.locate(9,12);oled.setFontColor(oled.toRGB(255,255,255));oled.printf("%02d",dcf_sec);
    oled.setFontSize(OLED_FONT5X7);
 } 
}
  
void showRTC()
{
    time_t seconds = time(NULL);
    char buffer[40];
    strftime(buffer, 40, "%I:%M:%S %p", localtime(&seconds));
    oled.drawText(1,0,(OLED_FONT8X12),buffer,oled.toRGB(255,255,255));
}

void showRtcDate()
{
    time_t seconds = time(NULL);    
    char buffer[40];
    strftime(buffer, 40, "%a %d %b %Y", localtime(&seconds));
    oled.drawText(1,1,(OLED_FONT8X12),buffer,oled.toRGB(255,255,255)); 
}

void setRTC()
{
            //Mbed rtc clock set
            struct tm t;
            t.tm_sec = (second-1);        // 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
            set_time(mktime(&t)-3600);
            time_t seconds = mktime(&t);               
            showRtcDate(); // Print DATE once per minute
            if (summertime==0) {oled.drawText(19,0,(OLED_FONT5X7),"Winter",oled.toRGB(0,255,255));}               
            else {oled.drawText(19,0,(OLED_FONT5X7),"Summer",oled.toRGB(255,128,128));}
   
}
  
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;
  }