Sometimes, a question will lodge in my brain and not let me go until I solve it. Thankfully, these are not the questions of philosophers and theologians, or I would have gone mad(der) ages ago. One lodged in my mind while trying to learn basic electronics. Specifically, the function of a Shift Register. In particular the Texas Instruments SN74HC595. A lovely, difficult to remember name for an inexpensive chip that is designed to remember one byte of data, converting from a serial input to a parallel output.

The first stage of this fixation was just trying to comprehend how this chip worked in a general manner to make some lights light up in a pattern I defined. My simplistic understanding of electronics and electrical engineering is going to irritate the more learned among us, but I will endeavor to be correct and keep it such that those for whom this is a mystery can still follow.

I have made no secret that of late I’ve been running through tutorial projects from an educational electronics kit I recently acquired. But I endeavor to fully understand what is going on and prove it by making the hardware do some arbitrary other thing that I decided upon. When it was just blinkenlights, that was much simpler. But now I was faced with my first integrated circuit – a black box with sixteen pins that did stuff more complicated than just toggle on or off. Yes, at a basic level elements within the black box were just toggling on and off, but I can’t interrogate those elements directly. I have the sixteen pins and the documentation.

In physical appearance it was a piece of black plastic with eight metal pins on each of the long sides, and a small notch on one of the short sides to help you orient it in the correct direction. I didn’t pay enough attention to that notch once and I broke one. Thankfully, they are cheap, and I acquired four more. Electrically, we’re going to abstract things a bit. Because we’re concerned with things when they are working correctly, we will only have two states – high and low – and treat the flow as ending at ground. Because the exact behavior of the electrons isn’t a factor in this discussion, we will avoid that rabbit hole. There’s enough blathering about things I marginally comprehend without getting into more trouble. Since we’re talking about data that originates at a computer, and is stored in the registers, I will end up referencing bits, which are ones and zeroes. Thankfully, these correspond to high and low values respectively at the locations where we can see them.

If the initial text I read had done a better job of describing how the chip worked, I might not have become fixated on delving its mysteries. At the most high level, well above the logically useful, you feed it ones and zeroes on a single pin and it holds on to eight of them, outputting the remembered values on eight separate pins. Now, if I were designing the interface for this chip, I’d have lined up those output pins along one side in a neat row. But, for whatever reason, pin eight is instead dedicated to ground, and the output is done on pins one through seven, and pin fifteen. Worse, pin fifteen is the first digit, followed by pins one through seven, in that order.

From the Official Documentation

In the example I was learning from, they had a jumper tying pins sixteen and ten together, and ran pin thirteen to ground, without explaining one iota of what any of that was supposed to accomplish. An uncurious soul would have seen that power was going into pin sixteen and gone “two power, two ground” and moved on. No, this is very wrong. Pins ten and thirteen have important functions but are also what are known as “Active Low”, which means they trigger their logical functions when not getting current. So, the example had permanently disabled pin ten and permanently enabled pin thirteen. What do they do? Pin thirteen is “Output Enable”. So, by tying it to ground, they permanently turned-on pins one through seven and pin fifteen to send out whatever data they’re responsible for. And pin ten? That pin clears the contents of the register, making it forget what was in there and switch to all zeroes. So, while we could play around with them (and I did change the pin ten wiring to be able to send clear messages) my curiosity there was satisfied with regards to those.

But then there’s pin nine.

In the example, pin nine was not hooked up to anything. It was hanging out there, unused, and that got my attention. Searching the internet, I found that pin nine was there to allow for chaining these chips together. It always output the H bit from the register. Notation wise, the eight bits in the register are lettered A-H. In normal output pin fifteen is A, while one through seven are B-H. So pin nine imitated pin seven. Or so I thought. I might not have kept digging had I not run into an offhand remark in someone else’s article on the chip. They asserted that the purpose of pin nine was to feed into the input of another shift register chip and allow you to control “and infinite number of chips” in series “from just three pins”.

