DCF-77 PIC LED Clock

An atomic clock with automatic time synchronization

DCF77 clock connected

A PIC16F84A-based LED clock with DCF-77 atomic time synchronization.

Overview

This project uses a PIC16F84A with a 4x7 segment LED display. It supports hours/minutes or minutes/seconds display modes. The DCF77 atomic clock provides automatic time set-up. The firmware uses only 861 bytes of ROM and 62 bytes of RAM.

Clock display

What is DCF77?

DCF77 is a German radio station broadcasting on 77.5 KHz, transmitting atomic clock time from Frankfurt. The signal encodes time in a 59-bit frame: a 100ms pulse represents a 0 bit, a 200ms pulse represents a 1 bit. One pulse is sent per second, with a 2-second gap marking the minute synchronization point. The signal range is approximately 2000 km.

Building a DCF77 Receiver

Ready-made DCF77 receiver modules are available for 12-18 euros. They have 3 pins: Gnd, Pulse, and Vcc. The firmware handles both positive and negative logic modules. The ferrite antenna must be oriented perpendicular to the direction of Frankfurt for best reception.

Circuit Schematic

Circuit schematic

How to Use

When not locked to the DCF77 signal, the display shows the current frame bit number and pulse signal indicator. Once locked, the display shows hours:minutes. Pressing RB7 toggles to minutes:seconds display mode.

C Source Code

dcf77clock.c
/*
 * DCF-77 LED CLOCK
 *
 * PIC16F84A
 * 10 Mhz crystal, HS clock
 *
 * PORTA.0->3, out : 7 segment cathod control
 * PORTA.4, in ; DCF pulse input
 *
 * PORTB.0->7, out : 7 segment output
 * PORTB.7, in : button input
 *
 * Author : Bruno Gavand, november 2005
 * see more details on www.micro-examples.com
 *
 */

/*
 * constant definitions
 */
#define MAXCOUNT        9766    // number of TMR0 overflows in 1 second

#define ADJUST          96      // extra ticks in 1 second


/*
 * this values are reduced in practice
 * to give some flexibility to DCF-77 pulse reception
 */
#define timer_d_min 14000       // number of TMR0 overflows in 2 seconds : 19531

#define timer_h_0 640           // number of TMR0 overflows in 0.1 second : 976

#define timer_h_1 1400          // number of TMR0 overflows in 0.2 second : 1953


/*
 * RAM variables
 */
unsigned int    tmrh ;          // count of TMR0 overflows since pulse is high

unsigned int    tmrd ;          // count of TMR0 overflows since pulse is down

unsigned char   bitnum = 0 ;    // number of last valid bit received

char            last_bit ;      // value of last valid bit received

unsigned char   parity ;        // count of positive bits received

unsigned char   full = 0 ;      // set to 1 when DCF frame is complete

unsigned char   locked = 0 ;    // set to 1 when clock has been adjusted

unsigned char   mode = 1 ;      // 0:positive logic receiver, 1:negative logic receiver

unsigned char   mn ;            // next minutes in DCF frame

unsigned char   hh ;            // next hours in DCF frame

unsigned int    scaler ;        // count of TMR0 overflows for RTC

unsigned char   rhh = 12, rmn = 34, rss = 56 ; // RTC clock : hours, minutes, seconds

unsigned char   digiled[4] ;    // 7 segment for each 4 displays

/*
 * 7 segment encoding for numbers from 0 to 9
 */
unsigned char   septSeg[10] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67 } ;
unsigned char   digit ;         // current digit in multiplexing sequence

unsigned char   kp ;            // keypad : button to press for displaying mn:ss instead of hh:mn

unsigned char   dp = 0 ;        // decimal points to be reported on display

unsigned char   i ;             // general purpose register


/*
 * interrupt routine called 2500000/256 times by seconds :
 * the register TMR0 is increased each 4 clock cycles (quartz frequency is 10 Mhz),
 * and overflows when reseting from 255 to 0,
 * fetching to the interrupt precedure with bit T0IF set
 */
