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