Back to www.micro-examples.com

Nixie Clock C Source Code

/*
 * DCF-77 SINGLE NIXIE CLOCK
 * Bruno Gavand
 * January, 2006
 * www.micro-examples.com
 *
 * This code is given 'as this', without any garantee
 * Use it at your own risks !
 *
 * PIC16F84A
 * 16 Mhz crystal, HS clock
 *
 * PORTA.0, in  : DCF pulse input
 * PORTA.1, out : LED pulse repeater
 * PORTA.2, out : LED pulse synch
 * PORTA.3, in  : SWITCH, pull-down
 *
 * PORTB.0->3, out      : BCD to 74141 decoder
 * PORTB.4->5, undef    : not connected
 * PORTB.6, in          : voltage control
 * PORTB.7, out         : 40 Khz pulse for nixie high voltage power supply
 *
 */

/*
 * constant definitions
 */
#define BLANK   0x0f            // BCD code for nixie blanking
#define MAXCOUNT        15625    // number of tmr0 overflows in 1 second
#define ADJUST  0             // number of extra cycles in one second

/*
 * following values are reduced
 * to give some flexibility to DCF-77 pulse reception
 */
#define timer_d_min 28000       // number of tmr0 overflows in 2 seconds
#define timer_h_0 1400          // number of tmr0 overflows in 0.1 second
#define timer_h_1 2800          // number of tmr0 overflows in 0.2 second

/*
 * macro definitions
 */
#define putNibble(b)            PORTB = b       // write a BCD value for 74141

/*
 * 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 ;        // number of last valid bit received
char            last_bit ;      // value of last valid bit received
unsigned char   parity ;        // count of positive bits
unsigned char   full ;          // set to 1 when sequence is complete
unsigned char   locked ;        // set to 1 when clock has been adjusted
unsigned char   mode ;          // 0:positive logic receiver, 1:negative logic receiver, 2:no receiver
unsigned char   mn ;            // minutes in sequence progress
unsigned char   hh ;            // hours in sequence progress
unsigned int    scaler ;        // count of tmr0 overflows for RTC
unsigned char   ohh, omn, oss ; // RTC clock : hours, minutes, seconds
unsigned char   dhh, dmn, dss ; // display : hours, minutes, seconds
unsigned char   pwm ;           // PWM output enable flag
unsigned char   vmax, vcurr ;   // max and current value for selector
unsigned char   curr ;          // selector choice

/*
 * wait n times 25 ms
 */
void    delay_25ms(unsigned char n)
        {
        while(n)
                {
                Delay_ms(25) ;
                n-- ;
                }
        }

/*
 * display a value from 00 to 99 on nixie, tenths first and then units
 */
void    mkDigit(unsigned char d)
        {
        putNibble(BLANK) ;
        delay_25ms(10) ;

        putNibble(d / 10) ;
        delay_25ms(16) ;
        
        putNibble(BLANK) ;
        delay_25ms(2) ;
        
        putNibble(d % 10) ;
        delay_25ms(12) ;
        }

/*
 * select a digit from 0 to vmax
 * make the digit vcurr flash to show the current selected value
 * curr contains the digit displayed when the key is pressed
 */
void    selectDigit(void)
        {
        unsigned char i, j, k ;

        pwm = 1 ;       // turn nixie on

        i = 0 ;
        for(;;)
                {
                for(j = 0 ; j <= vmax ; j++)
                        {
                        for(k = 0 ; k < 10 ; k++)
                                {
                                putNibble(j) ;
                                delay_25ms(1) ;
                                putNibble(vcurr == j ? BLANK : j) ;
                                delay_25ms(1) ;
                                if(PORTA.F3)
                                        {
                                        putNibble(BLANK) ;
                                        delay_25ms(8) ;
                                        putNibble(j) ;
                                        pwm = 0 ;
                                        curr = j ;
                                        return ;
                                        }
                                }
                        }

                if(vcurr < 10)
                        {
                        i++ ;
                        if(i == 3)
                                {
                                pwm = 0 ;
                                curr = vcurr ;
                                return ;
                                }
                        }
                }
        }

/*
 * interrupt routine called 2500000/256 times by seconds
 */
