top of page

Control LEDs with a Shift Register and Arduino

If you're a DIY enthusiast or an electronics hobbyist, you're probably familiar with Arduino, the open-source microcontroller platform that enables you to create a wide range of projects. One of the most exciting applications of Arduino is controlling LEDs. This guide will explore controlling LEDs using a shift register and an Arduino.



Understanding Shift Registers

Over the following few subsections, we'll explore shift register basics, mechanics, and operation.


What is a Shift Register?

A shift register is a digital circuit component that enables the storage and movement of binary data sequentially, allowing you to expand the number of output pins on a microcontroller, such as an Arduino. By using shift registers, you can control many LEDs or other devices with just a few pins from the microcontroller.


How Does a Shift Register Work?

A shift register works by storing and shifting bits of data sequentially. It has several input and output pins, including data (SER), clock (SRCLK), and latch enable (RCLK) pins. These pins play vital roles in shifting data into the shift register.


To begin, we bring the latch pin low/ground. This action prepares the shift register for receiving new data. Next, we start the shifting process by toggling the clock pin. Each toggle of the clock pin represents one clock pulse, which drives the shifting operation.


Now, let's examine the role of the data pin. The data pin determines whether a 0 or a 1 is shifted into the shift register. If the data pin is set high, a 1 is shifted in. On the other hand, if the data pin is set low, a 0 is shifted in.


For example, if we want to shift in the binary value 1010, we would follow these steps:

  1. Bring the latch pin low.

  2. Set the data pin high (1) and toggle the clock pin.

  3. Set the data pin low (0) and toggle the clock pin.

  4. Set the data pin high (1) and toggle the clock pin.

  5. Set the data pin low (0) and toggle the clock pin.

With each clock pulse, the shift register advances and shifts the bits one position over. The data pin value determines whether a 0 or a 1 is shifted into the shift register at each clock pulse.


We raise the latch pin high after shifting all the desired bits into the shift register. This locks the data in the shift register and transfers it to the output pins, controlling the LEDs accordingly.

Diagram depicting data shifting into a 74HC595 shift register
Visual representation of the shifting operation between the data and clock pins

Choosing the Right Shift Register for Your Project

When selecting a shift register for your LED project, consider the number of LEDs you want to control and the type of shift register that suits your needs. One popular shift register is the 74HC595, which is widely used in Arduino projects. It has eight output pins and can control up to eight LEDs per shift register.


If you require more pins, you can cascade multiple shift registers together, allowing you to control hundreds of LEDs. Additionally, other shift registers like the TPIC6B595 offer higher current-handling capabilities, making them suitable for projects that require higher current outputs.


Shift Register Pinout

For this project, we'll be using a 74HC595 shift register. Below is an image with the pinout for this integrated circuit (IC).

74HC595 pin labels/pinout

Wiring the Shift Register and LEDs to an Arduino

The following table illustrates the connections between the shift register and Arduino for a basic setup using the 74HC595 shift register:

Shift Register Pin

Arduino Pin

SER (Serial Data)

Digital Pin 4

RCLK (Register Clock)

Digital Pin 5

SRCLK (Shift Register Clock)

Digital Pin 6

SRCLR (Shift Register Clear)

5V (VCC)

OE (Output Enable)

Ground (GND)

VCC (Power Supply)

5V (VCC)

GND (Ground)

Ground (GND)

Q0-Q7 (Output Pins)

LEDs

Schematic of a 74HC595 wired to eight LEDs
Completed Schematic for 74HC595

Step 1:

Start by placing the shift register into the breadboard across the separating notch. Pin 1 of the shift register is in the top left corner, indicated by the small white dot. You likely will not have a white dot on the actual shift register. Look for a small notch/indent in the IC, which indicates pin 1.

74HC595 shift register in a breadboard and an Arduino

Step 2:

Grab three jumper wires and connect digital pin 4 of the Arduino to pin 14 (SER) of the shift register, pin 5 of the Arduino to pin 12 (RCLK) of the shift register, and finally, pin 5 of the Arduino to pin 11 (SRCLK) of the shift register. In the image below, the purple wire connects the Arduino to the SER pin, the orange wire connects the Arduino to the RCLK pin, and the yellow wire connects the Arduino to the SRCLK pin.

