CKRE150 Project Report

clock

This document is a post-mortem of a Nixie clock incorporating a microcontroller, an FPGA, and a GPS module. This was created for the class CKRE150 at Toronto Metropolitan University.

GPS Nixie clock

The project is a 24-hour, 6-digit clock which retrieves the time via GPS, and displays it using nixie tubes. The use-case for such a clock is to provide maintenance-free accurate time, in an aesthetically pleasing package.

Component list

1 x SIM28 GPS module + antenna

For retrieving GPS information.

This differs from the proposal document, which anticipated using the effectively equivalent NEO6m module.
1 x Arduino uno MCU board (ATmega328P)

For managing the stored time.

1 x Basys3 FPGA board (Artix 7)

For multiplexing the nixie tubes, in conjunction with a transistor array.

1 x 5V→180V power board

For powering the nixie tubes.

17 x 2N6517TA high voltage NPN transistors
6 x 2N6520TA high voltage PNP transistors

19 x 10 KΩ resistors 19 x 143 KΩ resistors For interfacing the low voltage FPGA signaling pins with the high voltage input pins of the nixie tubes.

This differs from the proposal document, which anticipated using only 2N6517TAs.
6 x IN-12B nixie tubes
6 x IN-12 nixie tube socket
4 x Red LEDs
1 x Blue LED

Display indicators.

1 x SPST switch
2 x Momentary button

Input hardware.

Functionality

Power

The clock is powered by an external 5V USB connection.

The proposal anticipated that the device would be powered from the Arduino’s 5 volt connection. However, the Arduino’s VRM can only handle relatively small current loads, which frequently caused issues during development.

GPS Timekeeping

The clock relies on the SIM28 GPS module for timekeeping.

This module tracks the time and whether or not it has a GPS signal. Thus, it would be redundant to track the time on the Arduino. This would not be appropriate if it were intended for the user to be able to manually set the time.

It is undefined what time will be shown prior to first obtaining GPS lock after power on. In practice, the SIM28 starts counting at a time shortly before midnight.

The time zone is accounted for using a manual offset in half-hour increments. Time zones with a 15 minute multiple offset (such as NPT) are not supported.

Why automatic time zone detection is not supported

While it is theoretically possible to determine the time zone using coordinates read from the GPS unit, in practice this was determined to be out of project scope due to storage requirements.

There is an open source C library called ZoneDetect which was evaluated for this purpose. The project was successfully compiled for the Arduino and would have fit (barely) in the available flash memory. However, this library requires a "shape file" with definitions of all the time zones, which is several megabytes even at a low level of precision. The same would apply to any time zone calculation library. This could have been resolved using an SD card reader, but there were two difficulties with this:

  1. The typical way to connect an SD card reader is using the Arduino’s serial pin, which is already in use for the GPS module. This can be overcome using a "soft serial port". The Arduino IDE provides a default implementation of this, but GCC does not.

  2. ZoneDetect uses memory-mapped storage to access the shape data. Implementing this using a serial device is non-trivial.

It was determined that these two issues, while interesting to solve, would likely double the complexity of the project. Other challenges were also anticipated, such as ensuring that the time used to calculate the time zone did not interfere with accurate timekeeping. Thus, this stretch goal was dropped.

Inputs

The following inputs are provided:

  • Two (2) buttons which are used to increment and decrement the timezone in half-hour increments.

  • One (1) switch which toggles between the 12- and 24-hour modes.

Display

The display uses six IN-12B nixie tubes, as described here: https://www.swissnixie.com/tubes/IN12B/.

In 12-hour mode, the decimal point on the most-significant hour digit is used to indicate PM.

When the hour value is a single digit, the leftmost nixie tube is disabled. The minutes and seconds are padded to 2 digits.

The display shall incorporate LEDs used for the following purposes:

  • Four (4) red LEDs in pairs, used to distinguish the hours:minutes:seconds.

  • One (1) blue LED which turns on if the GPS module does not report a valid GPS lock.

Design

The internal design is illustrated by the following diagram:

Diagram

The general flow is as follows:

  • The GPS module is repsonsible for time tracking. It sends messages to the Arduino over the serial connection, at approximately 1 second intervals. These messages include the time in UTC, and the GPS signal status. + The GPS module continues to track the time using an internal clock, even if it does not currently have a GPS lock.

  • The Arduino is responsible for reading the time from the GPS module, handling user inputs, and applying the time zone to the UTC time from the GPS. + Note: The time is not sent to the FPGA immediately upon being received from the GPS module. See the Update Timing subsection of the Design in Detail section for more information.

  • Whenever the second rolls over, the arduino writes out the new time (one digit at a time) to the Basys3, which will store each digit in a register.

  • The Basys3 multiplexes the nixies by providing low-voltage signals to transistors which switch the 180V required by the nixie tubes. On each sweep, the current values stored in the registers are used.

