GPS time information is often precise down to the tens of nano-seconds. But if all you want to do is display time for humans, you probably would be satisfied with 4 orders of magnitude less precision, which is what this design achieves - 125 µs accuracy (with v5 hardware) with 100 ms granularity.
Over the course of the project, the clock has undergone a number of design revisions. The original design paired an ATTiny841 with a MAX6951 display driver. The 6951 connects to up to 8 seven-segment common cathode LED display digits (with decimal). This project is going to use 7 of them (6 large and 1 small for the 10th of a second digit), plus two LEDs for AM and PM, 4 for two colons between the 3 large groups, and a FIX LED from the GPS module. There are also two pushbuttons for parameter setting (timezone, DST config and display brightness).
The GPS module has a bidirectional UART and a PPS output. What complicates things just slightly is that the NMEA timecodes on the serial port describe the current second, meaning they happen just after the PPS signal. The serial codes aren't precise enough to do more than name the current second. Actual timing needs to come from the PPS signal. So the serial data needs to have a second added to it, and the result is stored in a buffer in RAM. The PPS interrupt transfers that buffer to the display chip. For tenths of a second, a timer in the controller is used for interpolation. The correct counting rate is determined by counting how many timer ticks occur between adjacent PPS interrupts and dividing that by 10. The timer runs from the controller's RC oscillator, configured for 8 MHz, with a divide-by-8 prescale - so approximately 1 MHz. The controller has interrupt handlers for timer overflow and input capture (for the PPS). The overflow interrupt increments a high order word, allowing us to keep a 32 bit counter. Even at a 1 MHz counting rate, it takes more than an hour to overflow a 32 bit counter, and that gives us microsecond measurement granularity. The RC oscillator's short term stability should be good enough for the purpose, and its stability beyond 2 seconds is mooted by the way it's being used.
The 6951 chip communicates with SPI. It requires a chip select line to be driven low and then 16 bits of data is clocked in. The write format is 8 bits of address and 8 bits of data. The chip has a bunch of registers inside that allow the display to be fully configured and even dimmed. The chip has a resistor that's used to configure the chip for constant-current for each LED segment to insure correct brightness. The chip's maximum SPI clock speed is over 20 MHz, and the highest SPI rate we can use with an 8 MHz ATTiny is 4 MHz, so we can run it flat out without worry. Our accuracy claim (~200 µs) stems from the fact that a full display update over SPI takes approximately 70 µs start to finish, and that happens in the PPS capture interrupt handler once a second that takes around 100 µs before the display updates commence (we do go to the trouble of updating the least significant digits first so that hopefully most of the time the only actual changes take place much faster). The accuracy of the tenth updates is likely much better, but keep in mind that their timing is estimated via interpolation, so that's based on the stability of the 8 MHz RC oscillator in the controller over τ 2s.
The brains of the clock is an ATTiny841, simply because I have a bunch of them and they have a UART (actually, two). It runs from the internal 8 MHz oscillator. The entire circuit runs from a 3.3 volt supply, so 12 MHz is the maximum frequency that could be used, but that would require adding an external crystal for no other reason (and rearranging the pins to allow the crystal to be connected).
The GPS module is the Skytraq Venus838LPx-T timing module. The board has an edge-mount SMA connector for an external antenna, and passes 5v (or 3.3v selectable with a solder jumper) active antenna power with an AP2331 current limit switch and a TVS protection diode. The board also has provisions for optional backup power via a 100 mF super-capacitor. The circuit consists of a Schottky diode from the 3.3v rail to the VBatt pin to allow the pin to receive power while the rest of the GPS system is powered up (it's a Schottky diode to reduce voltage drop). There is also a bypass cap near that pin and then the line connects to the supercap through a resistor. The resistor's purpose is to reduce inrush when power is applied when the capacitor is discharged. The resistor spreads the charging over several seconds. Without it, the super-cap would represent an extremely large load on the power supply for long enough to be problematic. During discharge (when the GPS is not powered), the resistor is not a factor since the current required is on the order of a couple dozen µA. The supercap allows the GPS receiver to retain its almanac for almost an hour, allowing it to warm-start across brief power interruptions and obtain a fix much faster. It's also good to retain the almanac, because it includes the current GPS-time-to-UTC-time difference (which is to say, the leap second count). Often when the clock cold boots, it will be some number of seconds off for up to 12 minutes (the leap second correction message is transmitted that often).
At maximum brightness, the display consumes quite a bit of power. To supply it, the input 5v power is dropped down to 3.3 volts by a PAM2305AAB330 buck converter. 3.3 volts is not only required by the GPS module, it's also desirable for the display controller, as it minimizes the voltage drop needed for the correct powering of the display. The PAM2305AAB330 has good efficiency and output noise and ripple, and compared to an LDO is the same size (SOT23-5) it requires only one extra external component. The net result is that the entire clock should run on around 1.25 watts at 5 VDC.
For version 5.0, the architecture was revised. Instead of the MAX6951 and ATTiny841, the design has changed to use an ATXmega32E5 doing software display multiplexing. The XMega series can run at 32 MHz, which is fast enough for our needs. The 32E5 has 3 16 bit timers and a rich event routing system. Two of the 16 bit timers are cascaded to make a 32 bit timer. Running that at full system clock speed allows for 32.5 ns timer granularity but more than 2 minutes of time between overflows. The third timer is also run at 32 MHz, but is used as an interrupt source to run the display rastering. We want to support 4 brightness levels for the display and there are 8 digits (7 actual digits and one virtual one for the colons and AM/PM) and we must put a "dead" slot after each digit to allow the high-side switch to turn fully off (this requires 2 µs and our interrupt timing is 3.125 µs), so a full raster cycle requires 64 interrupts. If the interrupt timer is set for 100 cycles, the result is a 5 kHz raster rate. A high rate is necessary to maintain the accuracy spec, since the digit has to actually be lit with the updated data to count. Since the display raster ISR is around 50 instructions, it winds up taking around 50% of the CPU. But what's left is enough.
The XMega can't source or sink enough current on its pins to make for a bright enough display, so external switching is called for. Toshiba has two chips that fit the bill just perfectly: the TBD62083A and TBD62783A. The 083 is designed to be an 8-way low-side switch and the 783 as an 8 way high-side switch. Both require 8 logic level inputs, with high meaning "on." It turns out that with this system, either common anode or common cathode LED displays can be used. The only difference is which side is used as segment select and which as digit select. Since the MAX6951 required common cathode displays and I have a bunch, I've decided to stick with them.
It would be ideal for LED displays to have a system to deliver constant current, but that would require separate current limiters for the segment lines. Particularly ideal would be to combine that with the high side switch bank, but that doesn't seem like a thing you can get. The next best thing is to supply the LEDs with a constant voltage slightly higher than the Vf rating (we can do this because of the relatively low duty cycle - 12.5% in this case). For our bright red LEDs, the Vf is something around 2.2 volts, so we want around 2.3 volts. This winds up meaning that we have to have 2 different supplies - a constant 3.3 volts for the XMega and GPS, and 2.3 volts for the display. The display requires quite a bit of current - potentially 160 mA at a time. For good efficiency, this means a buck converter. The GPS receiver and controller only require around 50 mA or so. For the 5.0 version, I designed with an LDO, but I'm considering a dual buck converter chip to generate both supplies.
The PPS pin from the GPS module winds up triggering a timer capture event and interrupt. I used an unused pin on port C to measure the worst-case latency of the ISR, and it was around 25 µs. Combined with the maximum display update latency of 100 µs, this keeps us at our 125 µs accuracy specification.
For version 6.0, the architecture from v5 was simplified slightly. The high-side switch array is now connected to the 5 volt rail (in actuality, this rail is not fixed at 5 volts - keep reading) and each output passes through a 30 mA current limiter "diode." This essentially mirrors the architecture of the MAX6951 from v3. The current requirements of the GPS receiver, controller and active antenna are low enough that we can use a simple LDO to supply the 3.3v rail. If we select a wide-input LDO, then the "5 volt" rail - and therefore the power input - need only be within the acceptable range of the LDO and not so high that the voltage drop (and therefore the power dissipation) is not too much. This opens up the architecture to be able to drive larger displays that tend to have increased Vf, but still tend to operate at ~30 mA (keep in mind that this is a multiplexed system with a low duty cycle). The current regulators have a 1.8v headroom requirement (not unlike the MAX6951), so the input power must be above the headroom for the 3.3v LDO and also more than 1.8v above the LED Vf. For most LEDs, the Vf is 2.3 volts or so, so our 5 volt nominal power supplies will be fine. If you want to replace the LEDs with blue ones, the Vf rises to 3.2 volts or so. 5 volts is just barely enough, but you might find better results with 6 volts input.