74HC595 shift register connected to an Arduino

Step 3:

Next, we will connect the output enable (OE) pin to the ground rail of the breadboard and the shift register clear (SRCLR) pin to the power rail of the breadboard.

74HC595 shift register connected to an Arduino Uno

Important Note: The OE and SRCLR pins are active low pins. We would like the output pins of the shift register to be enabled, so we tie the OE pin to the ground rail, but we do not want the shift register to clear, so we tie the SRCLR pin to the power rail.


Step 4:

Now take two jumper wires and connect the GND pin of the shift register (pin 9) to the ground rail of the breadboard. Then connect the VCC pin of the shift register (pin 16) to the power rail of the breadboard.

74HC595 shift register connected to an Arduino

Step 5:

Place the eight LEDs into the breadboard in a straight line. Ensure that the LEDs are also in the same orientation, i.e., the anode and cathode of each LED face the same direction. Then connect eight 220Ω resistors in series with the LEDs (one resistor for each LED). It does not matter which end you connect the resistor to.

74HC595 shift register and LEDs connected to an Arduino

Step 6:

With your LEDs in the breadboard, connect each LED to an output pin of the shift register, where the first LED is connected to the QA pin and the last to the QH pin.

74HC595 shift register and LEDs connected to an Arduino

Step 7:

The last thing to do is connect the power and ground rails to the Arduino. Connect the power rail of the breadboard to +5V and the ground rail to a GND pin.

74HC595 shift register and LEDs connected to an Arduino

Programming the Arduino to Control a Shift Register

With the LEDs and shift register adequately wired to the Arduino, we are ready to start writing code. Below is a table of all primary functions that will be used to program the Arduino.

Function

What it Does

pinMode

Sets a pin on the Arduino as either an input or output pin

digitalWrite

Sets an output pin to either HIGH (+5V) or LOW (0V)

delay

Pauses the program's execution for a specified time in milliseconds

Shift register specific function that shifts out each bit of data sequentially while controlling the data transfer timing and the clock signal.

Sets a specific bit within a variable to a logic HIGH state. It allows you to manipulate individual bits by specifying the bit position to be set while preserving the state of other bits.


Define Output Pins

While wiring the shift register to the Arduino, we used digital pins 4-6. Let's create three constant variables that sore these values.


Setup Output Pins

We must tell the Arduino that digital pins 4-6 are outputs. We can do this in the setup function using the "pinMode" function.


Shift Data to the Shift Register

Before we create any animations, let's write a for loop that will shift the numbers 0-255 to the shift register.

To shift data into the shift register, we must first bring the latch pin down to GND. On line 3, you can see this is accomplished by using the "digitalWrite" function and passing LOW. Now that the latch is at zero volts, we can use the "shiftOut" function to write the current iteration number to the shift register. The "shiftOut" function expects four parameters: data and clock pin numbers, order to write the bits, and the actual data (just a number).


To specify which bit to start writing first, you use one of the following Arduino constants: LSBFIRST and MSBFIRST, which are "least significant bit first" and "most significant bit first," respectively. If you change the code above to use MSBFIRST, you'll see that the output is "rotated" 180° from the original.


Here's the completed code:





Working with the bitSet Function

Using the above code, you can create any pattern you'd like if you know the correct decimal number. Suppose you wanted to light up every other LED. You'd have to know that the value 170 has the following binary representation: 10101010. I don't know about you, but if I looked back at that code a month from now, I'd have no idea why I was writing 170 to the shift register. This is where the "bitSet()" function comes in. Rather than figuring out the correct number ourselves, the "bitSet()" function will create the number for us one step at a time.


How does the bitSet Function Work?

Imagine you have a variable, let's call it "num," which consists of a sequence of bits. Each bit in the number variable can either be 0 or 1. The "bitSet" function allows you to set a particular bit in that variable to 1.