void    interrupt(void)
        {
        if(INTCON.T0IF)
                {
                PORTB.F7 = !PORTB.F7 & !PORTB.F6 & pwm ;
//                PORTB.F7 = !PORTB.F7 ;


                if(PORTA.F0 ^ mode)
                        {
                        tmrh++ ;
                        if(tmrd > timer_d_min)
                                {
                                bitnum = 0 ;
                                if(full)
                                        {
                                        ohh = hh ;
                                        omn = mn ;
                                        oss = 3 ;
                                        scaler = 0 ;
                                        locked = 1 ;
                                        }
                                mn = hh = 0 ;
                                parity = 0 ;
                                full = 0 ;
                                PORTA.F2 = 1 ;
                                }
                        tmrd = 0 ;
                        }
                else
                        {
                        tmrd++ ;
                        if(tmrh > 0)
                                {
                                if(tmrh > timer_h_1)
                                        {
                                        last_bit = 1 ;

                                        switch(bitnum)
                                                {
                                                case 21: mn++ ; break ;
                                                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 ;
                                                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 ;
                                                }

                                        if((bitnum != 28) && (bitnum != 35) && (bitnum != 58))
                                                {
                                                parity++ ;
                                                }
                                        bitnum++ ;
                                        }
                                else if(tmrh > timer_h_0)
                                        {
                                        if(bitnum == 20)
                                                {
                                                last_bit = -1 ;

                                                bitnum = 0 ;
                                                PORTA.F2 = 0 ;
                                                }
                                        else
                                                {
                                                last_bit = 0 ;

                                                bitnum++ ;
                                                }
                                        }
                                else
                                        {
                                        last_bit = -1 ;

                                        bitnum = 0 ;
                                        PORTA.F2 = 0 ;
                                        }

                                if(bitnum == 21)
                                        {
                                        parity = 0 ;
                                        }
                                if((bitnum == 29) || (bitnum == 36) || (bitnum == 59))
                                        {
                                        if((parity & 1) != last_bit)
                                                {
                                                bitnum = 0 ;
                                                PORTA.F2 = 0 ;
                                                }
                                        parity = 0 ;
                                        }

                                if(bitnum == 59)
                                        {
                                        full++ ;
                                        }
                                }
                        tmrh = 0 ;
                        }

                scaler++ ;
                if(scaler > MAXCOUNT)
                        {
//                        TMR0 += ADJUST ;
                        scaler = 0 ;
                        
                        oss++ ;
                        if(oss == 60)
                                {
                                oss = 0 ;
                                omn++ ;
                                if(omn == 60)
                                        {
                                        omn = 0 ;
                                        ohh++ ;
                                        if(ohh == 24)
                                                {
                                                ohh = 0 ;
                                                }
                                        }
                                }
                        }

                PORTA.F1 = PORTA.F0 ^ mode ;

                INTCON.T0IF = 0 ;
                }
        }

/*
 * program entry point
 */
main()
        {
        TRISA = 0b00001001 ;            // see header
        TRISB = 0b01000000 ;                  // see header
	
        INTCON = 0b10100000 ;           // T0IF and GIE enabled

        OPTION_REG = 0b11011000 ;       // no prescaler
        
        locked = 0 ;

        vcurr = EEPROM_Read(0) ;        // get last mode

        /*
         * select mode from 0 to 2
         * mode 0 : positive DFC pulse
         * mode 1 : negative DCF pulse
         * mode 3 : no DCF, manual adjustment
         */
        vmax = 2 ;
        selectDigit() ;
        mode = curr ;           // new mode

        /*
         * if mode 2 (no DCF), proceed to hour and minute settings
         */
        vcurr = 10 ;
        if(mode == 2)
                {
                /*
                 * tenth of hours
                 */
                vmax = 2 ;
                selectDigit() ;
                ohh = curr * 10 ;
                /*
                 * units of hours
                 */
                vmax = 9 ;
                selectDigit() ;
                ohh += curr ;

                /*
                 * tenth of minutes
                 */
                vmax = 5 ;
                selectDigit() ;
                omn = curr * 10 ;
                /*
                 * units of minutes
                 */
                vmax = 9 ;
                selectDigit() ;
                omn += curr ;

                /*
                 * seconds must be 3 in advance, due to display system
                 */
                oss = 3 ;

                /*
                 * clock is adjusted, lock it
                 */
                locked = 1 ;
                }

        EEPROM_Write(0, mode) ;         // store selected mode
        
        /*
         * main loop
         */
        for(;;)
                {
                /*
                 * if not locked, no display to prevent
                 * radio emission from high voltage supply
                 * that could perturb DCF receiver
                 */
                if(locked > 0)
                        {
                        if(PORTA.F3)
                                {
                                /*
                                 * enter edit sleep mode
                                 */
                                for(dhh = 0 ; dhh < 24 ; dhh++)
                                        {
                                        pwm = 1 ;       // turn nixie on
                                        mkDigit(dhh) ;  // display hours
                                        vcurr = EEPROM_Read(0x10 + dhh) ;       // get current sleep mode for hour
                                        vmax = 1 ;                              // range from 0 to 1
                                        selectDigit() ;                         // select new value
                                        EEPROM_Write(0x10 + dhh, curr) ;        // store choice in EEPROM
                                        }
                                }
                        else
                                {
                                /*
                                 * otherwise, start display cycle
                                 */
                                PORTB = BLANK ;
                                pwm = 1 ;                       // turn nixie on
                                delay_25ms(1) ;

                                /*
                                 * time values have to be stored in other variables
                                 * in order to prevent changes during display
                                 */
                                dss = oss ;
                                dmn = omn ;
                                dhh = ohh ;

                                mkDigit(dhh) ;
                                mkDigit(dmn) ;
                                mkDigit(dss) ;

                                pwm = 0 ;               // make nixie extinguish
                        
                                if(EEPROM_Read(0x10 + dhh))
                                        {
                                        /*
                                         * display time, just make a short pause
                                         */
                                        delay_25ms(60) ;
                                        }
                                else
                                        {
                                        /*
                                         * sleep time
                                         */
                                        if(mode == 2)
                                                {
                                                /*
                                                 * long pause when no dcf receiver
                                                 */
                                                delay_25ms(255) ;
                                                }
                                        else
                                                {
                                                /*
                                                 * dcf receiver is there,
                                                 * unlock to allow new sync
                                                 * meanwhile there will be no PWM activity
                                                 */
                                                locked = 0 ;
                                                }
                                        }
                                }
                        }
                }
        }


This is the EEPROM definition of the PIC, you can copy and paste if to the .EED file of your mC project :

0x00 01 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0x10 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 
0x20 01 01 01 01 01 01 01 01 FF FF FF FF FF FF FF FF 
0x30 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0x40 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0x50 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0x60 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0x70 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0x80 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0x90 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0xA0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0xB0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0xC0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0xD0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0xE0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 
0xF0 FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF