Back to www.micro-examples.com

DCF77 LED Clock C Source Code

If this page is translated by Google, the source code below will not be useable : leave the translation to get it correctly.


/*
 * 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
                }
        }