Of course, my head tried to sort out how you would do that. Which is where the as yet undiscussed pins come into play. Eleven, twelve and fourteen are the only ones I haven’t talked about yet. Fourteen is the serial input. It’s where you send the ones and zeroes to get them into memory. If chaining two chips in series, you’d run pin nine from the first to pin fourteen of the next. Pins eleven and twelve are the SR-Clock and the R-Clock. So what exactly are these clock pins? If the chip were a macro-scale mechanical device, these are the levers which cause the mechanism inside to move to its next state. It acts each time either of these pines switching from low to high. Note the language. If the pin stays high, the chip doesn’t care. If the pin switches to low, the chip doesn’t care. It only has an effect when switching from low to high. This is because it triggers a logical operation on the contents of the memory inside the chip, changing to the next state.

To get a handle on what the clock pins trigger, we need a picture of the logical guts of the register. While I’ve been saying it holds eight bits, that’s a bit of a lie. If you try, you can make it remember sixteen. That’s because there are two registers in the chip – the shift register and the storage register. The storage register is the simpler one to talk about. It is the memory which pins fiften and one through seven are outputting when the output enabled pin is low. Any time the R-Clock moves from low to high, whatever is in the shift register gets copied into the storage register, and the output changes. If the R-Clock doesn’t toggle, the output doesn’t change, regardless of the inputs (except for a clear signal on pin ten, or a loss of power overall).

So what about the other register? The one the whole chip is named for? Well, it still has eight bits, from A-H, and it doesn’t care what the R-Clock is doing. It pays attention to the SR-Clock. When the SR-Clock goes from low to high, the shift register shifts all the bits forward one spot, A moving into B, B into C, and so on, forgetting the old value in H, and then reads from the serial input to populate A. So, it’s sort of like a moving sidewalk, advancing every time the SR-Clock ticks, and whenever the R-Clock ticks, a snapshot gets taken and put in the storage register for output.

So what’s that “control multiple chips with just three pins” thing? Well, I could only assume they meant all the chips would have the same two clock signals, and each additional chip would read from pin nine of the previous one.

That’s when my brain hit a snag.

It became convinced that this setup would result in bit H of chip one and bit A of chip two moving in lockstep, providing only seven additional bits per chip and one duplicate. This was not the world the articles were implying. There had to be some means of interrupting the clock so the bits were sequential. So I began mentally drafting crazy circuits. Logical trees of shift registers controlling the clocks of the other chips in the tree, feeding a mix of data and control bits into a system with a rapidly exploding amount of control data for a laughably small amount of stored data. No, that wasn’t what was implied either.

I kept digging into the documentation. There had to be something I was missing. I mean, why have pin nine clone pin seven? Only it wasn’t a clone of pin seven. I didn’t find that detail in the text of anything, but in a timing chart in Texas Instruments’ data sheet on the chip. Pin nine went high a half tick before pin seven. Before? Looking closer at the graph, pin seven was going high on the R-Clock signal as expected. Pin nine was synchronized with the SR-Clock signal, which they had drawn as inverse of the R-Clock. Plus, pin nine was shown as not being impacted my the state of the Output Enable signal. That meant it wasn’t outputting from the storage register the regular output pins were connect to. It had to be outputting bit H of the Shift Register.

Also from the official documentation. Credit Texas Instruments. Red lines added

Okay, so, how does this help me? It means I can push data into the shift registers of as many chips as I like in series without changing the state of the outputs from the other chips. But if they shared an SR-Clock signal, wouldn’t the H bit from chip one still be in lock step with the A bit from chip two in the shift register values?

So what about filling the first chip’s storage register first then the second? Well, if they share an R-Clock, then it doesn’t matter, because the current state of the shift register would overwrite that first round of data.

Well, could we invert the R-Clock signal for the even chips and interleave the writes? I mean, it only happens when the signal switches To high. I suppose it’s possible, but that wasn’t even hinted at in anything I’d seen talking about chaining the chips.

The truth is, I’d reached my theoretical limit. I had to set up a practical experiment where I chained some chips together and saw whether bits 1H and 2A were indeed in lock step, or if there was something at play that made it work as is.

The Experimental Apparatus

*Four Hours Later*

Behold – Actual wires!

I’ve gone and wired two of the chips into a bread board with some cargo cult engineering. I don’t know why the other examples have a 220Ω resister in-line with LEDs. I can speculate that it takes the edge off the current provided to something the LED is happier with, but I don’t know for sure. But, I figured that I should do the same. I attached one LED to each output pin, including pin nine. I attached pin nine of the first chip to pin fourteen on the second. I have both pin thirteens connected to ground to always enable output. I connected the SR-Clocks together and the R-Clocks together, so that the chips are synched. This is the “Three pin” layout the internet implied should work. Though I did choose to connect the clear pin to its own signal source.

Some of the LEDs have been color-coded. I put a Red LED on the pin fifteen signal from each chip, so bit A will show up as red. Blue LEDs are connected to pin seven on each chip, so bit H will show up as blue. I’ve attached green LEDs to pin nine on each to see what that is up to. This will make it easier to examine what’s going on when we run our tests.

I stopped referencing the internet once I decided to do this experimentally. I don’t doubt you can find the exact same circuit elsewhere – it’s not that unique.

The source of power, ground, and the data signals is a ribbon cable adapter connected to a raspberry pi. This lets me programmatically control the inputs to the system. So I can do a number of tests without having to fuss around with the wiring. My first few will be sanity tests to make sure my wiring is correct. Since I only have experience in driving the ribbin cable pins from Python, the code will be in that language. I am an extreme novice in Python, so I borrowed the code from other sources.

Test Number One – One pin at a time

The test code is supposed to hold the input pin to high and then cycle both clocks sixteen times, to fill both registers with ones. As it does so, the LEDs should come on in sequence. While this test can test my hypothesis regarding the chained registers, I am more interested in making sure my layout is done correctly.

#!/usr/bin/env python3

import RPi.GPIO as GPIO
import time

# Set up pins
SDI   = 17 # Pin to write to register
RCLK  = 6 # Write from register to output buffer
SRCLK = 26 # Clock pin, shifts and reads from SDI
SRCLR = 18 # Clears register.

