Table of Contents
I’ve got a few shifty characters with me today but don’t worry, I’ll show you how to control them and expand the capabilities of your Arduino. And, as a bonus, we’ll build a fancy LED light display.
Introduction
Today we will work with a couple of basic electronics “building blocks”, shift registers. These handy devices are used for all sorts of purposes like data conversion, buffering and storage, but today we will be seeing how they can also be used to expand the number of digital I/O ports on an Arduino or other microcontrollers.
By learning to use shift registers you’ll be adding another handy tool to your designers’ toolkit.
Expand your Arduino
Arduino’s have a number of digital I/O ports already, in fact, the Arduino Mega 250 boasts 54 digital I/O pins plus another 16 analog inputs that can double as digital I/O pins. So with 70 potential I/O pins you don’t usually have a need for more.
But sometimes you actually do need more.
Take the familiar “LED Cube”. A cube with a 4x4x4 dimension would require 64 LEDs, within the capability of an Arduino Mega 2560 if you “borrow” a few analog pins. But you’re nearly at the limit.
If you wanted to expand to a 5x5x5 cube then you’re out of luck, you’ll need 125 LEDs for that and you can’t control them individually with one Arduino.
Even a 4x4x4 cube using standard (i.e. non-addressable) RGB LEDs would put you past the limit.
There are many ways to solve these issues, including running the LEDs ina matrix or using a shift register. The shift register will allow you to address a large number of LEDs using only a few Arduino I/O pins.
There are other times when you have a lot of sensors, displays or other I/O devices and can’t spare a lot of pins for LEDs or switches, but you need a multi-LED display or a small keypad. Again shift registers can come to the rescue.
Let’s examine these handy devices.
Shift Registers
Shift Registers are sequential logic circuits that are used for the conversion, storage or transfer of binary data.
These devices are used to convert between serial and parallel data. They can be used in data communications circuits as well as memory and buffer circuits. Many complex electronic circuits, such as microprocessors and microcontrollers, use shift registers internally.
Types of Shift Registers
Shift registers deal with both serial and parallel data on both their inputs and outputs and they can convert between these formats.
There are four basic types of shift registers:
Serial In – Parallel Out
The Serial In – Parallel Out (SIPO) shift register converts serial data to parallel data. It is used in communications and to drive multiple output ports using serial data.
Parallel In – Serial Out
The Parallel In – Serial Out (PISO) shift register converts parallel data to serial data. It is used in communications and to convert multiple input ports to serial data.
Parallel In – Parallel Out & Serial In – Serial Out
You might find these two strange. Why would you want a shift register that outputs data in the same format that it was input?
The answer is that this can be used as a buffer, to hold data for a specific number of clock cycles. The shift registers we are using today both employ similar buffers to hold data on tehi=r inputs and outputs so it doesn’t change while the register is being shifted through.
How Shift Registers work
Internally shift registers consist of a number of basic logic gates, many arranged as “flip flops”.
If you aren’t familiar with a flip-flop it is a basic electronic circuit that can act to hold the value of data from its input. It is a fundamental building block and is used everywhere, including in many forms of memory circuitry.
A Serial In – Parallel Out, or SIPO, register uses a series of flip-flops, one for each bit on the parallel output. The illustrations here show a 4-bit device.
As the first bit of serial data is clocked in it is stored in the flip-flop and appears on its output.
The next bit of data pushes the original bit over to the next flip-flop.
This process continues as the serial data is clocked in. Note that the flip-flop only updates the input value when it is clocked.
Finally when all the data is clocked in the parallel output can be read. In most shift registers an extra buffer holds the parallel data and doesn’t change it until all the dat ais clocked in.
A PISO, or Parallel In – Serial Out shift register is constructed as follows
The “MUX” section of this diagram actually consists of a number of discrete logic gates, and they serve to feed the data into their associated flip-flop at the correct time.
This is important as a PISO shift register needs to clock each bit of the parallel data individually. This means that the data on the parallel input can’t change while it is being read, again most practical designs employ a buffer to hold the parallel data.
Cascading Shift Registers
A shift register is cataloged by the number of bits it handles, the ones shown in the previous illustrations were 4-bit registers and both of the shift registers that we will be using today are 8-bit devices.
If you need to increase the amount of parallel data you can handle with a shift register you can cascade it with another shift register. So two 8-bit shift registers can support 16-bits, add another one for 24-bits, etc.
You don’t need additional connections to the microcontroller to cascade shift registers, so it’s a great way to drive a lot of LEDs or read a lot of switches without using up a lot of ports.
74HC575 & 74HC165 Shift Registers
We will be using two very common and easily obtained shift registers today, the 74HC595 SIPO and 74HC165 PISO. Let’s take a closer look at these chips.
74HC595 – 8-bit Serial In – Parallel Out
The 74HC575 is an 8-stage serial shift register that also has an internal storage register. The storage register buffers the output data and can be clocked independently of the shift register. This prevents the data from changing while it is being loaded.
The 74HC595 has a “3-state” output. This means that the pins on the parallel data output can be at three different states.
- LOW
- HIGH
- OFF
The OFF state is a high-impedance state that effectively disconnects the output of the chip. This technique allows several 3-state chips to drive the same bus, with only one of them active at any given time.
The pinout of the 74HC575 in the DIP package is illustrated here:
The serial data is input on the DS pin (pin 14). You can use the Q7’ pin to cascade these devices to increase the number of parallel outputs you can control.
The Output Enable (pin 13) controls the 3-state bus, if it is LOW then the output bus is enabled.
74HC165 – 8-bit Parallel In – Serial Out
The 74HC165 is an 8-bit parallel-load shift register which has a serial output. It has complementary outputs, one of which can be tied to another 74HC165 to cascade them.
This device is used for parallel to serial data conversion and has the following pinout:
As with the 74HC595, this is a very common integrated circuit and you should have no trouble obtaining it at almost any electronics supplier.
Extra Output Ports with the 74HC595
We will begin our experiments with the 74HC595 SIPO (Serial In – Parallel Out) shift register.
The 74HC595 allows us to expand the number of digital I/O ports on our Arduino. In these experiments, we will be using it to drive some LEDs, which we will control using an Arduino.
Arduino & 74HC595 Hookup
Here is how we will be hooking up the 74HC595 to an Arduino and to eight LEDs.
Note the addition of a decoupling capacitor, across the power supply, this is a good idea when working with TTL chips like the 74HC595. I used a 100uf capacitor but any value from 10uf upwards will work just fine. Make sure that you observe the polarity of the capacitor.
On my breadboard, I replaced the eight dropping resistors with an 8×2 220-ohm resistor array. This is a convenient component to have when you need a lot of identical resistors. Of course, you may use discrete resistors if you don’t have an array.
There are a lot of wires here so double-check your wiring. You may test the LED-dropping resistor combos by wiring them first and then applying 5 volts to the resistor, if it is wired correctly you’ll light the LED. Repeat the test for all eight resistor-LED pairs. Do this before you wire up the 74HC595 and the Arduino.
Once it is all wired up you can move forward and write some code to make it all work.
Arduino shiftOut() Function
There are a couple of ways to “talk” to a shift register with an Arduino. One way is to use the SPI bus, which would allow you to make use of existing libraries to simplify writing code.
Another way is to use any standard I/O pins on the Arduino to create a clock and to exchange serial data. This is the method we will employ to work with the 74HC595 shift register.
Arduino provides a shiftOut() function to simplify moving data on a serial connection. It can take a byte value and output it in a serial format in sync with a clock pulse on another pin. You can choose to output the data in two directions.
- MSB First – The Most Significant Bit first. So the binary number 10110010 will output a bit at a time starting with “101”, or from left to right.
- LSB First – The Least Significant Bit first. In this case, the binary number 10110010 will output a bit at a time starting with “010”, or from right to left.
We will make use of this function in our sketch.
Arduino & 74HC595 Sketch
Our sketch is pretty simple. The shiftOut function takes care of sending our data to the shift register and of creating a clock signal.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
/* 74HC595 Shift Register Demonstration 1 74hc595-demo.ino Count in Binary and display on 8 LEDs Modified from "Hello World" example by Carlyn Maw,Tom Igoe and David A. Mellis DroneBot Workshop 2020 https://dronebotworkshop.com */ // Define Connections to 74HC595 // ST_CP pin 12 const int latchPin = 10; // SH_CP pin 11 const int clockPin = 11; // DS pin 14 const int dataPin = 12; void setup () { // Setup pins as Outputs pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); } void loop() { // Count from 0 to 255 and display in binary for (int numberToDisplay = 0; numberToDisplay < 256; numberToDisplay++) { // ST_CP LOW to keep LEDs from changing while reading serial data digitalWrite(latchPin, LOW); // Shift out the bits shiftOut(dataPin, clockPin, MSBFIRST, numberToDisplay); // ST_CP HIGH change LEDs digitalWrite(latchPin, HIGH); delay(500); } } |
This sketch is just an adaptation of one that Arduino provides in their excellent lesson on using the 74HC595 with the Arduino. It uses the eight LEDs as the display for a binary counter.
We begin by assigning variable names to the pins connected to the 74HC595. All of these pins are then set up as outputs. We then move on to the Loop.
We use a for-next loop to count from 0 to 255, incrementing by one. On each increment, we write the counter value out to the shift register. The latch pin is used to hold the data until we are ready so that the display doesn’t flicker as the shift register is being loaded.
After a half-second delay, the next number is loaded.
The result is that the LEDs display a binary count from 0 to 255.
You can experiment with the code and manipulate some values and observe the effect on the LEDs. Try changing the MSBFIRST parameter in the shiftOut statement to LSBFIRST and see what happens.
It’s a simple way to understand basic shift register operation.
Driving 7-Segment Displays
Another use for the 74HC575 is to drive a 7-segment LED display. You can use it to red=uce the number of connections to one display, or you can cascade multiple 74HC595s to drive several displays.
7-Segment LED Displays
A typical 7-segment LED display layout is shown here:
Note that there are actually eight LED elements in a “7-Segment” display, the eighth LED is used as a decimal point. In some displays, this may be substituted with a colon.
LED displays are available in two configurations:
- Common Anode – All the LEDs are connected with a common Anode (positive) connection.
- Common Cathode – All the LEDs are connected with a common Cathode (negative) connection.
Both display types use the same pinout, so it is very important to know what type you have. A good way to tell (other than referencing the display part number) is to use a multimeter on the “diode test” function. When connected with the proper polarity it can be used to light the LED elements.
The Common Cathode display is more common and is the type we will be using for our experiment.
74HC595 7-Segment Display Hookup
As the Common Cathode 7-segment LED display is really just eight LEDs connected to a common cathode( (negative) terminal it is no different than the eight LEDs we used in our first experiment. So we can use the exact same circuit to hook it up.
Use the chart in the hookup diagram to connect the display pins to the dropping resistors. The COM pin (the Common Cathode) is connected to the Arduino’s Ground. Note that the display will have two COM pins, you only need to connect one.
Once you have that all hooked up you can test it by running the previous sketch, which should test all the LED segments including the decimal point.
But to actually display something coherent we will need a different sketch.
74HC595 7-Segment Display Sketch
Here is the sketch we will use to test out our 7-segment display.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
/* 74HC595 Shift Register with 7-segment LED display 74hc595-7segdisplay.ino Count in hex from 0-F and display on 7-segment Common Cathode LED display DroneBot Workshop 2020 https://dronebotworkshop.com */ // Define Connections to 74HC595 // ST_CP pin 12 const int latchPin = 10; // SH_CP pin 11 const int clockPin = 11; // DS pin 14 const int dataPin = 12; // Patterns for characters 0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F int datArray[16] = {B11111100, B01100000, B11011010, B11110010, B01100110, B10110110, B10111110, B11100000, B11111110, B11110110, B11101110, B00111110, B10011100, B01111010, B10011110, B10001110}; void setup () { // Setup pins as Outputs pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); } void loop() { // Count from 0 to 15 for (int num = 0; num < 16; num++) { // ST_CP LOW to keep LEDs from changing while reading serial data digitalWrite(latchPin, LOW); // Shift out the bits shiftOut(dataPin, clockPin, MSBFIRST, datArray[num]); // ST_CP HIGH change LEDs digitalWrite(latchPin, HIGH); delay(1000); } } |
This sketch has many similarities to the previous one, which isn’t surprising when you consider that it does pretty well the same thing.
We start again by defining our connections to the 74HC595.
Then we create an array with 16 elements, each one representing the pattern for a character to display on the 7-segment LED.
The elements are written in binary and it makes it simple to understand how they work. Within the binary byte, each bit represents one of the LED segments. From MSB to LSB (left to right) they represent the following segments in the display:
a – b – c – d – e – f – g – DP
When the bit is set to “1” the LED segne=nt is on, a “0” means it is off.
The array is ordered so element 0 is the character for “0”. Element 1 is the character for “1” etc. It is in hexadecimal so element 15 is “F”.
Look at the array elements and you’ll see the pattern.
Once again in Setup, we set our connections as outputs and then move onto the Loop.
Again we use a counter, only this time ist is between 0 and 15. We will display these values as they are counted on the LED display in Hexadecimal format.
We step through the array one element at a time, using shiftOut to send the element data to the shift register.
Load up the code and observe the display. If everything is connected correctly you’ll see it count from 0 to F and then repeat.
Extra Input Ports with the 74HC165
Now that we have seen how to add output ports with a shift register it’s time to do the opposite and add some inputs. For that job, we will use the 74HC165.
We will use the 74HC165 shift register along with eight momentary-contact pushbutton switches. The shift register will take the 8 inputs from the switch and send them to the Arduino as serial data.
Arduino shiftIn() Function
Once again Arduino has a dedicated function for receiving serial data.
The shiftIn() function shifts serial data in one byte at a time. It can be set to take the MSB or LSB first. It is often used with a shift register like the 74HC165 or the CD4021BE.
As with its cousin the shiftOut function the shiftIn function also supplies a clock signal to synchronize the data transmission.
Arduino & 74HC165 Hookup
The inputs of the 74HC165 need to be pulled down LOW to prevent false readings, so in addition to our eight pushbutton switches we will also need eight pulldown resistors. I used 10k resistors, but any value from 4.7k to 27k will work fine.
Once again I used a 100uf decoupling capacitor, make sure you observe the polarity when connecting this.
Once you have it all hooked up we can focus on the sketch we will be using to make this work.
Arduino & 74HC165 Sketch
Our sketch is very simple, as all it does is read the status of the pushbuttons and display the results on the serial monitor. But that’s really all you need to do to understand how to get data from your pushbuttons and the 74HC165.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
/* 74HC165 Shift Register Demonstration 1 74hc165-demo.ino Read from 8 switches and display values on serial monitor DroneBot Workshop 2020 https://dronebotworkshop.com */ // Define Connections to 74HC165 // PL pin 1 int load = 7; // CE pin 15 int clockEnablePin = 4; // Q7 pin 7 int dataIn = 5; // CP pin 2 int clockIn = 6; void setup() { // Setup Serial Monitor Serial.begin(9600); // Setup 74HC165 connections pinMode(load, OUTPUT); pinMode(clockEnablePin, OUTPUT); pinMode(clockIn, OUTPUT); pinMode(dataIn, INPUT); } void loop() { // Write pulse to load pin digitalWrite(load, LOW); delayMicroseconds(5); digitalWrite(load, HIGH); delayMicroseconds(5); // Get data from 74HC165 digitalWrite(clockIn, HIGH); digitalWrite(clockEnablePin, LOW); byte incoming = shiftIn(dataIn, clockIn, LSBFIRST); digitalWrite(clockEnablePin, HIGH); // Print to serial monitor Serial.print("Pin States:\r\n"); Serial.println(incoming, BIN); delay(200); } |
The sketch starts out like all of our previous sketches, defining the four connections to the IC.
In the Setup, we initialize the serial monitor and then set the connections up as required.
In the Loop, we first write a pulse to the load pin, which will have it load the data from its parallel input into a buffer to be worked on.
Next, we set up the 74HC165 to prepare to send data and then use the shiftIn function to get that data, LSB (Least Significant Bit) first. We finish by taking the clock pin HIGH, signifying we are done.
Finally, we print the result to the serial monitor.
Load the sketch, open your serial monitor and observe the output. Right away you’ll notice something.
The data is all held HIGH on the output, the opposite of what is wired on the board. Pressing a pushbutton will cause it to read LOW, even though that is the opposite of what is really happening.
This is because we are using the complementary output from the 74HC165. Our data is inverted.
I’ll show you in a later sketch how to turn it back the right way around. Keep on reading!
The example we just used has many practical applications, one of the obvious ones is as a keypad (although there are better ways to make a large keypad). For a project that requires a lot of switches, it is a useful design technique.
One great application of this circuit is to use it with DIP switches or jumpers, ones that are only occasionally set. You can use the 74NC165 to reduce the number of connections required to read an 8-position DIP switch, and just read it in the Setup routine so it is only read when the device is powered on or reset.
Using the 74HC595 and 74HC165 Together
Of course, it would be a shame to have wired up all of those LEDs and switches without going the extra step to connect them together! So let’s do exactly that.
74HC595 and 74HC165 Hookup
If you built each of the demonstrations on its own solderless breadboard as I did then hooking both of them together is very simple.
Disconnect the Arduino from its breadboard on one of the demonstrations, it doesn’t really matter which one. Leave the connections on the breadboard so that you can reconnect them to the other Arduino. You can connect the 5-volt and Ground connections to the other breadboards power rails.
When you are done try running the previous sketches on the Arduino, everything should still work. If something doesn’t work then check your wiring, something might have become disconnected when you joined the projects – there are a lot of wires here!
Once you have everything tested it’s time to look at a sketch to use both the 74HC165 and 74HC595 together.
74HC595 and 74HC165 Sketch 1 – Boring!
As our demo is essentially two demos fused together our sketch is pretty well the same thing. You’ll see a lot of similarities between this sketch and the previous ones, and it’s no accident – some of it is literally cut and paste!
The purpose of the sketch is to simply use the LEDs to display the status of the pushbuttons. Really boring, and a complete waste of a microcontroller and some shift registers, but as a demonstration, it works well. I promise we’ll move on to something more exciting after this!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
/* 74HC595 & 74HC165 Shift Register Demonstration 74hc595-to-74ch165.ino Input for 8 pushbuttons using 74HC165 Output to 0 LEDs using 74HC595 DroneBot Workshop 2020 https://dronebotworkshop.com */ // Define Connections to 74HC165 // PL pin 1 int load = 7; // CE pin 15 int clockEnablePin = 4; // Q7 pin 7 int dataIn = 5; // CP pin 2 int clockIn = 6; // Define Connections to 74HC595 // ST_CP pin 12 const int latchPin = 10; // SH_CP pin 11 const int clockPin = 11; // DS pin 14 const int dataPin = 12; void setup () { // Setup Serial Monitor Serial.begin(9600); // 74HC165 pins pinMode(load, OUTPUT); pinMode(clockEnablePin, OUTPUT); pinMode(clockIn, OUTPUT); pinMode(dataIn, INPUT); // 74HC595 pins pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); } void loop() { // Read Switches // Write pulse to load pin digitalWrite(load, LOW); delayMicroseconds(5); digitalWrite(load, HIGH); delayMicroseconds(5); // Get data from 74HC165 digitalWrite(clockIn, HIGH); digitalWrite(clockEnablePin, LOW); byte incoming = shiftIn(dataIn, clockIn, LSBFIRST); digitalWrite(clockEnablePin, HIGH); // Print to serial monitor Serial.print("Pin States:\r\n"); Serial.println(incoming, BIN); // Write to LEDs // ST_CP LOW to keep LEDs from changing while reading serial data digitalWrite(latchPin, LOW); // Shift out the bits shiftOut(dataPin, clockPin, LSBFIRST, ~incoming); // ST_CP HIGH change LEDs digitalWrite(latchPin, HIGH); delay(500); } |
We stare by once again defining pin connections to both integrated circuits. And in the Setup, we initialize the serial monitor and set the connections up as required.
The Loop starts with the same routine we used earlier to read the pushbutton values from the 74HC165. Again we insert the data into an 8-bit byte called “incoming” and display its value on the serial monitor.
Next, we use the same code we used earlier to write the data to the 74HC595. But we make one change to the date we send to the shift register.
Remember, our data from the switch is inverted. If we send it to the 74HC595 it will work, but the LEDs will all be on, except where we have pressed a pushbutton.
To invert the date we use the “~” symbol in front of the “incoming” variable when we use it in the shiftOut function. The tilde (squiggly) symbol inverts the binary data, turning every zero into a one and vice versa. Exactly what we need to do.
You’ll also notice that one thing we do differently from the earlier 74HC595 sketch is we send the data LSB first. This matches how we receive it from the pushbuttons. Sending it MSB first would work, but the LED display would be reversed.
Load it up and try it out.
Thrilling, isn’t it?
OK, it really isn’t, but it does show how you can take parallel data (the switch inputs), convert it to serial with a shift register, send it to an Arduino, send it back out to a second shift register and convert it back into parallel again (the LED outputs). And that’s sort of thrilling.
If you aren’t completely thrilled then don’t worry, we can do other things now that we have eight switches and eight LEDs.
74HC595 and 74HC165 Sketch 2 – Exciting!
To add some excitement to our demo let’s use the eight switches to select LED light flashing patterns. As we have eight switches we can select eight patterns.
Here is how we will do it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
/* 74HC595 & 74HC165 Shift Register Demonstration 2 74hc595-to-74ch165-pattern.ino Input from 8 pushbuttons using 74HC165 Output to 8 LEDs using 74HC595 Select LED pattern using pushbuttons DroneBot Workshop 2020 https://dronebotworkshop.com */ // Define Connections to 74HC165 // PL pin 1 int load = 7; // CE pin 15 int clockEnablePin = 4; // Q7 pin 7 int dataIn = 5; // CP pin 2 int clockIn = 6; // Define Connections to 74HC595 // ST_CP pin 12 const int latchPin = 10; // SH_CP pin 11 const int clockPin = 11; // DS pin 14 const int dataPin = 12; // Define data array int datArray[8]; void setup () { // Setup Serial Monitor Serial.begin(9600); // 74HC165 pins pinMode(load, OUTPUT); pinMode(clockEnablePin, OUTPUT); pinMode(clockIn, OUTPUT); pinMode(dataIn, INPUT); // 74HC595 pins pinMode(latchPin,OUTPUT); pinMode(clockPin,OUTPUT); pinMode(dataPin,OUTPUT); } void loop() { // Read Switches // Write pulse to load pin digitalWrite(load,LOW); delayMicroseconds(5); digitalWrite(load,HIGH); delayMicroseconds(5); // Get data from 74HC165 digitalWrite(clockIn,HIGH); digitalWrite(clockEnablePin,LOW); byte incoming = shiftIn(dataIn, clockIn, LSBFIRST); digitalWrite(clockEnablePin,HIGH); // Print to serial monitor Serial.print("Pin States:\r\n"); Serial.println(incoming, BIN); // Setup array for LED pattern switch (incoming) { case B11111110: datArray[0] = B11111111; datArray[1] = B01111110; datArray[2] = B10111101; datArray[3] = B11011011; datArray[4] = B11100111; datArray[5] = B11011011; datArray[6] = B10111101; datArray[7] = B01111110; break; case B11111101: datArray[0] = B00000001; datArray[1] = B00000010; datArray[2] = B00000100; datArray[3] = B00001000; datArray[4] = B00010000; datArray[5] = B00100000; datArray[6] = B01000000; datArray[7] = B10000000; break; case B11111011: datArray[0] = B10000001; datArray[1] = B01000010; datArray[2] = B00100100; datArray[3] = B00011000; datArray[4] = B00000000; datArray[5] = B00100100; datArray[6] = B01000010; datArray[7] = B10000001; break; case B11110111: datArray[0] = B10101010; datArray[1] = B01010101; datArray[2] = B10101010; datArray[3] = B01010101; datArray[4] = B10101010; datArray[5] = B01010101; datArray[6] = B10101010; datArray[7] = B01010101; break; case B11101111: datArray[0] = B10000000; datArray[1] = B00000001; datArray[2] = B01000000; datArray[3] = B00000010; datArray[4] = B00100000; datArray[5] = B00000100; datArray[6] = B00010000; datArray[7] = B00001000; break; case B11011111: datArray[0] = B11000000; datArray[1] = B01100000; datArray[2] = B00110000; datArray[3] = B00011000; datArray[4] = B00001100; datArray[5] = B00000110; datArray[6] = B00000011; datArray[7] = B10000001; break; case B10111111: datArray[0] = B11100000; datArray[1] = B01110000; datArray[2] = B00111000; datArray[3] = B00011100; datArray[4] = B00001110; datArray[5] = B00000111; datArray[6] = B10000011; datArray[7] = B11000001; break; case B01111111: datArray[0] = B10001000; datArray[1] = B01000100; datArray[2] = B00100010; datArray[3] = B00010001; datArray[4] = B10001000; datArray[5] = B01000100; datArray[6] = B00100010; datArray[7] = B00010001; break; default: break; } // Write to LEDs // Count from 0 to 7 for(int num = 0; num < 8; num++) { // ST_CP LOW to keep LEDs from changing while reading serial data digitalWrite(latchPin, LOW); // Shift out the bits shiftOut(dataPin,clockPin,MSBFIRST,datArray[num]); // ST_CP HIGH change LEDs digitalWrite(latchPin, HIGH); delay(200); } } |
As you can see I’ve once again borrowed coded from all of the previous demos. In fact, there is really only one thing different about this sketch, and that’s the switch-case statement that allows you to select the LED patterns.
We use the ”incoming” byte, which holds the switch values, as the switch for the statement. Then we have eight cases, one for each switch press. You could probably add more if you wanted to allow for pressing two buttons simultaneously, but eight was enough for me!
In each case section, we populate the datArray array with LED patterns, written in binary so they are easy to see. In each byte, a “1” represents an LED that is illuminated, whereas “0” indicates the LED is off.
I used eight elements in the array to make it easier, but you can increase this to any number you like. Just change the number in the array definition and in the for-next loop that cycles through the array elements.
I set the delay between pattern changes to 200ms, but you can change that. Even better, try putting the delay as a variable in each case evaluation, so you can make the patterns run at different speeds.
The sketch runs as advertised, and it’s actually a lot of fun to watch.
You could improve on the sketch by cascading the 74HC595 to add more LEDs. You could also make the speed variable by adding a potentiometer to one of the analog inputs and using it to set the delay time. And the enable input on the 74HC595 can be driven with PWM to change LED intensity, which you could control with a second potentiometer.
You could even add some MOSFETs to drive larger LEDs and make your own marquis!
A simple demo with lots of potential.
Conclusion
Shift registers may be elementary building blocks that might seem out of place aside microcontrollers and other more capable chips, but they still can perform important functions in a modern design. They can be very useful if you need to add extra inputs or outputs to your project, they are inexpensive and easy to use.
Nothing shifty about that!
Parts List
Here are some components that you might need to complete the experiments in this article. Please note that some of these links may be affiliate links, and the DroneBot Workshop may receive a commission on your purchases. This does not increase the cost to you and is a method of supporting this ad-free website.
COMING SOON!
Resources
Code for this Article – All of the sketches used in this article in an easy-to-use ZIP file.
PDF Version – The article in a printable PDF format.
74HC165 Datasheet – The specs for the 74HC165.
74HC595 Datasheet – The 74HC595 shift register specs.


Great post and video. Can you share a link for the resistor array that you used? Thanks much.
Bumping this up in case the author is checking comments. Would like the link as well. I’ve done some initial searching but haven’t found anything other than high values (e.g. 10K ohms).
I found this on Mouser
I found 220 ohm array on amazon. But it would be nice to find a kit of arrays with multiple values.
Make your own.
Hello,
Thanks for the great content. It’s very informative and easy to follow. There was a small error in the 7- segment display hookup. The wires conecting to “F” and “G” pins were swapped. For the display to work correctly “G” needs to connect to “D1” and “F” to “D2”.
Thank you very clear vvery good explained 👍
Great tutorial, 74HC595 works like a charm. I don’t have a technical background but I was wondering does the 74HC595 and 8 burning LED’s (or more if you cascade multiple shift registers) exceed the 150ma that an Arduino can handle??
USE A TRANSISTOR IN THAT CASE IF YOU BC547 FOR EXAMPLE CONNECT ARDUINO OUTPUT WITH BASE EXTERNAL POWER SUPPLY WITH COLLECT EXTERNAL BASE WITH EMITTER NOW YOU CAN USE AS MUCH CURRENT AS YOU R POWER SOURCE CAN SUPPLY IF YOU HAVE A VERY HIGH POWER RATING EQUIPMENT THAT YOU WANT TO USE YOU CAN EXPLORE FOLLOWING TOPICS
>> USING TRANSISTORS AS A SWITCH
>> USING JFET AS A SWITCH
The LEDs are powered by the 595s power source
Just to let you know that after seeing this I am combining a the two chips to aid in Reprogramming a AT28C256 15PI Using these two chips allows me to write and also over come the issue of getting the WE pluse time down to 650 ns using a NPN transistor. If this works then I can show you what I have done and how a NANO Arduino can be used to program. Which Ben Eater showed when programming a 8 bit computer. By using these chips so much more can be accomplished. Including a robotic car using old but… Read more »
This is a fantastic guide, thank you. Is there an easy way of expanding 74HC595 and 74HC165 Sketch 1 to two of each (i.e. 16 button and 16 LEDs) please?
Hy,Im using 74HC165 but when pressing button it doesnt returns the change in state it always return 11111111.
I’ve got the exact same problem and haven’t found a solution yet.
I already grounded both unused pins (Q7 & DS) but without any effect on the result.
As soon as I press the push button connected to D7, it returns 0.
hello, did you find any solution?
Hello. My name is Swaraj. Here I see that HC165 pins are not connected to MISO, MOSI, SS, SCK pins of arduino. HC595 was connected to those. Does it matter for 165 too ?. As iam wishing to connect an sd card module that requires MISO, SCK, SS, MOSI pins of arduino. Can I use both hc165 and sd card module on the same arduino ?
For some reason I had to change MSB to LSB for the 74HC595. I cannot really figure out how that’s possible.
The example for 74HC165 that is shown above for buttons reading is for 1 shift register. Can you please share a simple code for cascaded 74HC165s. Just want to read button states, nothing else. Thankyou.
Hi, I just came across you tutorial and find it very well structured. Big thumbs up for the helping others out. Would one consider having 74HC165s and 74HC595s on the same clock/ce/latch lines, daisy them and keep a register in the uC of which is which? I didn’t try it (yet). You would have 8 ins and 8 outs on just 4 uC pins, but will it work?
Great info would be great for some of those really tiny boards that only have like 2-8 ports.
About 8-bit counter…
It takes 4min. 16s to light up all LEDs (1 tick per second).
I was just curious, so I made some calculations how much time would it take to light up 16-bit, 24-bit, 32-bit and 64-bit counter.
Here are my results:
8-bit counter 4 min. 16 sec.
16-bit counter 18 hrs 12 min. 16 sec.
24-bit counter ~194 days
32-bit counter ~136 years
64-bit counter ~500 billion years
For comparison, the age of the Universe is estimated to be ~14 billion years…
Think about the electricity bill for running Arduino for 500 billion years… 😉
In the video on using shift registers with the arduino you said that using the output of pin 9 not all 8 bits would be shifted in. could you elaberate?
I am using a Texas Instruments SN74HC165 and my bits seemed shifted to the left one bit, and I was not getting the last bit to show up. After confirming with the datasheet it turns out the shiftIn() function needed to pulse the clock LOW to HIGH. So I had to add my own version and use that instead. uint8_t myShiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) { uint8_t value = 0; uint8_t i; for (i = 0; i < 8; ++i) { digitalWrite(clockPin, LOW); if (bitOrder == LSBFIRST) value |= digitalRead(dataPin) << i; else value |= digitalRead(dataPin) << (7… Read more »
Hy,Im using 74HC165 but when pressing button it doesnt returns the change in state it always return 11111111.
If using multiple Shift Registers (SN74HC595N) and an Arduino Nano can I hook all of the clock pins up on the same wire to save pins on the Nano.
Good day, I was pleased to stumble onto your workshop about “universal shift registers.” In which programming language is the following code written in ? 74HC595 & 74HC165 Shift Register Demonstration 2 74hc595-to-74ch165-pattern.ino Input from 8 pushbuttons using 74HC165 Output to 8 LEDs using 74HC595 Select LED pattern using pushbuttons DroneBot Workshop 2020 https://dronebotworkshop.com */ // Define Connections to 74HC165 // PL pin 1 int load = 7; // CE pin 15 int clockEnablePin = 4; // Q7 pin 7 int dataIn = 5; // CP pin 2 int clockIn = 6; // Define Connections to 74HC595 // ST_CP pin… Read more »
Nice presentation, as usual! Learned a few details about shifting in both directions. Cascading I was taught (back in the early 80s) also goes by the term “daisy chaining”. Is that term still applicable?
Hey there! I am using two concatenated 74HC165s for reading incoming data (13 bits). But there are always the same bit errors when reading special pattern (in the first 8 bits = first serial shifter), for some pattern the bits are always correct. I checked the parallel input which is always correct, but the serial output is incorrect, so it is not a problem with my reading function since the bits are already arriving incorrectly at my MCU (and I also set the time for load to 5mus, which should be more than enough). Thus, I replaced my first PISO-shifter… Read more »
Don’t forget to tie pin 9 from the HC165 to ground! This was missing in this tutorial.
What effect does that have? Appreciate your question and will look forward to the answer.
Excelente artigo. Muito completo. Eu vou recomendar este blog para as pessoas aqui no Brasil. Muito bonito e bem construido. Didático e objetivo proporcionando muito conhecimento. Parabéns pelo bonito trabalho.
Thank you for this great post, i have a question, if we want each button to light a specific LED and stay on until pressed again, with the possibility of lighting one, two or all the LEDs at the same time, is that possible?
im trying to figure out the same thing
Hi . Thank you . I have a question in shiftOut() . What is different LSBFIRST and MSBFIRST?
I am cascading two 595 shift registers and everything (normally) works like it should. However, if I use Serial.print (or println) in the sketch, the shiftOut routines output random data. (Confirmed with logic analyzer) It does not matter if interrupts are off when sending the data to the shift registers. Has anyone else seen this behavior? (I am using PlatformIO)
I’m building a virtual pipe organ with 4 keyboards, 61 notes each, a pedalboard with 32 notes, each keyboard has from 10 to 20 small tactile switches under the front edges of the keys to use as shortcut memory recall switches. All this works and the MIDI data generated by each key and thumb piston goes onto the first of 3 laptop computers and is relayed to the others over my LAN. The next step is to set up a couple more arduinos on the second computer. These should connect to the 256 LEDs that will indicate which organ stops… Read more »
Is there a PDF version of this project?
ctrl+p and save as pdf :p
Yeah I did create my own PDF but it’s full of all the other crap.
hi i made the item with shift register 74HC165 and the 8 buttons exactly as the manual provided with the same code, wierd thing all buttons work except button 0
when i pres for ex button 2 i got 5 times one and in between a zero proving that the button is pressed
odly if i the first button, eg btn0, i got only 5 times 1 , so its like thhe shift register only outputs 5 buttons and not 6 anymore, of i release this button than ive got my 6 time one again
any idea?
Ok I’ve got the 165 button push working and the binary counter working. The button press seems to drop the leading zero. So when you push the 8th button the result ends up being 1111111 instead of 01111111. I found I had a bad 595 because one led wasn’t lighting up even though a test sending current directly to it showed that it worked. And the led counter is doing MSBFIRST when it should be LSBFIRST. Now on to the rest of the article. PS. In the 165 button push I increased the delayMicroseconds to 50 instead of 5 since… Read more »
Could you do a follow up to this post showing the use of multiple registers? Two 595s and two 165s?
I have a problem! Please consider helping me. I used 4 SIPO shift registers to control 4 * 8 relay modules, all controlled by an Arduino Uno and IR remote. This project was meant to control about 24 AC lamps and fans and 2 DC lamps. I tried everything before wiring it to the main control panel. Everything was going just fine, so I wired everything up. When I turn on a dc lamp nothing goes wrong, but when I attempt to turn on any AC device all relays turn on immediately. Tracking the problem I found out that if… Read more »
Is it possible to use both 595 and 165 shift registers (2 of each) with an ATTiny85? I have a couple of DigiSparks and am looking at a small project to take say twelve capacitive touch sensors and use them to switch twelve 12v outputs (with optocouplers in between to operate relays or similar). I could use just an ATTiny chip or the Digispark as I have both, but not sure how to connect the registers to the Digispark/Tiny.
I’d appreciate any input, but please bear in mind I am a beginner.
Thanks
Thank you so much!