Building a 4-bit shift register from 7400 NAND gates for GPIO output port expansion on a Raspberry Pi
In my various experiments with the Raspberry Pi, I’ve hit a limitation: the number of unused general-purpose I/O ports available. Quite a few pins are usable on the 40-pin header, but sometimes it just isn’t enough.
One solution to this problem is implementing a shift register. Only two GPIO outputs are needed, one for clock and one for data — the data is shifted in serially, and any number of outputs can be read in parallel, depending on the size of the shift register.
In this article I’ll build a 4-bit shift register from first principles. Ideally we could use a shift register on a chip such as the 7400 series 74HC595:
with 8-bits in this reasonably small package. However I didn’t have any of these shift register ICs on hand, all I had was logic gate ICs. Fortunately, this is all we really need to build a functional shift register.
Latches from NANDs
It all starts with NAND gates, cross-coupled into an /S/R NAND latch:
This is the building block for a gated D latch:
There are four NAND gates in 74HC00 (Quad 2-input NAND gate), so the gated D latch can be constructed with only one 74HC00 chip:
I soldered up four D latches:
Tested each individually using a multimeter, read the output signal (Q) voltage, latched in with the data input (D) when the enable input (E) was high.
But four latches isn’t enough. Added two more, for eight total:
This is a good start, but latches by themselves are insufficient to build a shift register. Clocking is needed:
Flip-flops from latches
Latches can be upgraded to flip-flops, a clocked circuit, by combining two together with an inverted enable. Two D latches plus an inverter create the master-slave falling-edge-triggered flip-flop:
For the inverter, we can use another 74HC00 chip (quad NAND gates), tying together the two inputs of the NANDs to create four NOT gates. Here’s the first master-slave falling-edge-triggered flip-flop I soldered up, with the inverter on the bottom:
Wiring up all of the remaining gates takes a while and gets messy, but when completed, we can store 4 bits in the total of four master-slave flip-flops, each with their own data input (D), clock (C), and data output (Q) lines:
The hardest step is completed, now it is only a matter of chaining the master-slave flip-flops into a shift register.
Shift register from edge-triggered flip-flops
Here’s a serial-in, parallel-out shift register:
Tying all of the edge-triggered clocks together, and feeding the output of one flip-flop into the input of another, creates this 4-bit shift register. To visualize the output, I connected LEDs (green, yellow, blue, and red) in series with 1 kΩ resistors to each of the parallel outputs:
Testing by manually plugging in the clock and data lines into power and ground, the register seems to shift in data on the falling edge. But when the wires are unplugged from the breadboard, the signals are “floating”, unpredictably alternating from high and low, making it difficult to reliably test the circuit in this manner. To test it for real, lets hook it up to a Pi.
Raspberry Pi for input
To shift in data, I connected the shift register to a Raspberry Pi 3. Powered it by +3.3 V, and decided on physical pin #7 (G4 = BCM GPIO 4) for the data signal.
For the clock, I repurposed physical pin #36 (G16) which I previously used for blinking the dot of the 7-segment display in Emerson MW8675W microwave oven teardown: salvaging the LED display (for my now-outdated GPIO pin assignment reference, see the end of Upgrading to a giant breadboard for Raspberry Pi GPIO peripherals). Disabled my gpdot service:
pi@raspberrypi:~/gpio $ sudo service gpdot stop
pi@raspberrypi:~/gpio $ sudo rm /etc/rc5.d/S01gpdot
Testing a simple Python script to clock in each of the bits:
import RPi.GPIO as GPIO
CLOCK = 36 # G16
DATA = 7 # G4
GPIO.setup([CLOCK, DATA], GPIO.OUT, initial=GPIO.LOW)
bits = [1, 1, 1, 1]
for i in range(0, len(bits)):
# falling transition -> clocks in data
and repeating with bits = [0, 0, 0, 0]. With a clock period of 0.1 seconds (frequency of 10 Hz), you can see the bits arrive as they are clocked in realtime. To make this effect invisible, change to time.sleep(0.01) or lower.
Segmented LED display
Next up: testing driving the 7-segment x 4 LED display, lighting up each of the vertical bars, in series with 300 Ω resistors (3.3 V / 300 Ω = 10 mA):
alternating between 1010 and 0101:
How about a more interesting pattern? To accomplish this, whipped up a startup script /etc/init.d/gpshiftreg, then wrote this gpshiftreg.py script:
Saved to /home/pi/gpio then installed with:
pi@raspberrypi:/etc/init.d $ cd /etc/init.d/
pi@raspberrypi:/etc/init.d $ sudo ln -s ~/gpio/gpshiftreg
pi@raspberrypi:/etc/init.d $ sudo ln -s /etc/init.d/gpshiftreg S01gpshiftreg
pi@raspberrypi:/etc/init.d $ /etc/init.d/gpshiftreg start
The vertical segments are lit up in a “bouncing” pattern:
This is more clearly seen in a video:
The dot is wired to the clock, so you can see it briefly blink at each falling edge transition when the data is clocked in. For each sequence, the shift register data is completely clocked in. Since the output is unbuffered, this leaves brief visual glitches when data shifting is in progress, but the effect isn’t too significant. Good enough for now, I’ll consider this success.
Limitations & alternatives
Unlike the 74HCT595 shift register, the outputs in this homemade shift register are not buffered.
The ‘595 has both a shift register and storage register, with separate clocks, so the outputs can be enabled only after all the data is clocked in. This simple circuit is a shift register only, no storage register nor tristate outputs, so it would be unsuitable for purposes where precise control of the output is required (communicating to other digital circuits), but it can work well where spurious intermittent outputs are not harmful, such as when driving LEDs / segmented displays, where the output changes too fast that the intermediate outputs are not humanly visible (or at least, not too visible).
A more interesting application would be to control each of the display’s LEDs, so we could make it display something more useful, such as the current time. This was my original plan, but this homegrown shift register is too small: 4 bits is not nearly enough; recall the pinout from 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
4 outputs from 2 GPIO inputs doubled the I/O ports, but it was a lot of work to build my own from NAND gates (9 x 74HC00B1 chips on a 5x7 cm board), all for a measly four bits.
Complexity & cost
This circuit took too long to construct and had too many interconnects, for what you get out of it. Estimated cost breakdown:
- 36¢ for the ICs = 9 * 74HC00 at 4¢ surplus
- 1.56¢ for the protoboard = $1.56/10 ea from Aliexpress
- 10¢? for wiring, $2.50/spool black/white, CAT5 $2.49 surplus
for a total of about $0.48/4-bits (~12¢/bit). But the real cost is time, the labor of carefully wiring up and soldering the circuit. Instructive for educational purposes, but the most practical.
For more practical purposes, one should consider an integrated shift register chip, such as the MC74HCT595A: 8-Bit Serial-Input/Serial or Parallel-Output Shift Register with Latched 3-State Outputs and LSTTL Compatible Inputs. I’ve ordered ten from Digi-Key for $0.35200/8-bits (4.4¢/bit), each chip providing 8 bits, fully buffered:
Multiple shift registers can be combined (shift out of one register into another) to expand the width arbitrarily. This is much more economical and sensible than building each bit yourself out of NAND gate chips.
TODO: once the new chips arrive, drive all of the LED display segments and make it show something interesting
We have seen it is possible to build a working 4-bit shift register from nine 74HC00 quad NAND integrated circuits, and use it to drive a 7-segment LED display via two Raspberry Pi GPIO outputs.
Here’s the demonstration video if you want to see it again: