LPT-Nixie-Clock
PublicLoading 3D model… large boards can take a moment.
LPT-Nixie-Clock
DOS-controlled Nixie clock, built with four IN-1/LC-516 tubes and interfaced through the LPT port.
Photo of a complete project (LC-516 tubes)Features
- Supports IN-1 and LC-516 Nixie tubes
- Multiplexed display with four digits and a colon
- Onboard high-voltage boost converter; requires only a single 12V power supply
- Serial control interface using just three control wires
- Hardware designed exclusively with easily available components
- Control software implemented as a DOS TSR written in TASM
- Uses the AT-style RTC programming model as the time source and multiplexing timebase
- Connection via the PC’s LPT port
Motivation
This project is another attempt to answer a question I’ve been asking myself ever since I built the Xi8088:
What can I use this PC for that it was never really designed to do, yet is still possible, reasonably practical, and fun to work on?
My first attempt was a WAV player - WAVWSS. With that working, I started looking for another challenge, Nixie clock felt like a perfect next step:
- it smoothly merges insanity with practicality: it is a Nixie clock - a useful and visually appealing device - controlled by genuinely ancient hardware that was never intended for such a task, especially not via a printer port;
- while not fully period-correct, the combination of a PC/XT-class computer built largely from 74xx TTL logic and Nixie tubes has a strong appeal for a low-level tinkerer like me :smile:;
- it provided a good opportunity to learn long-forgotten skills such as writing DOS TSRs, particularly in assembly language, the task I had never tried before. The project is programmatically simple, making debugging relatively straightforward when things went wrong;
- it allowed me to validate the idea of a Nixie tube driver circuit built entirely from easily available components, avoiding long-discontinued parts such as SN74141 or its Soviet counterpart K155ID1, which are increasingly expensive and slowly becoming unobtainium;
- finally, it gave me the motivation to put to use a set of IN-1 Nixie tubes I bought around 2020, but had never used due to their large, hard to use sockets.
Hardware
[!CAUTION] This project involves high voltages! Although the external power supply is only 12V, voltages of around 200V are present on the PCB and components during normal operation. Even higher voltages may occur if the boost converter feedback loop malfunctions, for example due to incorrect PCB assembly. Such voltages can be lethal. The author takes no responsibility for any damage, injury, or loss resulting from the use or construction of this project. Build and operate it entirely at your own risk.
[!WARNING] Use of this project may pose a risk to your vintage hardware. The author accepts no responsibility for any resulting damage.
Schematic and PCB layout
Principle of operation
Logic design and display interface
The logic section, which forms the input interface for the display, consists of U3 (4028), U6 (74HC595), and U7 (74HCT74). U6 together with U7 forms a 9-bit SIPO register with an output latch. The register outputs are connected to:
- the anode drivers for the Nixie tubes;
- the colon cathode driver;
- the BCD-to-decimal decoder (U3), which controls the Nixie cathode drivers.
The display operates in a multiplexed fashion: all corresponding Nixie digits share connections, and only one tube is turned on at a time. Rapidly switching between tubes (every 1/64 millisecond in this project) creates the illusion that all tubes are lit simultaneously. This approach drastically reduces the number of required shift register outputs or control lines compared to static (individual) tube control.
The display control word is loaded serially using three lines: DATA, CLOCK, and LATCH. The loading occurs in two steps:
- The control word is shifted into the register using the
DATAandCLOCKlines. - After the word is fully loaded, a rising edge on the LATCH line updates the output register.
The exact timing diagram, along with a description of each control word bit, is shown below.
A1..4- logic high enables the anode driver, A1 is the leftmost tube1..8- BCD value of a digit to be displayed:- logic high turns on the colon
Some design choices may seem unusual at first. Initially, I did not use the 4028 decoder for the cathode control signals. Instead, the inputs of the Nixie cathode drivers were connected directly to the outputs of two cascaded 74HC595 shift registers. The problem with this approach was efficiency: sending 15 bits (10 cathodes, 4 anodes, colon) to a shift register is more costly than sending just 9 bits. Each bit requires 3 OUT instructions:
- set
DATAline state, - set
CLOCKline high, - set
CLOCKline low.
Each control word also requires a LATCH toggle to update output register: 2 additional OUT instructions.
At a 256Hz multiplexing rate, using a 15-bit register results in:
256 * ((15 * 3) + 2) = 12032 OUT instructions per second
Tests on a breadboard prototype showed that spamming the LPT port with this many instructions from within an ISR was beyond the capabilities of an 8088 at 4.77MHz. Even at 8MHz, the PC was noticeably slowed - for example, WAV playback using WAVWSS would stutter while the clock was running.
Switching to a 9-bit register reduces the required instructions to:
256 * ((9 * 3) + 2) = 7424 OUT instructions per second
Still significant, but much more manageable.
With a 9-bit register, only one bit of a second, cascaded 74HC595 was actually needed. Rather than using an entire additional shift register just for a single bit, I replaced it with a 74HCT74 flip-flop, which met the circuit requirements much better. I also happened to have plenty of 74x74 chips and only a few 74HC595s, so this choice made it easy to make good use of the components I already had on hand.
Anode drivers
For the anode drivers, I usually go with the classic solution of a two-transistor high-side switch, which allows controlling high voltages using standard logic levels.
Two-transistor high side switchThis time, however, I decided to experiment with an approach using optocouplers. I had seen similar solutions in other projects, but they typically used TLP627
optocouplers or alternatives with a similarly high collector-emitter breakdown voltage VCEO ≈ 300V. They are much harder to source than common, widely
available types.
I wondered whether such a high breakdown voltage was really necessary. After some consideration, I thought that as long as the following condition is met, the circuit should operate correctly:
Va < Vm + VCEO
where:
Va - anode voltage from boost converter
Vm - maintaining voltage of a Nixie tube
VCEO - collector-emitter breakdown voltage of an optocoupler’s transistor
Looking through my components, I found a few CNY17-3 optocouplers with VCEO = 70V and decided to test them in a prototype circuit. They worked, and also reduced
the component count compared to the traditional 2-transistor driver, so I decided to keep this solution.
Cathode drivers
Using the same reasoning as for the anode driver, I decided to experiment with ULN2004 chips for the Nixie cathode drivers. These have a VCEO of only 50V, so this solution is definitely not very robust and is far from production-ready. The drivers operate close to or even slightly above their absolute maximum ratings.
During several months of testing, I didn’t blow a single ULN2004 channel, so either I was very lucky or happened to have particularly sturdy chips. However, the ULN2004-based solution does cause an issue when driving the neon bulbs used for the colon; see Known Issues.
[!WARNING] If anyone comes across this project and wants to take inspiration from it, please avoid this approach in your own projects! Operating so close to the maximum ratings of components is an engineering antipattern that should be avoided whenever possible, even in non-critical applications.
Boost converter
The boost converter circuit is a classic NE555-based circuit, using the chip in a somewhat unconventional way. This design has a number of drawbacks: low efficiency, feedback that affects not only the duty cycle but also the switching frequency, poor voltage stability under varying load, significant temperature drift due to the reference being based on forward voltage of feedback transistor's B-E junction, etc.
Despite these limitations, I chose this approach for one main reason: it is simple and relies only on widely available components.
Assembly
[!WARNING] Nixie tubes and neon bulbs are mounted on the opposite side of the PCB from the rest of the components.
PCB - top side
The top side of the PCB contains all components except the Nixie tubes and neon bulbs. When assembling this layer, install the components from the smallest to the largest - this makes the process much easier.
Pay close attention to the polarity of diodes and electrolytic capacitors, and ensure all ICs are installed with the correct orientation - U4 and U5 are oriented differently
from the rest. I strongly recommend using sockets for all ICs. I use them in all of my projects: they are inexpensive and can save a lot of soldering work if any IC ever
needs to be replaced.
PCB - bottom side
The bottom side of the PCB is where the Nixie tubes and neon bulbs are mounted. Start by installing the Nixie tubes. They can be either soldered directly to the PCB or socketed. Both approaches have their advantages and drawbacks:
- direct soldering is quicker, but replacing a tube later will be difficult and will require desoldering;
- socketing makes tube replacement easy, but is more involved during assembly.
If you decide to solder the tubes directly, there is not much to watch out for - the tube base is keyed, so simply align it correctly, insert it into the PCB, and solder.
If you decide to use sockets, the process is a bit more complicated. Unfortunately, Nixie tube sockets were never standardized and are no longer manufactured, so they are not available as off-the-shelf parts. For this design, I used female contacts intended for 5.08mm Molex-type CD/HDD power plugs. These are widely available and likely to remain so for the foreseeable future.
However, these contacts cannot be used as-is - they require slight modification to fit the circular pads on the PCB. This is the most tedious part of the socketing process:
- Remove the cable crimp section of the contact by cutting or breaking it off. Its irregular shape prevents the contact from fitting properly into the circular PCB pad.
- Check the fit in the PCB pad. Some contacts have a slightly oversized diameter. If a contact does not fit easily, gently squeeze the soldering end with pliers to reduce the diameter.
Perform these two steps on all contacts before proceeding, so that they are ready for installation.
Once all contacts are modified, install the first set onto the pins of a Nixie tube. Installing the contacts on the tube first and inserting the assembly as a whole makes it much easier to keep the contacts properly aligned. After the entire set of contacts is placed on the tube, align it with the PCB footprint using the base key, then carefully insert all contacts into their respective pads. This step requires some patience; tweezers are very helpful here.
Once all pins are fully inserted - ideally up to the retention springs - solder the contacts, remove the tube, and repeat the entire process for the remaining three Nixie tubes.
After all the contacts have been soldered, install the neon bulbs, then insert Nixie tubes into the sockets.
PCB - cleaning and final inspection
When the assembly is completed, clean the PCB thoroughly using isopropyl alcohol to remove any residual flux. Perform a careful final inspection: check for bad solder joints, accidental solder bridges or any potential shorts. Make sure all ICs are correctly oriented and look for any obvious mistakes before powering up the board.
Fully assembled PCB - top viewLPT cable
To connect the device to the PC, an LPT-to-board connector cable is required. The wiring diagram is shown below.
| DB-25 LPT pin | PCB connector pin | Signal name |
|---|---|---|
| 2 | 4 | DATA |
| 3 | 3 | CLOCK |
| 4 | 2 | LATCH |
| 18-25 (pick one) | 1 | GND |
On the PC side, a standard male DB-25 connector is used. On the board side, any 4-pin connector with a 2.54 mm pitch that fits the available space can be used. In my build, I chose a Molex KK-254 series connector, mainly because I already had suitable parts left over from another project. The connector may also be omitted entirely, with the cable soldered directly to the PCB.
The cable itself must contain at least four conductors. I used a generic Cat 5e Ethernet cable, taking one wire from each twisted pair for the signal and connecting the corresponding paired wire to ground. This arrangement helps reduce noise pickup on the control lines.
Bring-up
After the PCB is assembled and cleaned, the device is ready to be powered up for the first time. If the board was assembled correctly, it should work without issues. During the initial power-up, the Nixie tube anode voltage must be set to 170V. This adjustment is performed only once.
[!CAUTION] Reminder: high voltages (up to approx. 200V) are present on the PCB during this procedure. Neither the board nor any components should be touched while the device is powered. Capacitor C4 may remain charged for several minutes after the power supply has been turned off. Do not touch the board or components immediately after power-down; always verify that the high-voltage rail has discharged before handling the PCB.
- Set the
RV1potentiometer (labeled170V Adj.) to approximately its mid-position. This provides a safe starting point and helps ensure that the output voltage will not exceed the target value. - Connect a voltmeter with a range of at least 200V to the
TP1test point. - Connect a 9-12V power supply with a current rating of at least 0.5A to the
J1connector, observing correct polarity. The circuit includes reverse-polarity protection, but it will not operate if the supply is connected backwards. If available, a bench power supply with adjustable current limiting is strongly recommended. Set the current limit to 0.5A. - Do not connect the control cable at this stage.
- Turn on the power supply. Some digits may begin to glow faintly - this is normal. Upon power-up, the output states of the SIPO register are undefined. Due to limitations of the 74HC595 reset mechanism (it clears only the shift register, not the output register), implementing a power-on reset circuit that guarantees all tubes are off would significantly increase circuit complexity. Since the display is not intended to operate without control signals, I've decided that this behavior is acceptable.
- Adjust
RV1until the voltage atTP1reads 170V ±2V. Turning the potentiometer clockwise increases the voltage; turning it counterclockwise decreases it. If the voltage atTP1exceeds 250V and cannot be reduced usingRV1, immediately turn off the power supply and verify the assembly of the boost converter's feedback loop. Continued operation at this voltage may causeC4to fail catastrophically. - Turn off the power supply.
- Connect the control cable between the LPT port and the board.
- Start the DOS TSR.
- Turn the power supply back on. If the board and the cable were assembled correctly, the display should show the current RTC time, and the colon should blink once per second.
Known issues
Colon not turning off completely
This issue is caused by the low VCEO rating of the ULN2004. Neon bulbs have a much lower maintaining voltage than Nixie tubes and as a result, the condition described in the Anode Drivers is not met. This causes the ULN2004 output transistor to enter C-E breakdown. Although the transistor is not damaged due to the limited current, it continues to conduct even when its base is not being driven.
I didn't notice this problem during prototyping stage, as the neon bulbs I initially used happened to work correctly under these conditions. They likely have an unusually high maintaining voltage.
This issue can be worked around in two ways:
- Add a parallel resistor across each neon bulb. Solder the resistor directly to the pads of each bulb. A value of 470kΩ works well in most cases. This forms a resistor divider with the neon bulb’s current-limiting resistor and lowers the voltage across the bulb below its maintaining voltage, even while the driver transistor is in breakdown.
- Lower the anode voltage using
RV1until both neon bulbs fully extinguish when turned off. This will also reduce the brightness of Nixie tubes, though.
Too large footprint for non-polar capacitors
Somehow, I ended up selecting a footprint with a 7.5mm pin pitch instead of 5mm, and didn’t notice it despite reviewing the project countless times before placing an order for PCBs. As a result, typical 100nF ceramic disc capacitors are too narrow for the footprint, and their leads need to be bent to fit. I’ll likely fix this if I ever order another batch of PCBs, and (assuming the corrected version works as expected) I’ll update the design files.
Bill of Materials
| Component type | Reference | Description | Quantity | Possible sources and notes |
|---|---|---|---|---|
| PCB | - | LPT Nixie Clock PCB | 1 | Your favourite PCB manufacturer |
| Capacitor | C1, C6, C7, C9-C11 | 100nF ceramic | 6 | TME CC-100N |
| Capacitor | C2 | 2.2nF ceramic or polyester | 1 | TME R82EC1220DQ50J |
| Capacitor | C3 | 47pF 1kV ceramic | 1 | TME CCH-47P/1000V |
| Capacitor | C4 | 4.7μF 250V electrolytic | 1 | TME PF2E4R7MNN0812 |
| Capacitor | C5 | 330μF electrolytic | 1 | TME PF1C331MNN0812 |
| Capacitor | C8 | 10μF electrolytic | 1 | TME CE-10/50PHT-Y |
| Resistor | R1 | 56kΩ | 1 | TME MBB02070C5602FCT00 |
| Resistor | R2 | 1kΩ | 1 | TME MRS25000C1001FCT00 |
| Resistor | R3 | 10kΩ | 1 | TME MRS25000C1002FCT00 |
| Resistor | R4 | 2.2kΩ | 1 | TME MRS25000C2201FCT00 |
| Resistor | R5 | 330kΩ | 1 | TME MBB02070C3303FCT00 |
| Resistor | R6 | 820Ω | 1 | TME MBB02070C8200FCT00 |
| Resistor | R7, R9, R13, R15 | 470Ω | 4 | TME MF006JJ0471A50 |
| Resistor | R8, R10, R14, R16 | 6.8kΩ | 4 | TME MFFU2FF6801A50 |
| Resistor | R11, R12 | 180kΩ | 2 | TME MRS25000C1803FCT00 |
| Resistor | R17 | 0Ω | 1 | Note: used only in prototype, install a jumper |
| Trimpot | RV1 | 2.2kΩ | 1 | TME CA6V-2K2 |
| Diode | D1 | UF4007 | 1 | TME UF4007-DC |
| Diode | D2 | 1N5817 | 1 | TME 1N5817-ST |
| Inductor | L1 | 100μH 1A | 1 | TME RLB0914-101KL |
| Transistor | Q1 | BC547C | 1 | TME BC547C-DIO |
| Transistor | Q2 | IRF740 | 1 | TME IRF740PBF-BE3 |
| Optocoupler | OK1-OK4 | CNY17-3 | 4 | TME CNY17-3X |
| IC | U1 | NE555 | 1 | TME NE555P |
| IC | U2 | 78L05 | 1 | TME AS78L05Z-E1 |
| IC | U3 | 4028 | 1 | TME CD4028BE |
| IC | U4, U5 | ULN2004A | 2 | TME ULN2004A |
| IC | U6 | 74HC595 | 1 | TME 74HC595N-TT |
| IC | U7 | 74HCT74 | 1 | TME SN74HCT74N |
| IC socket | U1 | 8-pin 300mil DIP socket | 1 | TME 1-2199298-2; Note: optional |
| IC socket | U3-U6 | 16-pin 300mil DIP socket | 4 | TME ICVT-16P; Note: optional |
| IC socket | U7 | 14-pin 300mil DIP socket | 1 | TME ICVT-14P; Note: optional |
| Fuse | F1 | 500mA 250VAC 5x20mm slow-blow fuse | 1 | TME UDA500MA250V |
| Fuse socket | F1 | Fuse mounting contact | 2 | TME ZH8 |
| Connector | J1 | 2-pin 5mm 90° screw terminal block | 1 | TME DG301-5.0-2P12 |
| Connector | J2 | Molex KK-254 4-pin PCB connector | 1 | TME MX-6410-04A |
| Neon bulb | NE1, NE2 | Neon bulb w/o resistor | 2 | TME NEON-2 |
| Nixie tube | V1-V4 | IN-1 or LC-516 | 4 | eBay, Allegro etc. |
| Contact | V1-V4 | CD/HDD 5.08mm female power plug contact | 44 | TME DFC01DR; Note: 11 pcs. for each tube; optional, but recommended |
| Plug | - | DB-25 male plug for cable | 1 | TME DSC-025; Note: for signal cable |
| Plug enclosure | - | DB-25 plug enclosure | 1 | TME DSC-225; Note: for signal cable |
| Plug | - | Molex KK-254 4-pin cable connector | 1 | TME MX-47054-1000; Note: for signal cable |
| Contact | - | Molex KK-254 contact | 4 | TME MX-4809C-P914L; Note: for signal cable |
| Cable | - | Any signal cable with at least 4 conductors | 1 | Note: for signal cable |
| Power supply | - | 9-12VDC 0.5A power supply | 1 | TME POSC12100A-25 |
Software
The program was developed on the Xi8088 using Borland Turbo Assembler 2.01. Initially, I considered implementing it in C, as this is my mother tongue in programming :smile:. However, I decided it would be more interesting to go fully low-level, try 8088 assembly, and minimize the size of the TSR as much as possible.
To keep the design simple, I did not implement any unloading or presence-checking mechanisms. I also didn't explore loading the program into upper memory. My Xi8088 build has only 640KB of RAM, so I neither had a practical need for this nor a convenient way to test it. The final TSR occupies 720B of conventional memory.
TSR memory usagePrinciple of operation
At a high level, the program’s functionality is straightforward. Upon startup, it installs an interrupt handler for the RTC interrupt, configures the RTC interrupts and terminates. From that point on, the RTC periodically invokes the installed handler, which is responsible for updating the display.
Internally, the program follows a fairly standard DOS TSR architecture and can be logically divided into an initialization part and a resident part. The initialization part runs when the program is executed and is responsible for the following tasks:
- installing an interrupt handler for INT 70h (IRQ 8, connected to the RTC interrupt line), which effectively drives the entire resident part of the program;
- configuring the RTC periodic interrupt rate to 256Hz, which is used as the display multiplexing timebase;
- enabling the RTC update-ended interrupt, used to update the colon state and time variables;
- unmasking IRQ 8 in the 8259 PIC;
- terminating the program in a way that leaves the resident portion in memory.
The resident part is the core of the program. Each time the RTC asserts an interrupt request, the installed ISR is invoked and executes the following steps:
- all registers clobbered by the ISR are pushed onto the stack;
- the RTC interrupt flags are read to determine the interrupt source;
- if a periodic interrupt is pending, the display update procedure is executed; this procedure keeps track of the currently active Nixie tube to perform multiplexing, converts the time variables into the serial control word format, transmits the data frame via the LPT port, and executes the cathode depoisoning algorithm (described below);
- if an update-ended interrupt is pending, the time update procedure is called; this procedure keeps the time variables and colon state synchronized with the RTC;
- finally, IRQ 8 is acknowledged in the 8259 PICs, register values are restored from the stack, and the ISR returns.
Cathode depoisoning algorithm
The purpose of this algorithm is to prevent (or at least reduce) a well-known phenomenon inherent to Nixie tube technology. When a cathode (digit) is active, material from that cathode is sputtered onto the glass envelope and onto the inactive cathodes. If certain digits remain unused for long periods while others are displayed frequently, the deposited material can become thick enough to disturb the current flow, causing so-called cathode poisoning. A poisoned cathode may glow very weakly, develop dark spots or stop glowing altogether. Some excellent photographs illustrating this phenomenon can be found on Dieter's Nixie World page.
Some Nixie tubes are more prone to this than others, but over time the effect can show up in almost any tube if it is not actively prevented.
To counter this, a simple cathode “depoisoning” algorithm is implemented in the TSR. Once per hour, the algorithm cycles through all digits on all tubes, ensuring that every cathode is periodically activated. This is a standard approach and one I have used in my Nixie-related projects from the beginning. It has proven very effective for IN-12 and LC-531 tubes. I haven't had a long-term experience with IN-1 tubes yet, but I expect it to work just as well - time will tell :smile:.
Depoisoning algorithm at work (IN-1 tubes)Compatibility
The software has not been tested on a wide range of machines, but it should work on any system with:
- an LPT port located at I/O address 378h and,
- an AT-style RTC using the MC146818P / DS1287 programming model.
In addition to the Xi8088 used for development, the program has been tested on a 386-class PC, where it worked without issues.
In principle, the software could be adapted to systems without such an RTC. The display update routine needs to be invoked at a rate of at least 250Hz to maintain smooth multiplexing and an alternative time source would need to be provided.
The TSR uses the RTC interrupt exclusively; the original ISR previously installed on the RTC vector is replaced rather than chained. This is probably not the wisest design choice, but as far as I am aware, DOS itself doesn't use this interrupt for any purpose. Problems may arise if other software expects to receive RTC interrupts. Unfortunately, I don't know which programs might rely on this, as my retrocomputing experience is still fairly limited.
Compiling
Assuming Borland Turbo Assembler 2.01 is installed and added to system PATH, the program can be assembled as follows.
- Assemble the source file
CLOCK.ASMusingTASM:
tasm clock.asm
This will produce the object file CLOCK.OBJ.
- Link the object file using
TLINK:
tlink /t clock.obj
The /t switch tells the linker to use the tiny memory model and generate a .COM file instead of an .EXE.
If both commands run without errors, the output CLOCK.COM file will be created and is ready to run.
No comments yet. Be the first to ask about this board.