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