DCF77 clock using the HY-1.8 LCD display. Sets KL25Z RTC clock and will continue to run with no DCF signal. Import the libraries to work on FRDM-KL25Z board, sends 8 bit writes to the LCD.

Dependencies:   HY-1_8TFT_ST7735 TSI mbed

Fork of KL25Z_DCF77_HY-1_8LCD by Paul Staron

This project shows the KL25Z connected to a very cheap (3 Euros) Chinese TFT LCD Display (HY-1.8 SPI from Ebay). I have modified a ST7753 driver library to work the 8 bit SPI write that the Mbed library sets the FRDM board to (not sure why as the KL25Z MCU has16 bit cababiliy). The libraries are available for import from my home page. and includes five different fonts (three are needed for this example).

I have also used the Analog Out (pin PTE30) to control the LED back light level, simply connect a small NPN transistor in the LED -ve connection to ground and feed the analog out pin to the base of the transistor via a 10k ohm resistor. When you slide your finger across the TSI touch slider the display brightness can be changed from off to full level.

The code it much the same as the other projects I have published, just with different LCD drive code. This program will decode the DCF77 signal connected to pin PTE20 and set the RTC clock. The photo shows the display before I modified the LED backlight drive circuit.

The display runs at 5V and draws arround 20mA, the backlight led will also run at this voltage and is nice and bright. It can also be connected to the USB 3v pin and runs at a lower level. there is a solder jumper to bypass the 5v regulator, leave this open and it will run on both volages, if this jumper is closed it will bypass the regulator and if connected to the 5v supply, the display will be 'bricked' so take care!.

This display is considerably better and cheaper than the Nokia display but not as good as Oled. Easy to connect, 5 data connections and 2 power wires. It also includes a SD card slot that can be connected to the FRDM board.

The backlight is LED it does not use a switch mode DC-DC converter circuit so RF noise is very low, this is essential when using a sensitive VLF DCF77 radio receiver close by.

The FRDM board and display runs at 50mA depending on LED brightness.

/media/uploads/star297/_scaled_20130322_132442.jpg

main.cpp

Committer:
star297
Date:
2013-03-26
Revision:
2:3b26b9fda4f7
Parent:
1:6f4c5876140d

File content as of revision 2:3b26b9fda4f7:

// DCF77 Atomic Clock for HY-1.8 SPI (ST7735) LCD display

#include "mbed.h"
#include "ST7735_TFT.h"
#include "Arial24x23i.h"
#include "Arial11x11.h"
#include "Arial9x9.h"
#include "TSISensor.h"

AnalogOut DisplayLED(PTE30);
TSISensor tsi;

// FRDM-KL25Z
ST7735_TFT lcd(PTD2, NC, PTD1, PTA13, PTD5, PTD0 ,"TFT"); // sda, miso (not connected), sck, cs, AO, reset
DigitalIn dcfSignalIn(PTE20);//conection of output inverting MSF module
DigitalOut dcfLED(LED_GREEN); // indicates DCF signal on Mbed


// LPC1768 
//ST7735_TFT lcd(p5, p6, p7, p8, p11, p15); //,"TFT"); // mosi, miso, sclk, cs, rs, reset
//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 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 setRTC();
void s_display();
void drawgraph();
void showRTC();
void loop();
void oled_time_print();
void oled_date_print();
void drawgraph();
void bit_map_draw();
void set_time();
void restart();
void showRTCdate();

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,p_hour,p_dayofweek,p_dayofmonth,p_month,p_year;
int dcf_error;
int status;
int insecond_counter;
int delay;
int start_delay;
int RTCsecond;
int laststatus;
int lastbit,laststart;

float LedBright;

typedef unsigned char byte;