def setup():
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(SDI, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(RCLK, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(SRCLK, GPIO.OUT, initial=GPIO.LOW)
        # Reset is active low, so set to high
        GPIO.setup(SRCLR, GPIO.OUT, initial=GPIO.HIGH)

def main():
        i = 0
        GPIO.output(SDI, GPIO.HIGH)
        for i in range (0,16):
                GPIO.output(SRCLK, GPIO.HIGH)
                time.sleep(0.001)
                GPIO.output(SRCLK, GPIO.LOW)
                time.sleep(0.5)
                GPIO.output(RCLK, GPIO.HIGH)
                time.sleep(0.001)
                GPIO.output(RCLK, GPIO.LOW)
        while TRUE:
                i += 1
                time.sleep(i)

def destroy():
        GPIO.output(SRCLR, GPIO.LOW)
        GPIO.cleanup()

if __name__ == '__main__':
        setup()
        try:
                main()
        except KeyboardInterrupt:
                destroy()

 

So what does that all mean?

#!/usr/bin/env python3

import RPi.GPIO as GPIO
import time

# Set up pins
SDI   = 17 # Pin to write to register
RCLK  = 6 # Write from register to output buffer
SRCLK = 26 # Clock pin, shifts and reads from SDI
SRCLR = 18 # Clears register.

 

Here we define what interpreter we’re using so linux knows what type of file this is. Then we load some libraries that are needed to do pretty much anything beyond arithmetic that we want to do. Lastly we are setting variables to identify which of the pins from the ribbon cable we’re using for what. Now, the pinout on the ribbon cable is… deranged. I assume it is the result of factors that are not immediately obvious to the casual user, but they are in no kind of rational order without that in-depth knowledge. Someday I might run down that, but today, we’re looking at the shift register. So, we’ll let that slide and use whatever was printed on the board.

def setup():
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(SDI, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(RCLK, GPIO.OUT, initial=GPIO.LOW)
        GPIO.setup(SRCLK, GPIO.OUT, initial=GPIO.LOW)
        # Reset is active low, so set to high
        GPIO.setup(SRCLR, GPIO.OUT, initial=GPIO.HIGH)

 

Here we are defining a function called “setup”. This function initializes our hardware pins to a sane default value. The line “GPIO.setmode(GPIO.BCM)” is picking the manner in which we reference the pins off the ribbon cable. There are several naming schemas to choose from. That is also beyond the scope of this discussion. The BCM naming schema is the one we used when we set variables to identify our pins.

def destroy():
        GPIO.output(SRCLR, GPIO.LOW)
        GPIO.cleanup()

if __name__ == '__main__':
        setup()
        try:
                main()
        except KeyboardInterrupt:
                destroy()

 

I skipped to the end for this part of the overview. We define one more function “destroy” which we call to clean up before we exit the program, then we reach the arcane symbols which tell Python that logic execution starts here. It runs setup, then puts our main function in a try clause. This is to catch any errors, exceptions, or interrupts generated. We only have a defined action for “KeyboardInterrupt” which runs the destroy function. KeyboardInterrupt is generated by hitting Ctrl-C on the linux window.

Lastly, we have the guts of the logic.

def main():
        i = 0
        GPIO.output(SDI, GPIO.HIGH)
        for i in range (0,16):
                GPIO.output(SRCLK, GPIO.HIGH)
                time.sleep(0.001)
                GPIO.output(SRCLK, GPIO.LOW)
                time.sleep(0.5)
                GPIO.output(RCLK, GPIO.HIGH)
                time.sleep(0.001)
                GPIO.output(RCLK, GPIO.LOW)
        while TRUE:
                i += 1
                time.sleep(i)

 

The SDI pin is hooked up to pin fourteen on the first chip and thus is the input into the registers. We’re setting it to HIGH and leaving it there so we fill the registers with ones. The for loop cycles the clocks to move the data along and put it into the storage register for output. After going through sixteen iterations, we go on to the infinite while loop. This is so I can observe the state of the LEDs and trigger a cleanup with a Ctrl-C.

The results:

I ran into some bugs as I’d written the code for a Pi 4 and ran it on a Pi5 where they’d gone and done darn silly move of changing the output architecture and thus the libraries. Once I fixed the python environment I altered the code slightly to add another sleep at the end of the main function and flood the registers with zeroes in the destroy function because the Clear pin did not seem to be doing its job. I may have miswired something. But I can work around that. Once I’d fixed the code, the LEDs did come on in a manner consistent with expectation. Now to check the actual internal logic.

Test Number Two – Alternating Bits

Once I’m satisfied that my wiring is correct, I’m going to change the logic in the main function so that it indefinitely feeds alternating ones and zeroes into the input. That code looks like so:

def main():
        i = False
        while True:
                j = not i
                i = j
                if i:
                        GPIO.output(SDI, GPIO.HIGH)
                else:
                        GPIO.output(SDI, GPIO.LOW)
                GPIO.output(SRCLK, GPIO.HIGH)
                time.sleep(0.001)
                GPIO.output(SRCLK, GPIO.LOW)
                time.sleep(0.5)
                GPIO.output(RCLK, GPIO.HIGH)
                time.sleep(0.001)
                GPIO.output(RCLK, GPIO.LOW)
                time.sleep(0.5)

 

Other than this block, the program will be identical to the first test. Instead of a stream of ones, we switch between ones and zeroes forever, so the LEDs will march forward on the regular output bits, on-off-on-off… This repeating pattern will allow an inspection of the behavior of bits 1H and 2A in relation to each other. If my hypothesis is right, the middle red and blue LEDs will match each other and the second set of data bits will be the inverse of the first. If my hypothesis is wrong, the red and blue LEDs will be in opposite states, with both reds being on when the blues are off and vice-versa. In this circumstance, I will have to observe the behavior of the green LEDs to figure out what’s going on.

The results:

The Red LEDs blinked together, opposite the Blue. The Green LEDs lit up a half second before the Blue. The only thing I can think of is that the read operation to set the next state happens at some point or in some manner that captures the value on pin 9 before it changes to the new state triggered by the SR-Clock comes into effect. If I dug into the documentation, I could probably figure out how fast that window is, but it’s immaterial at this time. (Okay, I looked, it might be thirteen nanoseconds if I’m reading this right). I just know that the read for determining the next state of the bits in the register isn’t impacted by the change that takes place during the operation.

At least I’ve satisfied my curiosity. I’ve proven my intuition wrong, but found out I have set up a successful test. I call that a win.