After 20+ years of programming, I wanted to get some understanding of the electronics that make my career possible.
I had built the basic individual logic gates with transistors on a breadboard, but to build anything meaningful I needed loads of them and so I bought a somewhat random collection of logic chips on Amazon which introduced me to the 7400-series and the concept of datasheets.
After a few simple projects I wanted to build something more useful: a quartz clock with LED display.
A practical way to create a clock would be to hook up 7-segment LED displays to an Arduino or Raspberry Pi and write a few lines of code to drive the displays. For this project however I wanted to use just basic logic gates and no code.
BCD and 7-Segment displays
I started with a couple of 7-segment LED displays. Typically these have individual pins for each LED and a common cathode or anode. To display a number, you just have to figure out which LEDs to turn on.
To display the number 5, you’d bring high lines a, c, d, f and g. Number 1 would be b and c. Hard-wiring the pattern for each number takes a lot of wires and gates and so specialized ICs were developed like the 74HC4511 which takes a 4 bit binary number (e.g. 0101 for 5) on its 4 input lines D, C, B and A, and brings the corresponding output lines high (a, c, d, f and g in this case).
This chip only supports the numbers 0 to 9 and not the true width of its 4 bit input. Any binary input over 9 produces a blank display. For example, 1111 (15) does not produce a hexadecimal “F”.
This truncated use of just the lower 10 permutations of a 4 bit binary number is known as Binary Coded Decimal, or BCD.
Now that we can display the numbers 0 to 9 using BCD, we can hook up a binary ripple counter to the 4511’s input. A binary counter is created by daisy-chaining several flip-flops, each dividing the frequency of its input clock by two.
This works by connecting a clocked D-type flip-flop’s inverted output back to its input.
By connecting n of these in series, we get an n-bit binary ripple counter. An example is the 74HC393 chip which contains two separate 4 bit ripple counters, each counting from 0–15.
For my application however I needed a BCD counter that goes to 9, not 15 and so I used the 74HC390 which can be wired up to provide 2 independent BCD counters.
The HC390 actually contains a divide-by-2 and a divide-by-5 stage, each with their own input clock (CP0 and CP1). By connecting the divide-by-2’s output (Q0) to the clock of the divide-by-5 stage (CP1), we get a 0–9 counter. The HC390 contains 2 of these circuits.
To get the first counter to provide a “carry” clock pulse over to the second when it loops back from 9 to 0, we need to add some circuitry that looks for the pattern 1001 (decimal 9) on the BCD output lines of the first counter. We really only need to look for a 1 on lines A and D (Q0 and Q3 on the HC390) as 9 is the only value that matches that.
We run those two lines through an AND gate (74HC08) and connect the output to the second counter’s clock input (2CP0). Since the HC390’s clock input is negative edge triggered, the clock pulse is triggered once the AND gate’s output goes back down (when the first counter goes from 9 back to 0).
At this point we have a 2 digit display that counts from 0–99 and so we need to add a reset signal that caps counting at 0-59 seconds. To do this, we look for the number 6 (0110) on the second counter’s BCD output. We observe that we only need to AND lines 2Q1 and 2Q2 as 6 is the first value to have its middle 2 bits high. We AND these lines and connect the output to the second counter’s master reset line (2MR) to instantly reset it to 0 when it hits 6.
It’s worth noting that technically we count from 0–6, not 0–5, but the value 6 appears for only a few 10s of nanoseconds while the reset propagates, far too short for the LED display to respond.
Minutes and hours
The minutes display is identical to the seconds circuit described above, as both are base-60. We have another 74HC390 dual BCD counter, the first of which has its clock line (CP0) hooked up to same signal that drives the reset (2MR) of the seconds display. This way when the seconds go from 59 to 0, the minutes counter goes up by one.
The additional 2 AND gates needed for carry and reset of the minutes section are taken from the same 74HC08 quad AND chip used for the seconds.
The hours display is a little different, as this is base-24, split across 2 decimal displays with the following rules:
- right (lowest) display counts from 0 to 9
- loop back to 0 and send a carry signal to the left display
- right counts from 0 to 9 again
- loop back to 0 and send another carry signal to the left display
- right display counts from 0 to 4
- when the combined displays hit 24, send a reset signal to both
For the left display that counts from 0 to 2 we only use the divide-by-5 counter section with clock input 2CP1 wired to the carry signal of the right display, identical to the minutes and seconds circuits.
To reset at 24, we observe that 24 in BCD is 0010–0100, which is the first value that has both 2Q2 and 1Q2 high and so we can AND these and wire the result to both reset inputs.
Setting the time
To manually set the time we can take over the clock signals on the counters, hook them up to push buttons to advance them one push at a time. To keep this simple I decided to mimic the interface of many 70s and 80s clock radios that typically had three buttons: one you held down to enter override mode and then one to advance the minutes and one to advance the hours.
While setting the time we should stop the regular clock signal, reset the seconds display to 00, disconnect the normal carry signal from seconds to minutes and minutes to hours so you can loop the minutes counter without causing the hour section to advance.
To do this, we run the clock input lines for the minutes and hours counters through a 74HC157 line multiplexer that allows us to switch the clock inputs from the carry signals over to manual push buttons.
The 74HC157 has four 2-line inputs and one select line (S) which determines which of the input lines are active. The select line is wired up to the “set time” button.
Notice that all signals are inverted along the way. This is because the clock input of the counters is negative edge triggered, while the reset/carry signals that drive the clock of the next stage are positive edge triggered.
The “set time” button not only drives the multiplexer’s select line, it also connects to the second counter’s reset line, ensuring these are reset to “00”. Because that reset pin is also driven by the 59-to-60 trigger, I needed an OR gate to join these two lines. Yet, since this was the only logical OR I needed on the board, I didn’t feel it was worth adding a full quad-OR 74HC32 IC. Instead, I used two diodes to create an OR gate, saving most of the space another IC would’ve taken up.
When hooking up the push buttons to the inverter inputs we need to anticipate contact noise when pushing down or releasing the button. When a mechanical switch makes two metal contacts touch, there is a very brief moment during which the contacts “bounce”, making and breaking the electrical connection.
This can be seen on a logic analyzer or oscilloscope at a high enough sampling rate.
If we’d connect this signal straight to the counter input, a single button press would actually advance the counter by as much as there are bounces.
There are many ways to mitigate this phenomenon, either in hardware or, if a microcontroller is used, in software. We’ll do it in hardware using resistors and capacitors to build a low-pass filter to smooth out the signal transition.
When the push button is not pressed, the
hour_button signal is pulled low through
R5. When the it is now pressed, capacitor
C3 starts charging through resistor
R1, causing the voltage on
hour_button to rise slowly.
VCC at 3V,
R1 at 100KOhm and
C3 at 100nF, it takes about 11 milliseconds for the voltage to rise to 2V (CMOS logic high). If during this time the connection briefly bounces, the voltage no longer abruptly swings between 0V and 3V, but just slightly prolongs the capacitor charging time. This is important because wild rail-to-rail swings are what trigger digital oscillations.
C3 is slowly discharged through diode
D1 and resistor
R5, preventing bounce.
We’re not yet there though.
3.3V CMOS defines a logic low as any voltage below 0.8V and anything over 2V as a logic high. However, behavior between 0.8V and 2V is undefined and our capacitor charges for 11ms, spending a long time in this undefined zone.
To prevent unwanted behavior during this transition, we run the signal through a Schmitt trigger inverter. A Schmitt trigger applies hysteresis to the input signal while in the undefined zone, defining its output state to be its previous state. This means that when rising past 0.8V, the output remains a logic low until 2V is reached and vice versa on the way down.
At this point we have pretty much everything, except for an accurate clock signal. In true vintage style I wanted to use a quartz crystal for this. Quartz is a material which exhibits piezoelectricity: it generates an electrical charge in response to mechanical stress and slightly deforms when exposed to an electrical charge.
This property can be used to create a crystal oscillator circuit. First apply a voltage to the crystal and wait for it to deform to resist the charge. Then when fully deformed, remove the charge and wait for the crystal to counter the change while regaining its shape at which point we reapply the charge.
The duration for a quartz crystal to deform is directly related to its thickness which in turn very precisely defines its resonance frequency. This way quartz crystals can be cut with frequencies from a few kilohertz to hundreds of megahertz.
The mechanism to flip the applied voltage at the end of each phase is often a digital inverter which also provides the gain necessary to keep the oscillation going.
I chose the ECS-2X6X “watch crystal”, with a 32,768Hz resonance frequency. Since 32,768 equals 2¹⁵, we can run the signal through a 15-stage frequency divider to get a precise 1Hz clock.
The 74HC4060 is a combined oscillator inverter and 14 stage ripple counter which should produce an accurate 2Hz signal. We add an additional single flip-flop (74HC74) to provide the final 15th division down to 1Hz.
With all these components in place we could finally wire everything up on a breadboard for a test drive.
Automatic brightness control
I remember having a clock radio as a child whose LED display automatically adjusted its brightness to ambient light conditions. With daylight it was very bright while at night it dimmed so it didn’t light up the whole room.
If I were going to put this on a PCB and actually use it, it needed to have this feature.
In contrast to incandescent light bulbs, the brightness of LEDs is not easily controlled by varying the voltage or current in an analog fashion. A typical red LED won’t emit any light until about 2V is applied and burns out at about at about 3V. When modulating the current through a variable series resistor we get a relatively low contrast ratio and differences in chromaticity or color temperature.
Instead, the brightness of an LED is more easily controlled by making it strobe very quickly, varying the time between on versus off with a process called Pulse Width Modulation, or PWM.
A typical 555 oscillator circuit (astable multivibrator) is shown above. Capacitor
C1 charges through
R2 (t1 — output on pin 3 high) and discharges through
R2 (t2 — pin 3 low). When
R2 have equal resistance, interval t1 (high) is twice as long as t2 (low). The duty cycle is said to be 67%.
To create PWM, we can sink the common cathodes of all 7 segment displays through an NPN transistor to ground and connecting the 555 timer’s output to the transistor’s base.
We’d now like to use a photoresistor for R2 so we can shorten or prolong the discharging time (during which the LEDs are off), while keeping the charging the time fixed.
Because the duty cycle of the standard circuit cannot go below 50%, we add some diodes to bypass R2 during charging, fixing the charge time to ~0.3ms.
Since photoresistors go to many megaohm in total darkness, I put a 470K resistor in parallel to cap the discharge time at maximum 15ms at a flicker-free 64Hz (duty cycle ~2%). In full daylight discharge time falls to as little as 0.1ms for a 80% duty cycle at ~2kHz, providing a wide variation of intensity.
Despite all this, the breadboard prototype would not reach full brightness even in broad daylight. This turned out to be due to a 100K resistor
R12 I initially used between the 555 and the transistor. At 3V and 100K the current through the base is 30uA. The PN2222’s maximum gain ratio is about 300, capping the LEDs drain current at 9mA, insufficient for all displays to operate at full brightness.
I wanted to power the circuit using USB for its ubiquity. However, USB is 5V and while the CMOS chips work fine on that, the quartz crystal’s datasheet suggests driving it with 3V and provides values for the series resistor and load capacitors based on this. Running at 5V might require careful recalculation of these components.
Instead I decided to add a voltage regulator and run the entire circuit at 3V. Since I couldn’t find any readily available 3V fixed regulators (3.3V is a more popular output voltage), I picked the 800mA LD1117V adjustable linear regulator and added a potentiometer so I would be able to tune the exact voltage later on the board.
With a total of 42 LEDs, the six 7 segment displays are the primary energy consumers. Driven at 3V with a 150 Ohm series resistor and 2V drop, they draw 7mA each, for a total of almost 300mA.
Since most smaller TO-92 regulators are limited to ~100mA, the larger 800mA LD11187V was more appropriate.
At this point I was keen to see if I could design a PCB to move the project beyond the breadboard and turn it into an actual usable thing.
After weeks of painful adjustments and changes I decided to take the plunge and send the design off to a board house for a prototype run. I uploaded the project to OshPark and ordered the minimum 3 board batch.
In the meantime I ordered enough components from DigiKey to assemble the 3 boards.
Receiving those first ever boards was quite exciting but also nerve wracking. It would not be easy to make changes or correct mistakes and once everything would be soldered on, even debugging might be a challenge.
Crystal oscillator problems
It was at this point that I hit the first real issue. While the crystal oscillator circuit had performed OK on the breadboard, it did not on the fully assembled PCB. It ran too fast. Exactly 5 times faster than it should.
The cause for the discrepancy between the behavior on the breadboard versus the PCB might be a difference in capacitance. At 22pF, the load capacitors are fairly small, to the point that parasitic capacitance from the traces and wires and added resistance from poor breadboard contacts need to be anticipated and factored in.
During the breadboard phase I was able to experiment with different value load capacitors and series resistor. Now that everything was soldered this was a lot more difficult.
The high cost the PCB put on this stab-in-the-dark, trial and error approach made it unlikely to succeed and the only change I was able to achieve was a configuration at which the crystal oscillated precisely 7 times faster than it should. I was not able to get its frequency down.
I made an attempt to understand the fundamental properties and math behind RLC and crystal oscillator circuits such that I could understand the effects different values for load capacitance, series resistance and voltage would have on the drive level and frequency. That way I should be able to accurate derive the correct component values for my board.
Calculating Rs Value for 32kHz Pierce-Gate Crystal Oscillator Circuit
begingroup\$ Edit: My primary objective is to learn the mathematical model to compute the load resistor \$R_s\$ to…
Unfortunately I’m more of an engineer than an academic and I had to concede that an alternative approach was needed if I wanted to finish this project successfully.
Microelectromechanical system oscillator (MEMS)
A friend then pointed me at oscillator ICs which contain a highly accurate resonator along with all the circuitry necessary to provide a stable, precise output frequency. No tuning or external components are needed making them a convenient substitute for quartz crystal based circuits.
If I could replace the crystal oscillator circuit on the PCB with a MEMS oscillator IC of the same frequency and wire it up to the 74HC4060 clock divider, I might be able to get it running.
It would have to be pretty small to fit in the existing space though and I would have to create a little adapter to provide it with power and connect it to the right traces.
I picked the SiT1630, which is a 32,768Hz MEMS oscillator in a very small SOT23–5 package (they don’t seem to come in through-hole).
I then created a very small PCB with through-hole locations exactly matching those of the crystal circuit. I would then be able to use short solid wires to not only run signal connections between the two boards, but also use them to provide the structural mounting mechanism.
On this little board, H2 connects to one of the load capacitor’s GND leads, H1 uses the other capacitor’s now unconnected through-hole to run a power wire through. This wire sticks through the back of the board where it runs along and connects to the VCC terminal of a nearby IC.
H3 connects to the crystal footprint’s terminal that is connected to the 74HC4060’s clock input, while H4 is not connected to anything and merely provides structural support.
The earlier decision to add a voltage regulator to run the board at 3V instead the 5V USB input proved invaluable at this stage as all readily available MEMS oscillators on DigiKey are rated 1.5–3.63V.
With all this in place I finally had a working clock!
When I did the PCB I put in 5 mounting holes and now it was time to make them useful. To be able to put it up on the wall I created a very simple 3D printed frame on Tinkercad that would leave the entire PCB visible.
I used the public printers at the local library to try different colors and verify the dimensions and then used 3D Hubs to order 3 copies (after all, Oshpark sent me 3 PCBs).
Software projects are rarely true “done”. With software you can just create a new commit and release at any time, continuously improving quality, fixing bugs and adding features. You can test a half-baked version, try it on for size, and adjust accordingly. It’s such a normal part of software development that it’s easy to take it for granted.
I changed and tweaked the PCB layout for weeks, yet pretty much the minute I uploaded it to Oshpark I started finding things I should have changed.
The first thing I regretted was not putting in the ubiquitous 1Hz double blinking dots between the displays. It would have been trivial and they could have stopped blinking when you hit the “set time” button as a visual indicator that you paused the clock.
Another one was that the very first display, like all others, will show a zero. However, most digital clocks show midnight as “0:00” instead of “00:00” and that would have been a nice touch.
And lastly, probably most serious, I forgot to reset the clock divider when “set time” is pressed. Instead, I just bring the seconds counter’s reset line high. As a consequence it’s impossible to precisely synchronize time down to the second. You would let go of “set time” when it’s precisely a full minute, but because the clock divider wasn’t reset, the moment you let you, it won’t take a full second until the first tick. It might tick to 1 almost immediately and so you’d never truly get it synchronized.
Fixing this by modifying the board was impractical, as I tied the clock divider’s reset line to the ground pour on both the top and bottom layer and so I’d have to cut a circle around the pin on both sides, requiring desoldering of the IC socket first.
The project’s schematic and PCB layout files in KiCad, as well as the STL file for the base plate can be found at: https://github.com/erikvanzijst/clock