To use the bitSet function, you provide two arguments: the variable you want to modify and the position of the bit you want to set to 1. The position of the bit starts from 0, meaning the first bit is at position 0, the second bit is at position 1, and so on. It's like indexing an array.


When you call the "bitSet" function, it changes the value of the specified bit in the variable to 1, leaving the other bits unchanged.


For example, let's say you have a variable "num" with the value 5, represented in binary as 00000101. If you call the bitSet function with "num" and position 6 as arguments, it will modify the variable number to 01000101, setting the seventh bit (position 6) to 1.



Create a Helper Function

In the shift register test code I provided above, I directly brought the latch pin low, shifted out the data, and then brought the latch pin high. Instead, let's create a simple function that takes in one byte and shifts that data to the register. Then we can use this function throughout our main loop.

If you're chaining multiple shift registers together, change the "num" variable to an integer rather than a byte.



A Digestible Use of the bitSet Function

Before creating any animations, let's look at a simple example that uses the "bitSet" function.

First, I created a new variable called "leds." This variable stores the output number we create with the "bitSet()" function.


This code snippet then uses a loop to iterate through numbers 0 to 7. Inside the loop, the function "bitSet()" is used to set the bit at the current loop index (i) within the "leds" variable to 1.


Then, the function "updateShiftRegister()" is called to shift the bits from the "leds" variable into a shift register. A delay of 500 milliseconds is introduced using the "delay" function. After the delay, the "leds" variable is reset to 0, setting all the bits to 0.


This sequence repeats, effectively shifting 1s through the shift register, one location at a time, with a delay between each shift. You could also move the "leds = 0;" on line eight outside the for loop to see a different effect.





Creating an Animation!

Let's see how we can use the "bitSet" function to create a shifting light pattern like the one below.





Crude Way

The following code snippet shows how to get the desired output, as I originally described at the start of this section. I'm adding this here as a reference for the following two sections so you can compare the three ways and see why this is not the best way to accomplish the task. Yes, it works, but it's not very readable.



Sneaky Way

Another way you could alternate the lights, like in the video above, is by passing in a binary literal to the "updateShiftRegister" function. What is a binary literal? A binary literal represents a numeric value in binary form using only 1s and 0s. To specify a binary literal, type "0b" followed by the bits which make up the numeric value.


In our case, we want to write the binary representation of the numbers 170 and 85. Their binary literal values would be as follows: 0b10101010 (170) and 0b01010101 (85). With this new information, we can change the above code to the following:

Using the sneaky way to program a pattern wouldn't bother me for one shift register. I know I could come back months later and understand what's happening. But if you've chained together two or more shift registers, I wouldn't recommend typing out the binary representation of a 16-bit number, let alone something larger. In those cases, I would always recommend using a for loop and the "bitSet()" function.



Correct Way

Let's see how we can use a for loop, Arduino's "bitSet()" function, and a modified version of the "updateShiftRegister" function to create the binary representations of the numbers 170 and 85.


Let's start with a modified version of the "updateShiftRegister" function. Add an additional parameter of type "uint8_t" called "bitOrder." Replace the "LSBFIRST" in the "bitSet" function with the new argument. If you don't know, the "uint8_t" means "unsigned int" with eight bits. You should have something similar to this:

We can now pass in either "LSBFIRST" or "MSBFIRST" from our main loop with the modified function. You'll see why this is important soon.


Now let's set every other bit in the "leds" variable to a 1 using the "bitSet" function. We'll do that in the "setup" function this time.

All I did was add a for loop to the setup function that makes the locations 0, 2, 4, and 6 in the "leds" variable a 1 using the "bitSet" function.


With the "leds" variable setup, let's change the code in the loop function to utilize the modified "updateShiftRegister" function.

Notice how the only difference between each call of the modified "updateShiftRegister" function is passing in a different order to write the bits. By creating the animation this way, we only have to set the bits once and then change the order in which we write those bits to the shift register. It also allows you to easily change the code if you add additional shift registers. Simply change the for loops end condition to 8 x the number of shift registers you have.



553 views0 comments
bottom of page