4x7-segment LED display as a digital clock using two 74HCT595 8-bit shift registers

R. X. Seger
6 min readDec 25, 2016

--

This is a follow up to Building a 4-bit shift register from 7400 NAND gates for GPIO output port expansion on a Raspberry Pi, building a larger (16-bit) shift register to drive all of the LED segments of the display salvaged in Emerson MW8675W microwave oven teardown: salvaging the LED display, ultimately to create a digital clock driven by a Raspberry Pi.

I’ll use another, more practical member of the 7400 series: the ‘595, an 8-bit shift register. Two, chained together for 16-bit total. Specifically, the “HCT” variant of the 74595, high-speed CMOS with TTL compatible logic levels. Here’s the pinout and block diagram for the datasheet of the MC74HCT595A: 8-Bit Serial-Input/Serial or Parallel-Output Shift Register with Latched 3-State Outputs and LSTTL Compatible Inputs:

Through-hole/DIP packages may be more common for casual electronics hobbyist use, but I’m striving to do more with surface-mount electronics, so I opted for the SOIC-16 package of the 74HCT595, which I soldered onto the surface-mount prototyping board as in Surface-mount electronics for hobbyists: easier than you think. Soldered on two, to half of a board:

Wired up power (+5 V), ground, serial input (pin #14, “A”), and both clocks (pin #12 latch clock, pin #11 shift clock) to the Raspberry Pi, in parallel with the 4-bit shift register:

Measured Q_A (pin #15), the first parallel output, with a multimeter and confirmed the output was shifted in, as expected. Shifting is working, time to make it do something useful.

Driving the Anodes, with one 74HCT595

Recall the LED display pinout I reverse-engineered in Emerson MW8675W microwave oven teardown: salvaging the LED display:

  • 1: G1, 2: G2, 3: G3, 4: G4, 5: G5
  • 6: i, 7: h, 8: g, 9: f, 10: e, 11: d, 12: c, 13: b, 14: a

I soldered on 0603 surface-mount chip resistors, 330 Ω for to limit current to the LEDs, onto each of the parallel outputs and wired to the LED display’s anodes (positive, in whichever is most convenient to wire — the ordering will later be assigned in software), and group #5 to +3.3 V. The segments will be driven active-low, emitting a logic zero to turn them on. The active-low output enable (pin #13) is grounded so it always outputs, and the active-low reset (pin #10) is tied to power so it never resets.

For this initial test, only group #5, the leftmost digit, will be illuminated. Wrote a script to shift in 8 bits (e f g b c d i a; omitted ‘h’ for now), here group 5’s segments a, b, c, and e are lit up, after shifting in [1,0,0,1, 1,0,0,1]:

Adding another 74HCT595 for cathode control

To use all four of the digits, more parallel outputs are needed. Time to wire up the second chip, also an 8-bit 74HCT595 shift register. The serial output, SQ_H (pin #9) of the first chip connects to the serial output A (pin #14) of the second chip, altogether creating a 16-bit shift register. Plenty of bits.

First I wired Q_A of the 2nd shift register through a 330 Ω resistor to the ‘h’ segment anode, now all segments in group #5 can be driven, by 9 bits:

The remaining shift register outputs will be used to control the cathode (positive side) of the LEDs, corresponding to the five groups. No current-limiting resistors are needed on these outputs since the 330 Ω’s are already on the anodes.

After soldering on the register outputs to the cathodes, groups can now be selected by outputting high in the leading bits, for example this is [1,0,0,0,0, 0,0,0,0, 0,0,0,0,0,0], turning on all segments of group #2:

The group outputs are active-high, segment outputs are active-low, so current goes between the two logic outputs: one sources, one sinks. The 16 bits correspond to: x 2 3 4 5 1 h a e f g b c d i x, some tests/examples:

[0,1,0,0,0,0, 1,0,0,1, 1,0,0,1,1,0]
[0,0,1,0,0,0, 1,0,0,1, 1,0,0,1,1,0]
[0,0,0,1,0,0, 1,0,0,1, 1,0,0,1,1,0]
[0,0,0,0,1,0, 1,0,0,1, 1,0,0,1,1,0]
[0,0,0,0,0,1, 1,0,0,1, 1,0,0,1,1,0]

Extra outputs

There were two shift register outputs unused, I left one disconnected, and wired up (through another 330 Ω resistor) a red LED, a LiteOn LTST-C191KRKT clear 0603, just for fun. This LED is on the PCB, not part of the 7-segment display itself. Overwhelmingly bright for its size:

Symbol encoding

Now it is a simple matter of writing the software to display each digit. Or other symbol, there’s only so much you can display with 7-segment displays, but hexadecimal is easily displayed in mixed upper/lowercase. Wikipedia has this comprehensive graphic of all 128 possible states:

I programmed 0–9 digits, and various symbols and letters:

But how can we display different symbols on each group? The anodes for each segment are shared by all groups, and cannot be powered individually. The solution is exploiting persistence of vision, rapidly switching between each group and displaying the correct digit faster than you can notice.

First attempt, trying to display “beef”:

You can almost make it out, but with difficulty since the other segments are illuminated albeit briefly as the bits are shifted in.

Output enable/disable vs persistence of vision

To improve readability, I wired up the “output enable” input of both shift registers to another Raspberry Pi GPIO port, G24, physical pin #18. The output is turned off when the data is being shifted in, then turned on immediately after, so no intermediate data is shown — only the segments you want to light up are lit up. Looks a lot better, here’s the beef:

The display is even more visually distinctive in person. With a time.sleep(0.001) between setting each group, you can’t even see any blinking at all.

The output enable/disable line does require another GPIO port on the Raspberry Pi, bringing the total to 3 GPIO’s (serial data in, clock, output enable), but it is worth it. Not bad for sixteen possible parallel outputs.

Clock display

Coding up a script to set the LED display to the current time is straightforward. The only gotcha was I had to change the timezone on the Raspberry Pi by running tzselect, then adding the TZ variable to the startup script, otherwise it is set to UTC. Anyways, with some scripting we now have a digital clock, complete with a blinking center colon each second:

I used the same startup script /etc/init.g/gpshiftreg from Building a 4-bit shift register from 7400 NAND gates for GPIO output port expansion on a Raspberry Pi, and modified gpshiftreg.py as follows:

--

--