The following image shows the complete prototype.

full

The following image shows the prototype circuit for Arduino, Basys3, GPS module, and HID controls.

Only the rightmost switch is used for timezone control. The leftmost switch is used to disable serial signals from the SIM28 while programming the Arduino. All other switches are unused.
boards annotated

The following image shows the prototype circuit for transistor arrays which control the Nixie tubes.

transistors annotated

Design in Detail

MCU to FPGA protocol

The protocol between the Arduino and the Basys3 uses double-pumped 4-bit parallel signaling, led by the Arduino. The Arduino produces one clock signal clk and four binary signals D0→D3. On the clock’s rising edge the Arduino specifies which digit to control. On the clock’s falling edge the Arduino specifies which value to set it to. All numbers are specified in binary, with D3 being the most significant bit.

Digits are numbered as NXT0→NXT5 (NiXie Tube), with NXT0 being the most significant digit of the hour.

The following example shows how the Arduino would perform the following sequence:

  1. Start from all digits set to 0.

  2. Set NXT4 to 8.

  3. Set NXT5 to 2.

  4. Set NXT4 to 9.

Diagram
NXT0 and the decimal point

For NXT0 only (the least significant hour digit), the device must be able to show the decimal point simultaneously with any other value. However, it is not required to show values larger than 2. These cases can be covered with only the least significant two data bits, D0→D1, which can specify the values 0→3.

For this digit only, D3 instead indicates whether the decimal point should be enabled and D2 indicates whether a number should be shown.

Time accuracy

Because the clock retrieves the time from the GPS module every second, there is no risk of the time drifting. However, there are accuracy concerns regarding when the update occurs each second.

In an ideal scenario, the time displayed would be accurate to within 1 millisecond (the resolution of time transmitted in teh $GPRMC NMEA message). For a display with a maximum resolution of 1 second, this means that the display should update within 1 ms of the wall time changing. It should be noted that being millisecond accurate was not a requirement specified in the original design document. It is thus a "nice to have" feature.

While the GPS module provides NMEA messages with the accurate time at 1 Hz, it does not provide them exactly on the second. Thus, if the Arduino were to read an NMEA message and then immediately update the time, it would always miss the time change target by some significant amount.

For example, consider if the Arduino were to receive an NEMA message with the time 07:53:21.995. If the Arduino sets the time to 07:53:21 only upon receipt of that message, that update would be with a latency of 995 ms, far from the ideal target of 1 ms.

The solution used in this project is to use the NMEA message as the source for the next time to be shown. So in this case, the Arduino sleeps for 5 ms before showing the time 07:53:22. Assuming that the Arduino’s CPU clock does not gain or lose at least 1 ms in a single second, the update should occur within the target window.

Does the device hit the 1 ms accuracy target?

The short answer to this question is "probably, in most cases".

A properly functioning GPS transceiver is considered a stratum 0 source in the Network Time Protocol specification. This means that it can be relied upon as a source of immutable truth for wall time. We therefore assume that the time received from the SIM28 is accurate to within the 1 ms precision provided by the $GPRMC message, at the time the message is transmitted to the hardware UART decoder.

The following diagram shows the pipeline the time signal goes through after being transmitted by the SIM28:

Diagram

The following are points where significant latency could be introduced:

  • The UART baud rate is 115200, which amounts to ~12000 bytes per second. The $GPRMC sentence is ~80 bytes long. The Arduino compensates for this by removing 7 ms from its waiting time. As a result, if the transmitted time is within 7 ms of the rollover, the target will be missed.

The version of SIM28 in the prototype runs at 9600 baud, and therefore has a much longer latency (accounted for correctly in the Arduino code). The production device would use the 115200 baud version.
  • The Arduino runs at 16 MHz. This means that if all other parts of the system were instantaneous, the Arduino program would have 16000 cycles to perform any tasks (including waking from sleep). The functions performed in between reading the message and displaying the time are kept extremely short, and have been checked for low instruction count during development. Nowhere near that number of cycles is used.

  • Nixie tubes can be switched at over 100KHz. The transistors in use can be switched on and off in under 100 microseconds. The 6 nixie tubes are multiplexed by a ~1.5 MHz clock, which is further subdivided by a 3-bit tube selector clock. This means that the entire display is refreshed at ~187 KHz, far in excess of what is required to hit a 1 ms target.