void    interrupt(void)
        {
//        if(INTCON.T0IF)                                         // TMR0 overflow ?

                {
                /*
                 * test DCF pulse, inverts the level if negative logic flag 'mode' is set
                 */
                if(PORTA.F4 ^ mode)                             // DCF pulse is high ?

                        {
                        tmrh++ ;                                // yes, increment my high timer

                        if(tmrd > timer_d_min)                  // down timer is greater than pulse pause ?

                                {
                                bitnum = 0 ;                    // yes, reset bit number

                                if(full)                        // is the DCF frame complete ?

                                        {
                                        rhh = hh ;              // yes, set hours, min and seconds of RTC

                                        rmn = mn ;
                                        rss = 0 ;
                                        scaler = 0 ;            // reset my scaler

                                        locked = 1 ;            // locked flag is set, to enable display

                                        }
                                mn = hh = 0 ;                   // reset min and hours for next frame

                                parity = 0 ;                    // reset parity counter

                                full = 0 ;                      // reset full flag

                                dp.F3 = 1 ;                     // set frame flag

                                }
                        tmrd = 0 ;                              // reset down timer

                        }
                else
                        {
                        tmrd++ ;                                // pulse is low

                        if(tmrh > 0)                            // test if a high pulse was present

                                {
                                if(tmrh > timer_h_1)            // yes, was longer than delay for a 1 ?

                                        {
                                        last_bit = 1 ;          // yes, last bit was a 1


                                        switch(bitnum)          // depending on number of frame bit :

                                                {
                                                case 21: mn++ ; break ;         // add minutes weights

                                                case 22: mn += 2 ; break ;
                                                case 23: mn += 4 ; break ;
                                                case 24: mn += 8 ; break ;
                                                case 25: mn += 10 ; break ;
                                                case 26: mn += 20 ; break ;
                                                case 27: mn += 40 ; break ;

                                                case 29: hh++ ; break ;         // add hours weights

                                                case 30: hh += 2 ; break ;
                                                case 31: hh += 4 ; break ;
                                                case 32: hh += 8 ; break ;
                                                case 33: hh += 10 ; break ;
                                                case 34: hh += 20 ; break ;
                                                }

                                        /*
                                         * is it a data bit or a parity bit ?
                                         */
                                        if((bitnum != 28) && (bitnum != 35) && (bitnum != 58))
                                                {
                                                parity++ ;      // data bit, increment parity

                                                }
                                        bitnum++ ;              // next frame bit

                                        }
                                else if(tmrh > timer_h_0)       // was last pulse a 0 ?

                                        {
                                        if(bitnum == 20)        // yes, start bit ?

                                                {
                                                last_bit = -1 ; // bad karma, should be 1 !


                                                bitnum = 0 ;    // reset frame bit counter

                                                dp.F3 = 0 ;     // clear frame flag indicator

                                                }
                                        else
                                                {
                                                last_bit = 0 ;  // not a start bit, is ok


                                                bitnum++ ;      // next frame bit

                                                }
                                        }
                                else
                                        {
                                        last_bit = -1 ;         // ouch, last bit was garbage


                                        bitnum = 0 ;            // reset frame bit counter

                                        dp.F3 = 0 ;             // clear frame flag indicator

                                        }

                                if(bitnum == 21)                // bit 20 is passed ?

                                        {
                                        parity = 0 ;            // yes, clear parity counter

                                        }
                                        
                                /*
                                 * last bit was a parity bit ?
                                 */
                                if((bitnum == 29) || (bitnum == 36) || (bitnum == 59))
                                        {
                                        if((parity & 1) != last_bit)    // yes, but parity is incorrect

                                                {
                                                bitnum = 0 ;            // reset frame bit counter

                                                dp.F3 = 0 ;             // clear frame flag indicator

                                                }
                                        parity = 0 ;            // clear parity counter

                                        }

                                if(bitnum == 59)                // is the frame complete ?

                                        {
                                        full++ ;                // yes, should have a pulse pause

                                        }
                                }
                        tmrh = 0 ;                              // clear high pulse counter

                        }

                /*
                 * real time clock
                 */
                scaler++ ;                                      // increment the scaler

                if(scaler == MAXCOUNT)                          // a second is passed ?

                        {
                        TMR0 += ADJUST ;
                        
                        scaler = 0 ;                            // yes, clear scaler


                        rss++ ;                                 // next second

                        if(rss == 60)                           // seconds overflow ?

                                {
                                rss = 0 ;                       // yes, clear second

                                rmn++ ;                         // next minute

                                if(rmn == 60)                   // minutes overflow ?

                                        {
                                        rmn = 0 ;               // yes, clear minute

                                        rhh++ ;                 // next hour

                                        if(rhh == 24)           // hours overflow ?

                                                {
                                                rhh = 0 ;       // yes, clear hour

                                                }
                                        }
                                }
                        }

                dp.F1 = PORTA.F4 ^ mode ;               // copy the pulse level to the decimal point for display


                INTCON.T0IF = 0 ;                       // clear interrupt flag to enable next call on overflow

                }
        }