// 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][18] = {" Waiting Sync    "," Reading dcf data"," Checking Parity "," Last Minute Ok  "," Signal error    "};
char leapText[2][10] = {"    ", "Leap"};
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;dcf_sec=0;}
                 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){   
           if (start==1){
                dcf_array[dcf_sec]=dcf_status.sample150;
                  if (insecond_counter==1){
                        dcf_sec=0;x=0;y=0;dcf_array[58] = 0; bmd=0;        
                         if (r == 1){r=0;}
                            else {r=1;}
                   }
           
            if (dcf_sec > 1) {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()
{     
  DisplayLED=1; 
  lcd.background(Black);
  lcd.cls();
  lcd.set_orientation(0);
  lcd.set_font((unsigned char*) Arial11x11);
  lcd.rect(0,0,127,159,(White));
    lcd.locate (15,10);
    lcd.foreground (Red);
    lcd.printf("DCF77 Clock");
    lcd.locate (15,35);
    lcd.foreground (Green);
    lcd.printf("FRDM-KL25Z");
    lcd.locate (20,60);
    lcd.foreground (Blue);
    lcd.printf("HY-1.8 SPI");
    lcd.locate (15,90);
    lcd.foreground (Yellow);
    lcd.printf("128*160 TFT");
    lcd.locate (4,120);    
    lcd.foreground (Magenta);
    lcd.printf("Signal pin PTE20");
    lcd.set_font((unsigned char*) Arial9x9);
    wait (2);lcd.cls();r = 1;             
    bit_map_draw();dcf_sec=0;bmd=1;start=0;zero_bit=0;
    status = 0; laststatus=1; start_delay=1; laststart=1;
    d.start();showRTC();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 (minute>59) paritycheck=1;       
        if (paritycheck) {        
        status = 4;       
        }
     else {
            status = 3;
        }           
}
void loop()
{   
   if (interrupt_counter == 0) {
   showRTC();
   lcd.locate(60,127);
   lcd.foreground (White);
   lcd.printf("%02d",dcf_sec);
     
   if (status == 3 && dcf_sec == 1) {setRTC();RTCsecond=0;showRTC();} // set RTC clock if good data
   }
   
   if (interrupt_counter==3){
        if (dcf_status.sample150 != lastbit)  {lcd.locate(5,48);lcd.foreground (Cyan);lcd.printf("Bit %d  ",dcf_status.sample150);} 
           lastbit = dcf_status.sample150;       
        if (start==0 && laststart != start) {
           lcd.locate(60,48);lcd.foreground (Red);lcd.printf(" No Sync ");laststart=start;}
        if (start==1 && laststart != start) {
           lcd.locate(60,48);lcd.foreground (Green);lcd.printf(" In Sync ");laststart=start;}                
  }           
   if (interrupt_counter==3){   
   if (RTCsecond == 1) {showRTCdate();}
  }   
        if (interrupt_counter>5){
        LedBright = 0 + tsi.readPercentage();
        if (LedBright > 0 ){              
        if (LedBright > .01) {DisplayLED = LedBright; } 
        }
      }
   if (interrupt_counter == 10){
   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,147);
   if (status != laststatus) {lcd.printf(statusText[status]);}
   laststatus = status;
   lcd.set_font((unsigned char*) Arial9x9); 
        
   } 
 }

void showRTC()
{
    lcd.foreground (White);
    time_t seconds = time(NULL);
    char buffer[40];
   if (RTCsecond == 0) {
    strftime(buffer, 40, "%H:%M", localtime(&seconds));
    lcd.set_font((unsigned char*) Arial24x23i);
    lcd.locate(6,0);    
    lcd.printf(buffer);
   }
    lcd.set_font((unsigned char*) Arial11x11);
    strftime(buffer, 40,":%S", localtime(&seconds));
    lcd.locate(96,9);
    lcd.printf(buffer);
    strftime(buffer, 40,"%S", localtime(&seconds));
    RTCsecond = atoi(buffer);
    lcd.set_font((unsigned char*) Arial9x9); 
}

void showRTCdate()
{
    time_t seconds = time(NULL);    
    char buffer[40];
    strftime(buffer, 40, "%a %d %b %Y", localtime(&seconds));
    lcd.locate(1,24);    
    lcd.foreground (White);
    lcd.set_font((unsigned char*) Arial11x11);
    lcd.printf(buffer);
    lcd.locate(1,36);
    lcd.set_font((unsigned char*) Arial9x9);
    if (status==3) {
    if (summertime) {lcd.foreground (Orange);lcd.printf("Summer Time ");}
    else {lcd.foreground (LightBlue);lcd.printf("Winter Time ");}
    lcd.foreground (Olive);lcd.printf("%s",leapText[dcf_status.is_leap]);
    }
}

void setRTC()
{
            //Mbed rtc clock set
            struct tm t;
            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
            set_time(mktime(&t)-3600);
            time_t seconds = mktime(&t);                
}
void bit_print()
{
    if (r==1) {lcd.foreground (Magenta);}    // alternate bit draw colour
    else {lcd.foreground (White);}
    lcd.locate(x+20,(y+72));
    lcd.printf("%d",dcf_status.sample150);
    y=y+8;
    if ((dcf_sec)==27){(y)=64;}   
    if ((dcf_sec)==28){(y)=0;(x)=(x)+12; lcd.printf("          ");}     
    if ((dcf_sec)==34){(y)=64;}       
    if ((dcf_sec)==35){(y)=0;(x)=(x)+14;}
    if ((dcf_sec)==41){(y)=0;(x)=(x)+14;}
    if ((dcf_sec)==44){(y)=0;(x)=(x)+14;}
    if ((dcf_sec)==49){(y)=0;(x)=(x)+14;}              
}
void bit_map_draw()
{
    lcd.foreground (Yellow);
    (x)=18,(y)=59;
    lcd.locate(x,y);lcd.printf("m h d d m y");
    (x)=0,(y)=61;
    y=y+3; 
    lcd.locate(x,y+8);lcd.printf(" 1 i o a a o e");
    lcd.locate(x,y+16);lcd.printf(" 2 n u t y n a"); 
    lcd.locate(x,y+24);lcd.printf(" 4 u r e   t r");
    lcd.locate(x,y+32);lcd.printf(" 8 t       h  "); 
    lcd.locate(x,y+40);lcd.printf("10 e          ");
    lcd.locate(x,y+48);lcd.printf("20            ");
    lcd.locate(x,y+56);lcd.printf("40            ");
    lcd.locate(x,y+64);lcd.printf("80            "); 
    lcd.locate(x,y+72);lcd.printf(" P arity      "); 
    lcd.fillrect(15,60,16,144,(Red));
    lcd.fillrect(2,69,100,70,(Red));
}
 
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;
  }