Given that the Arduino clock is capable of sleeping with much better than millisecond accuracy over this short time period, it is reasonable to say that the time is likely to be millisecond accurate for any second in which the Arduino receives the time from the SIM28 before the transmitted second rolls over to the next.

In a fully developed version of this device, it would be necessary to use a high speed camera with a timestamp generated from a stratum 0 source to confirm the actual accuracy of the displayed time, and compensate precisely for any fixed offset.

The final section of this document discusses further improvements that could be made to the millisecond accuracy of the clock.

Input handling

The toggle switch between 12- and 24-hour mode is read before displaying the time.

A timer overflow interrupt is used to check the time zone increment/decrement buttons every 16 ms. This is sufficient to debounce the buttons. The loop which checks this is short enough to not interfere with update accuracy.

Transistor arrays

Nixie tubes require ~180V voltage supply. In addition to 180V anode for each of the six tubes, each number requires its own cathode. Because we are multiplexing the display, this means having six independent anode control circuits and 11 cathode control circuits.

There are several options available for switching 180V using the 3.3v signals of the Basys3 (for example, the vintage SN74141 ICs that were traditionally used to control Nixie tubes). A transistor array was selected, primarily due to low cost.

It was originally planned to use only 2N6517TA NPN transistors for the character selector circuit. In practice, it was found that the NPN design leaked 1 amp when used to disable a tube; far more than was acceptable. It was also found that this circuit did not behave well when multiple were used in parallel, as required for multiplexing. The original circuit was as follows:

failed

Instead, a circuit using a combination of NPN and PNP transistors was used. This circuit is based on an existing design (linked in the references section), which also mitigates a significant amount of ghosting between the characters of a given tube.

revised
One alternative solution which may have worked would be to keep the original transistor arrangement, but adopt the additional 412k resistors of the revised design between the transistors. This would likely have been sufficient to resolve the excessive current draw.

The high-side (the Nixie’s anode) requires two stages of transistors, an NPN and a PNP. The PNP transistor is the primary transistor responsible for switching 180V. The Basys 3 is not capable of creating the required negative current flow on the base, so it is connected to a second NPN transistor which provides a path to ground. When the Basys3 sets its signal high, the NPN transistor allows current to flow from the PNP transistor’s base to ground and thus current is allowed to flow to the Nixie tube. The high resistor value between VCC and the NPN transistor minimizes current draw by the control circuit.

The low-side (the per-character cathode) uses a simple control circuit, wherein an NPN transistor is used to enable or disable that character.

revised

Within a nixie tube, it is possible for an unselected character to use the cathode of a selected character as a high-resistance path to ground. This can cause unselected digits to glow faintly, an effect known as "ghosting". In this design, a high-value resistor joining all cathodes allows the selected character to raise the low-side voltage level slightly. This reduces the apparent voltage for other characters, reducing the probability that those characters will glow.

Further developments

Simplified hardware

Using an FPGA and a microcontroller for this purposes is excessive; the same functionality could be achieved by either:

  • Using only the FPGA, moving the serial and control functionality on to it.

  • Using only the Arduino, in conjunction with specialized ICs that allow controlling high-voltage circuits with fewer GPIO pins.

    This would make achieving timing targets more complicated, since the Arduino would need to manage the time while also multiplexing the Nixie tubes.

Most Nixie clock projects use a simple microcontroller in conjunction with specialized ICs.

Guaranteed millisecond accuracy

There are several possible methods for guaranteeing that we are millisecond accurate in rollover timing.

For example, one option would be to configure a timer on the Arduino to call an interrupt with a one second period. We could update the time within the interrupt. At some regular interval (for example every minute), we could reset the timer based on milliseconds provided by the GPS module, preventing clock drift from affecting the timing precision.

Another possibility would be to use the FPGA as a timing trigger. The FPGA could send a signal every second requesting that the time be incremented and sent. The Arduino could periodically reset that trigger by sending an accurate number of milliseconds.

In both cases, these solutions mitigate the problem that we may receive a time from the SIM28 after the rollover to the next second has already occurred. Both of these solutions use a local clock to maintain second-to-second accuracy, meaning that we will still keep correct time (in the short term) if we receive a message from the GPS module too late.