/*
 * program entry point
 */
main()
        {
        TRISA = 0b00010000 ;            // see header

        TRISB = 0x00 ;                  // see header


        INTCON = 0b10100000 ;           // T0IF and GIE enabled


        OPTION_REG = 0b11011000 ;       // no prescaler


        /*
         * main loop
         */
        for(;;)
                {
                if(locked > 0)          // is the RTC up to date ?

                        {
                        if(kp)          // yes, is the key pressed ?

                                {
                                /*
                                 * yes, prepare display for MN:SS
                                 */
                                digiled[0] = septSeg[rmn / 10] ;        // minutes tenth

                                digiled[1] = septSeg[rmn % 10] ;        // minutes unit

                                digiled[2] = septSeg[rss / 10] ;        // seconds tenth

                                digiled[3] = septSeg[rss % 10] ;        // seconds unit

                                }
                        else
                                {
                                /*
                                 * no key, prepare display for HH:MN
                                 */
                                digiled[0] = (rhh < 10) ? 0 : septSeg[rhh / 10] ;       // hours tenth, blank if null

                                digiled[1] = septSeg[rhh % 10] ;        // hours unit

                                digiled[2] = septSeg[rmn / 10] ;        // minutes tenth

                                digiled[3] = septSeg[rmn % 10] ;        // minute unit

                                }
                        }
                else
                        {
                        /*
                         * the RTC is not up to date, display DCF frame info
                         */
                        digiled[0] = 0 ;                // nothing on first 2 digits

                        digiled[1] = 0 ;
                        digiled[2] = septSeg[bitnum / 10] ;     // number of bit tenth

                        digiled[3] = septSeg[bitnum % 10] ;     // number of bit unit

                        }
                                
                /*
                 * set each decimal points,
                 * F1 is pulse repeater,
                 * F3 is frame progress
                 */
                digiled[0].F7 = dp.F0 ;
                digiled[1].F7 = dp.F1 ;
                digiled[2].F7 = dp.F2 ;
                digiled[3].F7 = dp.F3 ;

                PORTA = 0 ;                     // shut down display

                PORTB = 0 ;                     // clear segment outputs

                        
                TRISB = 0x80 ;                  // set PORTB.F7 as input

                kp = PORTB.F7 ;                 // read key

                TRISB = 0x00 ;                  // set PORTB.F7 as output again


                digit++ ;                       // next digit to be displayed

                if(digit > 3)                   // all done ?

                        {
                        digit = 0 ;             // yes, start again with first

                        i = 0x01 ;              // assign bit mask for PORTA first display led cathod control

                        }
                else
                        {
                        i = 0x01 << digit ;     // shift bit for next led display cathod activztion

                        }

                PORTB = digiled[digit] ;        // write 7 segment output

                PORTA = i ;                     // light on selected 7 segment display

                }
        }

Download

DCF-77 LED Clock project files

Download the source code and pre-compiled HEX file: