Single-Tube Nixie Clock
A PIC16F84A DCF-77 nixie tube clock
A PIC16F84A single-digit nixie clock with DCF-77 atomic clock support and a high voltage power supply built with only 4 components.
Overview
This project features a PIC16F84A driving a single-digit nixie tube as a clock. It supports DCF-77 atomic clock synchronization for automatic time setting. The high voltage power supply uses only 4 components. 24-hour programmable extinction saves tube life. No MikroC licence is needed as the firmware fits within the demo limit.
Powering the Nixie
Nixie tubes require approximately 170V at about 1.5mA. A DC/DC converter generates this from the +5V supply using software PWM driving a MOSFET. The components are: an IRF830 MOSFET, a 330uH coil, a fast recovery diode, and a 2.2uF 250V capacitor.
WARNING: High Voltage
The DC/DC converter produces dangerous voltage (170V). Exercise extreme caution when building and testing this circuit.
Driving the Nixie
The nixie tube is driven through a 74141 BCD-to-decimal decoder, which contains high-voltage open-collector transistors suitable for nixie driving. The IN-14 Russian nixie tube fits on a standard 14-pin DIP socket.
Circuit Schematic
R3 and D2 protect the PIC input from the high voltage feedback.
Operation
Mode selection at power-up: 0 = positive logic DCF, 1 = negative logic DCF, 2 = manual time setting. The single digit sequentially displays hours, minutes, and seconds with pauses between each pair of digits.
C Source Code
/*
* DCF-77 SINGLE NIXIE CLOCK
* January, 2006
*
* 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 HV power supply
*
*******************************************************************************
*/
#define BLANK 0x0f // BCD code for nixie blanking
#define MAXCOUNT 15625 // TMR0 overflows in 1 second
#define ADJUST 0
#define timer_d_min 28000 // TMR0 overflows in ~2 seconds
#define timer_h_0 1400 // TMR0 overflows in ~0.1 second
#define timer_h_1 2800 // TMR0 overflows in ~0.2 second
#define putNibble(b) PORTB = b
unsigned int tmrh ;
unsigned int tmrd ;
unsigned char bitnum ;
char last_bit ;
unsigned char parity ;
unsigned char full ;
unsigned char locked ;
unsigned char mode ; // 0:pos, 1:neg, 2:no DCF
unsigned char mn ;
unsigned char hh ;
unsigned int scaler ;
unsigned char ohh, omn, oss ; // RTC
unsigned char dhh, dmn, dss ; // display values
unsigned char pwm ; // PWM output enable
unsigned char vmax, vcurr ;
unsigned char curr ;
void delay_25ms(unsigned char n)
{
while(n)
{ Delay_ms(25) ; n-- ; }
}
/* display a value 00-99 on nixie */
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, flashing vcurr */
void selectDigit(void)
{
unsigned char i, j, k ;
pwm = 1 ;
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 ; }
}
}
}
/*
* ISR
*/
void interrupt(void)
{
if(INTCON.T0IF)
{
PORTB.F7 = !PORTB.F7 & !PORTB.F6 & pwm ;
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 ;
}
/* RTC */
scaler++ ;
if(scaler > MAXCOUNT)
{
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
*/
main()
{
TRISA = 0b00001001 ;
TRISB = 0b01000000 ;
INTCON = 0b10100000 ;
OPTION_REG = 0b11011000 ;
locked = 0 ;
vcurr = EEPROM_Read(0) ; // get last mode
/* select mode 0-2 */
vmax = 2 ;
selectDigit() ;
mode = curr ;
/* if mode 2 (no DCF), set time manually */
vcurr = 10 ;
if(mode == 2)
{
vmax = 2 ; selectDigit() ; ohh = curr * 10 ; // hours tens
vmax = 9 ; selectDigit() ; ohh += curr ; // hours units
vmax = 5 ; selectDigit() ; omn = curr * 10 ; // minutes tens
vmax = 9 ; selectDigit() ; omn += curr ; // minutes units
oss = 3 ;
locked = 1 ;
}
EEPROM_Write(0, mode) ; // store selected mode
/* main loop */
for(;;)
{
if(locked > 0)
{
if(PORTA.F3)
{
/* edit sleep mode schedule */
for(dhh = 0 ; dhh < 24 ; dhh++)
{
pwm = 1 ;
mkDigit(dhh) ;
vcurr = EEPROM_Read(0x10 + dhh) ;
vmax = 1 ;
selectDigit() ;
EEPROM_Write(0x10 + dhh, curr) ;
}
}
else
{
/* display cycle */
PORTB = BLANK ;
pwm = 1 ;
delay_25ms(1) ;
dss = oss ; dmn = omn ; dhh = ohh ;
mkDigit(dhh) ;
mkDigit(dmn) ;
mkDigit(dss) ;
pwm = 0 ; // nixie off
if(EEPROM_Read(0x10 + dhh))
{
delay_25ms(60) ; // short pause
}
else
{
if(mode == 2)
{ delay_25ms(255) ; } // long pause
else
{ locked = 0 ; } // unlock for re-sync
}
}
}
}
}
Download
Single-Tube Nixie Clock files
Pre-compiled HEX file ready to program into your PIC16F84A (16 MHz crystal):
- nixieclock.hex — HEX file (5.3 KB)