74HC595 Shift Register with Arduino – Complete Guide

Arduino boards come with a decent number of digital I/O pins, but sometimes that’s just not enough. For example, controlling a 7-segment display takes 8 I/O pins for just one digit. Similarly, an 8×8 LED matrix needs 16 pins, which quickly exhausts the available GPIOs on most Arduino boards.

That’s where the 74HC595 shift register comes in handy. With just three GPIO pins and this simple IC, you can increase the number of output pins on your Arduino boards. A single 74HC595 IC can control 8 digital outputs, and you can even increase the number of I/O by cascading multiple shift registers.

In this tutorial, you’ll learn how to use the 74HC595 shift register with Arduino and cascade multiple shift registers to drive a large number of LEDs.

What is a Shift Register?

A shift register is a digital circuit designed to store and shift binary data in a specific direction, typically one bit at a time. The name comes from its core function—shifting data through a series of flip-flops.

Ads

A typical n-bit shift register is made up of n flip-flops (usually D-type), connected in a cascade configuration. Each flip-flop stores a single bit, and data moves from one flip-flop to the next on the rising or falling edge of a shared clock signal. This synchronized data flow makes shift registers ideal for a wide range of embedded applications.

Common Uses of Shift Registers

  • Temporary Data Storage in Digital Systems
  • Data Transfer
  • Serial-to-Parallel Data Conversion
  • Parallel-to-Serial Data Conversion

Types of Shift Registers:

Shift registers are generally classified into four main types based on their input/output configuration:

  1. Serial In – Serial Out (SISO)
  2. Serial In – Parallel Out (SIPO)
  3. Parallel In – Serial Out (PISO)
  4. Parallel In – Parallel Out (PIPO)

74HC595 Shift Register

The 74HC595 is a Serial-In Parallel-Out (SIPO) shift register. It allows you to control 8 output pins using just 3 pins from an Arduino. The 74HC595 IC contains two 8-bit registers inside:

  • The Shift Register – Accepts data serially, one bit at a time.
  • The Storage Register (Latch) – Holds the data and sends it to the output pins in parallel.

When you send data to the SER (Serial Data Input) pin and give a clock pulse to the SRCLK (Shift Register Clock) pin, each rising edge of the clock moves the existing bits one step forward inside the shift register, and a new bit enters from the SER pin.

A latch is placed between the shift register and the output pins to temporarily hold the data bits, so that the output doesn’t change with every incoming bit.

Once all 8 bits are loaded into the shift register, a pulse is required at the RCLK (Register Clock or Latch Pin) to update the latches, and the 8-bit data becomes available at the output pins (QA to QH) all at once.

Ads

The pin diagram for the 74HC595 Shift Register and all the functions of the pins are given below.

74HC595 Pinout

The 74HC595 shift register is available in a 16-pin DIP or SOIC package. In this tutorial, we’ll use the DIP package. The pinout for the DIP 74HC595 shift register is shown below.

74HC595 Pinout

A brief description of all the pins is given below.

NamePinDescription
VCC16Power supply pin. Connect to +5V.
GND8Ground pin. Connect to GND.
SRCLR10Shift register clear (active LOW). Keep HIGH to avoid clearing.
SRCLK11Shift register clock. Shifts data on the rising edge.
RCLK12Latch (register) clock. Moves data to the output on the rising edge.
OE13Output enable (active LOW). Tie to GND to enable outputs.
SER14Serial data input. Reads 1 bit per clock cycle.
QA–QH15, 1–78-bit parallel data outputs (QA = Pin 15, QB = Pin 1, …, QH = Pin 7).
QH’9Serial out. Connect to the next register’s SER for chaining.

You can see that there are two power supply pins – VCC and GND. These pins are used to provide a 5V power supply to the IC.

Then, there are four control pins – SRCLK, RCLK, SRCLR, and OE.

SRCLK is the shift register clock pin. Data shifts into the register on the rising edge of this clock pulse. RCLK is the latch (register) clock pin. Data moves from the shift register to the output latch on the rising edge of this clock.

SRCLR is the shift register clear pin, used to reset the shift register. This is an active-low pin. In most cases, we don’t need to clear the shift register manually. So you can keep it always high. OE is the output enable pin, which controls whether the latched data appears on the output pins. This is also an active-low pin – connecting it to ground enables the outputs (QA to QH).

The remaining 10 pins are data pins – SER, QA-QH (8-bit parallel output) and QH’. SER is the serial data input pin, which takes the serial data from the microcontroller. QA-QH are the 8-bit parallel output pins. QH’ is the serial data out pin, which is used to cascade multiple shift registers together.

Wiring the 74HC595 with Arduino

Connecting the shift register to the Arduino is very simple. First, place the shift register on a breadboard, and connect the VCC, GND, input, and control pins to the Arduino. Then, connect the output devices to the shift register.

So, let’s start by placing the shift register on the breadboard.

Place the shift register on the breadboard as shown in the image below. Make sure that each side of the IC is seated across the center gap, so the pins are on opposite rows of the breadboard.

First, connect the VCC to the Arduino 5V output and the GND to the Arduino ground pin.

Then connect the input data pin and the two clock pins to the Arduino. Connect the SER to pin 8, RCLK to pin 9, and SRCLK to pin 10.

Ads

There are two other control pins that we won’t use in this example — SRCLR and OE — but they must not be left floating. Connect SRCLR (pin 10) to the Arduino 5V pin and OE to the GND.

Leave the QH’ pin open for now; we will use this pin later in this tutorial.

The remaining eight pins (QA to QH – pins 15, 1–7) are the 8-bit output pins. In this example, we’ll use the shift register to drive eight LEDs, so connect eight LEDs to these output pins.

Connect the positive leg (long leg) of each LED to one of the output pins. Connect the negative leg (short leg) of all LEDs to a common ground line on the breadboard, which must be connected to the Arduino’s GND pin as well.

To protect the LEDs, add a 200–300Ω resistor in series with each one.

A summary of the PIN connection is given below.

Shift Register PinsConnected to
VCC (16)Arduino 5V
GND (8)Arduino Ground
SER (14)Arduino Digital Pin 8
RCLK (12)Arduino Digital Pin 9
SRCLK (11)Arduino Digital Pin 10
8 Data Pins8 LEDs (+ve end)
OE (Pin 13)Ground
SRCLR (10)5V
QHNo Connection
74HC595 Arduino Circuit Diagram

Arduino Code for 74HC595

Generating an input signal for the shift register is a bit complex because it requires three Arduino pins to work in sync.

One pin (SER) sends the input data bits serially. Another pin is used to generate the clock pulses (SRCLK) for shifting data into the register. A third pin provides the latch clock (RCLK) to transfer the data from the shift register to the output latches. All three signals—data, shift clock, and latch clock—must be carefully synchronized to ensure correct data transfer and output.

To simplify this process, Arduino provides a built-in function called shiftOut(). This function handles the timing and bit shifting internally, making it much easier to send serial data to devices like the 74HC595.

First, we’ll look at how the shiftOut() function works, and then we’ll write some Arduino code to send data to the 74HC595.

The syntax for the shiftOut() function is:

shiftOut(dataPin, clockPin, bitOrder, data)

You can see that this function requires four arguments. The first two arguments are the data and clock pins.

Then comes the bit order, which defines the way bits are shifted out. The bit order can be MSBFIRST or LSBFIRST (Most Significant Bit First or Least Significant Bit First).

Ads

The last argument is the data to be shifted out, which should be of type byte.

Arduino Code – Send 8-bit Data to the Shift Register

The code below will send a single byte (10101010) to the shift register. You can see the output in the LED array.

int dataPin = 8;  // SER, serial data input
int latchPin = 9;  // RCLK, storage register clock input pin
int clockPin = 10; // SRCLK, shift register clock input

byte leds = 0b10101010;

void setup() {
  // Set the pins as outputs
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
}

void loop() {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, leds);
  digitalWrite(latchPin, HIGH);
}

You can see that we create three variables – latch pin, clock pin, and data pin to hold the Arduino PIN that is connected to the corresponding shift register pins. We also create a variable of type byte to hold the byte data to be sent to the shift register.

Then, in the setup section, we make all three pins output pins.

In the loop section, we pull the latch pin LOW, send the data using the shiftOut() function, and then set the latch pin HIGH again to write the data to the latch. Since this is a one-time task, you can move it to the setup section of the code.

Arduino Code – Send Multiple Bytes to the Shift Register

The above example displays only a single LED pattern. To create an LED effect, we need to send multiple bytes, one byte per frame.

The code below will send a byte array to the shift register, which creates an LED pattern on the output pins.

int dataPin = 8;  // SER, serial data input
int latchPin = 9;  // RCLK, storage register clock input pin
int clockPin = 10; // SRCLK, shift register clock input

byte ledPattern[9] = {
  0b00000000,
  0b10000000,
  0b11000000,
  0b11100000,
  0b11110000,
  0b11111000,
  0b11111100,
  0b11111110,
  0b11111111
};

void setup() {
  // Set the pins as outputs
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);
}

void loop() {
  for (int i = 0; i < 9; i++) {
    displayPattern(i);
    delay(100);
  }
}

void displayPattern(int pattern) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, ledPattern[pattern]);
  digitalWrite(latchPin, HIGH);
}

The code is very similar to the previous one.

There are two main things to look at here: we create a data array (ledPattern), an 8-bit data set to generate different patterns on LEDs.

We also create a custom function (displayPattern) to send 8 bits of data to the shift register. It will avoid complexity in the loop section.

In the loop section, we use our custom function, displayPattern, to send the LED sequence data to the shift registers, 8 bits at a time. We use a for loop that helps to send 9 datasets one by one.

Arduino Code – Multiple LED Effects with a Shift Register & 8 LEDs

int dataPin = 8;    // SER, serial data input
int latchPin = 9;   // RCLK, storage register clock input pin
int clockPin = 10;  // SRCLK, shift register clock input

int patternDelay = 1000;
const int repeatCounter1 = 6;  // Counter for single loop effects
const int repeatCounter2 = 4;  // Counter for double loop effects

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

// Write a byte to 74HC595
void writeToShiftRegister(byte data) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, data);
  digitalWrite(latchPin, HIGH);
}

void loop() {
  singleChase();            // Single LED moving left to right
  bounce();                 // Single led bouncing from one side to another
  fillAndClear();           // Fill from one side and clear from opposite
  reverseFillAndClear();    // Fill from one side and clear from same side
  fillAndClearAlternate();  // Fill and Bounce back
  alternateBlink();         // Blink LEDs alternatively
  expandFromCenter();       // Fill all the LEDs from center
  contractToCenter();       // Fill all the LEDs from side
  expandAndContract();      // Fill from center, then clear from side
  meetInMiddle();           // Single LED move from both side to center
  splitFromMiddle();        // Single LED move from center to side
  splitAndMeet();           // Single LED repeat both
}

// Single LED Moving Left to Right
void singleChase() {
  int delayTime = 50;
  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 8; i++) {
      writeToShiftRegister(1 << i);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Single LED Bouncing From One Side to Another
void bounce() {
  int delayTime = 50;
  for (int r = 0; r < repeatCounter2; r++) {
    for (int i = 0; i < 8; i++) {
      writeToShiftRegister(1 << i);
      delay(delayTime);
    }
    for (int i = 6; i > 0; i--) {
      writeToShiftRegister(1 << i);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill from one side and clear from opposite
void fillAndClear() {
  int delayTime = 50;
  uint8_t data = 0;
  for (int r = 0; r < repeatCounter2; r++) {
    for (int i = 0; i < 8; i++) {
      data |= (1 << i);
      writeToShiftRegister(data);
      delay(delayTime);
    }
    for (int i = 7; i >= 0; i--) {
      data &= ~(1 << i);
      writeToShiftRegister(data);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill from one side and clear from same side
void reverseFillAndClear() {
  int delayTime = 50;
  uint8_t data = 0;

  for (int r = 0; r < repeatCounter2; r++) {
    // Fill from left to right (bit 0 to 8)
    for (int i = 0; i < 8; i++) {
      data |= (uint8_t)1 << i;
      writeToShiftRegister(data);
      delay(delayTime);
    }

    // Clear from left to right (bit 0 to 8)
    for (int i = 0; i < 8; i++) {
      data &= ~((uint8_t)1 << i);
      writeToShiftRegister(data);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill and Bounce back
void fillAndClearAlternate() {
  int delayTime = 50;
  uint8_t data;

  for (int r = 0; r < repeatCounter2; r++) {
    // Fill from left to right, clear from left to right
    data = 0;
    for (int i = 0; i < 8; i++) {
      data |= (uint8_t)1 << i;
      writeToShiftRegister(data);
      delay(delayTime);
    }
    for (int i = 0; i < 8; i++) {
      data &= ~((uint8_t)1 << i);
      writeToShiftRegister(data);
      delay(delayTime);
    }

    // Fill from right to left, clear from right to left
    data = 0;
    for (int i = 7; i >= 0; i--) {
      data |= (uint8_t)1 << i;
      writeToShiftRegister(data);
      delay(delayTime);
    }
    for (int i = 7; i >= 0; i--) {
      data &= ~((uint8_t)1 << i);
      writeToShiftRegister(data);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Blink LEDs alternatively
void alternateBlink() {
  int delayTime = 200;
  for (int r = 0; r < 6; r++) {
    writeToShiftRegister(0b10101010);
    delay(delayTime);
    writeToShiftRegister(0b01010101);
    delay(delayTime);
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill all the LEDs from center
void expandFromCenter() {
  const int delayTime = 150;
  const byte pattern[] = {
    0b00000000,
    0b00011000,
    0b00111100,
    0b01111110,
    0b11111111
  };

  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 5; i++) {
      writeToShiftRegister(pattern[i]);
      delay(150);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill all the LEDs from side
void contractToCenter() {
  const int delayTime = 150;

  const uint16_t pattern[] = {
    0b11111111,
    0b01111110,
    0b00111100,
    0b00011000,
    0b00000000
  };

  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 5; i++) {
      writeToShiftRegister(pattern[i]);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill from center, then clear from side
void expandAndContract() {
  const int delayTime = 50;

  // Expansion + full ON frame
  const uint8_t pattern[] = {
    0b00000000,
    0b00011000,
    0b00111100,
    0b01111110,
    0b11111111
  };
  for (int r = 0; r < repeatCounter2; r++) {
    for (int i = 0; i < 5; i++) {
      writeToShiftRegister(pattern[i]);
      delay(delayTime);
    }
    for (int i = 3; i >= 0; i--) {
      writeToShiftRegister(pattern[i]);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Single LED move from both side to center
void meetInMiddle() {
  int delayTime = 120;

  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 4; i++) {
      byte left = (1 << i);
      byte right = (1 << (7 - i));
      writeToShiftRegister(left | right);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Single LED move from center to side
void splitFromMiddle() {
  int delayTime = 120;
  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 4; i++) {
      byte left = (1 << (3 - i));
      byte right = (1 << (4 + i));
      writeToShiftRegister(left | right);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Single LED repeat both
void splitAndMeet() {
  int delayTime = 50;

  for (int r = 0; r < repeatCounter2; r++) {
    // From edges to middle
    for (int i = 0; i < 4; i++) {
      uint8_t pattern = (1 << i) | (1 << (7 - i));
      writeToShiftRegister(pattern);
      delay(delayTime);
    }
    // From middle to edges
    for (int i = 3; i >= 0; i--) {
      uint8_t pattern = (1 << i) | (1 << (7 - i));
      writeToShiftRegister(pattern);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

Cascading Multiple Shift Registers

Cascading 74HC595 Shift Registers with Arduino

One of the most useful things is that you can connect multiple shift registers serially, and still control them using the same number of Arduino pins. That means you can control 8,16, 24, and more I/O pins using only three Arduino pins.

Let’s see how to control 16 LEDs using 2 Shift registers.

Circuit Diagram

For this circuit, use two breadboards side by side to keep the wiring neat and clean.

Place two shift registers on one breadboard, and keep their data pins on one side. Use the second breadboard to place the 16 LEDs. Connect the ground legs of all LEDs to the ground line, and the positive legs to the 16 output pins of the two shift registers through individual 220 Ω resistors.

Connect the breadboard power lines together (+ve to +ve, -ve to -ve).

Next, connect the VCC pins of both shift registers to the 5V line and the GND pins to the ground line. Connect the OE (Output Enable) pins of both ICs to ground, and connect SRCLR (Shift Register Clear) to the 5V line.

Use a common line from Arduino digital pin 9 to drive both RCLK pins, and another common line from digital pin 10 for both SRCLK pins.

Then, connect the SER pin of IC1 to Arduino digital pin 8, and connect IC1’s QH’ pin to the SER pin of the second IC.

The complete circuit diagram is shown below.

Arduino with two 74HC595 Circuit Diagram

Refer to the pin connection table below for a quick and easy setup.

IC1 PinsConnected toIC2 PinsConnected to
VCC (16)Arduino 5VVCC (16)Arduino 5V
GND (8)Arduino GroundGND (8)Arduino Ground
SER (14)Arduino Digital Pin 8SER (14)IC1 QH‘ Pin (9)
RCLK (12)Arduino Digital Pin 9RCLK (12)Arduino Digital Pin 9
SRCLK (11)Arduino Digital Pin 10SRCLK (11)Arduino Digital Pin 10
8 Data PinsLED 1 to 88 Data PinsLED 9 to 16
OE (Pin 13)GroundOE (Pin 13)Ground
SRCLR (10)5VSRCLR (10)5V
QH‘ (9)IC2 SER Pin (14)QH‘ (9)No Connection

Note that IC1 refers to the first shift register, which is connected directly to the Arduino data pin, and IC2 refers to the second shift register, which is daisy-chained to the first shift register.

Your circuit is complete. Now test the setup using the code below.

Arduino Code – Cascading Two Shift Registers

The code below helps to test the circuit and the orientation of the LEDs.

int dataPin = 8;   // SER
int latchPin = 9;  // RCLK
int clockPin = 10; // SRCLK

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

// Send 16-bit data
void writeToShiftRegister(uint16_t data) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, (data >> 8) & 0xFF); // High byte → IC2
  shiftOut(dataPin, clockPin, MSBFIRST, data & 0xFF);            // Low byte  → IC1
  digitalWrite(latchPin, HIGH);
}

void loop() {
  for (int i = 0; i < 16; i++) {
    writeToShiftRegister((uint16_t)1 << i);
    delay(200);
  }
}

Upload the code to your Arduino board. If everything is correct, one LED will light up and move from one side to another.

Explaining the code

The Arduino shiftOut() function operates in 8-bit mode, so for 16-bit data, we need to break it into two parts and then send them using the shiftOut() function. We will do it using a custom function – writeToShiftRegister().

// Send 16-bit data
void writeToShiftRegister(uint16_t data) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, (data >> 8) & 0xFF); // High byte → IC2
  shiftOut(dataPin, clockPin, MSBFIRST, data & 0xFF);            // Low byte  → IC1
  digitalWrite(latchPin, HIGH);
}

To explain the working of this function, let’s take a 16-bit data as an example: 10101011 11001101. Here, the first byte (high byte) is 10101011 and will go to IC2, the Second byte (low byte) is 11001101 and will go to IC1.

This is done by calling the shiftOut() function twice.

shiftOut(dataPin, clockPin, MSBFIRST, (data >> 8) & 0xFF);
shiftOut(dataPin, clockPin, MSBFIRST, data & 0xFF);

We will understand these bitwise operations with the help of a step-by-step operation table.

StepOperationBinary ResultDescription
0data10101011 11001101
1data >> 800000000 10101011Shift right by 8 to get the High Byte
2(data >> 8) & 1111111110101011Mask with 0xFF to keep 8 bits
→ sent to shiftOut()10101011Goes to IC2 (2nd shift reg)
3data & 1111111111001101Mask to get Low Byte directly
→ sent to shiftOut()11001101Goes to IC1 (1st shift reg)

Using the same logic, you can send data to 3, 4, or more shift registers.

Arduino Code – LED Effects with Two Shift Registers & 16 LEDs

Now, you have understood how to work with multiple shift registers. Now upload the code below to your Arduino board, and enjoy some cool LED effects.

int dataPin = 8;    // SER
int latchPin = 9;   // RCLK
int clockPin = 10;  // SRCLK

int patternDelay = 1000;
const int repeatCounter1 = 4;  // Counter for single loop effects
const int repeatCounter2 = 2;  // Counter for double loop effects


void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

// Send 16-bit data
void writeToShiftRegister(uint16_t data) {
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, (data >> 8) & 0xFF);  // High byte → IC2
  shiftOut(dataPin, clockPin, MSBFIRST, data & 0xFF);         // Low byte  → IC1
  digitalWrite(latchPin, HIGH);
}

void loop() {
  singleChase();            // Single LED moving left to right
  bounce();                 // Single LED bouncing from one side to another
  fillAndClear();           // Fill from one side and clear from opposite
  reverseFillAndClear();    // Fill from one side and clear from same side
  fillAndClearAlternate();  // Fill and Bounce back
  alternateBlink();         // Blink LEDs alternatively
  expandFromCenter();       // Fill all the LEDs from center
  contractToCenter();       // Fill all the LEDs from side
  expandAndContract();      // Fill from center, then clear from side
  meetInMiddle();           // Single LED move from both side to center
  splitFromMiddle();        // Single LED move from center to side
  splitAndMeet();           // Single LED repeat both
  randomSparkle();          // Single LED Blink Randomly
}

// Single LED moving left to right
void singleChase() {
  int delayTime = 30;
  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 16; i++) {
      writeToShiftRegister((uint16_t)1 << i);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Single LED bouncing from one side to another
void bounce() {
  int delayTime = 30;
  for (int r = 0; r < repeatCounter2; r++) {
    for (int i = 0; i < 16; i++) {
      writeToShiftRegister((uint16_t)1 << i);
      delay(delayTime);
    }
    for (int i = 14; i >= 0; i--) {
      writeToShiftRegister((uint16_t)1 << i);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill from one side and clear from opposite
void fillAndClear() {
  int delayTime = 30;
  uint16_t data = 0;
  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 16; i++) {
      data |= (uint16_t)1 << i;
      writeToShiftRegister(data);
      delay(delayTime);
    }
    for (int i = 15; i >= 0; i--) {
      data &= ~((uint16_t)1 << i);
      writeToShiftRegister(data);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill from one side and clear from same side
void reverseFillAndClear() {
  int delayTime = 30;
  uint16_t data = 0;

  for (int r = 0; r < repeatCounter1; r++) {
    // Fill from left to right (bit 0 to 15)
    for (int i = 0; i < 16; i++) {
      data |= (uint16_t)1 << i;
      writeToShiftRegister(data);
      delay(delayTime);
    }

    // Clear from left to right (bit 0 to 15)
    for (int i = 0; i < 16; i++) {
      data &= ~((uint16_t)1 << i);
      writeToShiftRegister(data);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill and Bounce back
void fillAndClearAlternate() {
  int delayTime = 20;
  uint16_t data;

  for (int r = 0; r < repeatCounter2; r++) {
    // Fill from left to right, clear from left to right
    data = 0;
    for (int i = 0; i < 16; i++) {
      data |= (uint16_t)1 << i;
      writeToShiftRegister(data);
      delay(delayTime);
    }
    for (int i = 0; i < 16; i++) {
      data &= ~((uint16_t)1 << i);
      writeToShiftRegister(data);
      delay(delayTime);
    }

    // Fill from right to left, clear from right to left
    data = 0;
    for (int i = 15; i >= 0; i--) {
      data |= (uint16_t)1 << i;
      writeToShiftRegister(data);
      delay(delayTime);
    }
    for (int i = 15; i >= 0; i--) {
      data &= ~((uint16_t)1 << i);
      writeToShiftRegister(data);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Blink LEDs alternatively
void alternateBlink() {
  int delayTime = 150;
  for (int r = 0; r < 6; r++) {
    writeToShiftRegister(0b1010101010101010);
    delay(delayTime);
    writeToShiftRegister(0b0101010101010101);
    delay(delayTime);
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill all the LEDs from center
void expandFromCenter() {
  const int delayTime = 60;

  // Expansion + full ON frame
  const uint16_t pattern[] = {
    0b0000000000000000,
    0b0000000110000000,
    0b0000001111000000,
    0b0000011111100000,
    0b0000111111110000,
    0b0001111111111000,
    0b0011111111111100,
    0b0111111111111110,
    0b1111111111111111
  };

  for (int r = 0; r < repeatCounter1; r++) {
    // Expand
    for (int i = 0; i < 9; i++) {
      writeToShiftRegister(pattern[i]);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill all the LEDs from side
void contractToCenter() {
  const int delayTime = 60;

  const uint16_t pattern[] = {
    0b1111111111111111,
    0b0111111111111110,
    0b0011111111111100,
    0b0001111111111000,
    0b0000111111110000,
    0b0000011111100000,
    0b0000001111000000,
    0b0000000110000000,
    0b0000000000000000
  };
  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 9; i++) {
      writeToShiftRegister(pattern[i]);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Fill from center, then clear from side
void expandAndContract() {
  const int delayTime = 20;

  // Expansion + full ON frame
  const uint16_t pattern[] = {
    0b0000000000000000,
    0b0000000110000000,
    0b0000001111000000,
    0b0000011111100000,
    0b0000111111110000,
    0b0001111111111000,
    0b0011111111111100,
    0b0111111111111110,
    0b1111111111111111
  };

  for (int r = 0; r < repeatCounter1; r++) {
    // Expand
    for (int i = 0; i < 9; i++) {
      writeToShiftRegister(pattern[i]);
      delay(delayTime);
    }
    // Contract (excluding the fully ON state again)
    for (int i = 7; i >= 0; i--) {
      writeToShiftRegister(pattern[i]);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Single LED move from both side to center
void meetInMiddle() {
  int delayTime = 60;

  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 8; i++) {
      uint16_t left = (uint16_t)1 << i;
      uint16_t right = (uint16_t)1 << (15 - i);
      writeToShiftRegister(left | right);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Single LED move from center to side
void splitFromMiddle() {
  int delayTime = 60;

  for (int r = 0; r < repeatCounter1; r++) {
    for (int i = 0; i < 8; i++) {
      uint16_t left = (uint16_t)1 << (7 - i);
      uint16_t right = (uint16_t)1 << (8 + i);
      writeToShiftRegister(left | right);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Single LED repeat both
void splitAndMeet() {
  int delayTime = 20;

  for (int r = 0; r < 6; r++) {
    // From edges to middle
    for (int i = 0; i < 8; i++) {
      uint16_t pattern = (1 << i) | (1 << (15 - i));
      writeToShiftRegister(pattern);
      delay(delayTime);
    }

    // From middle to edges
    for (int i = 7; i >= 0; i--) {
      uint16_t pattern = (1 << i) | (1 << (15 - i));
      writeToShiftRegister(pattern);
      delay(delayTime);
    }
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

// Random sparkle
void randomSparkle() {
  for (int r = 0; r < 20; r++) {
    uint16_t randomBit = (uint16_t)1 << random(0, 16);
    writeToShiftRegister(randomBit);
    delay(100);
  }
  writeToShiftRegister(0);
  delay(patternDelay);
}

Conclusion

In this tutorial, you have learned how to use a shift register with Arduino. You have also learned how to cascade multiple shift registers together.

In the next tutorial, I will show you how to use 6 shift registers to control 16 RGB LEDs.

Stay tuned for more interesting projects and tutorials.

Help me to Build more Projects for You!

At CircuitGeeks, we're passionate about creating exciting electronics projects and sharing our knowledge with the world. Our projects are free and open to everyone, but we would need your support to keep the creativity flowing!

If you enjoy our work and find our projects valuable, please consider supporting us on Buymeacoffee. By buying us a coffee, you help us buy more components and keep our projects going strong.

We truly appreciate your contribution!

Leave a Comment