Arduino Platform - Working with Shift Registers

Note: I originally published this a long time ago on my old website (accidentally killed by my ISP) and also on CodeProject

Introduction

Following on from the previous Arduino articles I have written on CodeProject, in this article we shall take a quick look at shift registers. Why? Well, this article is a combination of two smaller articles I had previously written on my own website, and I thought it would be good to share these here, which would extend the articles reach further as well as further extend the Arduino collection of articles on CodeProject. The articles on my website are the 2 most popular of all the articles on there, and going by the analytics data from Google, these are direct search results on the subject.

In this article, we will start of with a single shift register, then increase to dual shift registers and then see what can be done to take things further.


What are Shift Registers, Why Do We Need Them?

Shift registers are an integrated circuit that can be used to change the source data from a serial stream to a parallel stream. They can be used to do other things as well, e.g. serial to serial, parallel to serial, etc. but this is dependant on which type of shift register you are using.
In this article, we are only going to focus on serial to parallel implementation. The image below shows the most basic representation of what is happening, the serial data is clocked into the chip, and the representative bits are placed on the outputs. The outputs are held at the appropriate state until they are changed.

On the Arduino platform, each of the different types of boards, e.g. Duemilanove/Uno or Mega, have differing numbers of inputs and outputs available to use as connections to the real world. By using a shift register, it is possible to extend the available outputs. In the following examples, you will see how first we will control 8 LEDs with just 3 outputs, and then how we will go onto control 16 LEDs with the same 3 outputs. You can of course, go and scale this up as far as your project requires.

Depending on the nature of your project, e.g., one that makes use of a large number of LEDs, you may wish to design your project from the start using shift registers, making it a lot easier to add more in the future, or to save the other IO pins for devices such as switches or motor controllers.


What Hardware is in Use with this Article?

Throughout this example, the hardware being used consists of:

  • Arduino Duemilanove Board
  • Bunch of typical 5V LEDs
  • Suitable resistors for current limiting
  • Shift Register type 74HC595N

As with any electronic project, it is important to take recognition of the limitations of any electronic device, and it is the responsibility of the individual building the circuit to check that they are building within the design limitations of the device(s). Slight variations between manufacturers and suppliers may result in damage to equipment, so always check what exactly you are using. At the end of the day, only you know what you are using and articles such as these are only a guide.

Starting Simple - Single Shift Register

Ok, let's start with a single shift register, and see what is required to get this going using an Arduino. The image below is the schematic used for the initial examples.



As you can see from the schematic, we are picking up +5V and a Ground connection at the Arduino board, Digital IO pins 2,3,4 are being used for the Data, Latch and the Clock. The Shift Register also requires a couple of links to pull the Reset High and the Enable Low to make these examples work. You could, of course, control these pins using the Arduino, but for these simple examples, there is no requirement to do this. A more complex project might want to take full control of the IC, but that would be up to the circuit developer.

For the coding, we will use the started Arduino IDE available for the Arduino website.

This project's code is no different from any other Arduino project and follows the same Initialisation, Setup and Loop Structure. There are no special libraries required to be included or initialised within the code, and standard methods are all that is required to make this work. The basic structure is as follows:
int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

//Add any other variables used by the main program loop here

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    //Main program loop as required to go here
}


Example 1 - Single Shift Register - Binary Counter

The first example will simply create a binary counter, and show this incrementing on the LEDs.
To do this, we must first add a variable to the initialisation section which will store the counter, and then add a standard for loop to do the incrementing in the main program loop method. As this is the first example, I will show the complete program, so you can see where the variables and the loop need to be added.

int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

int counter = 0;       //The counter for storing the byte value

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    for (counter = 0; counter < 256; counter++)
    {
        digitalWrite(latchPin, LOW);          //Pull latch LOW to start sending data
        shiftOut(dataPin, clockPin, MSBFIRST, counter);         //Send the data
        digitalWrite(latchPin, HIGH);         //Pull latch HIGH to stop sending data
        delay(500);
    }
}

As you can see in the main program loop, a simple for loop increments the counter byte. When we are ready to send the new byte value to the shift register, we must first pull the latchPin LOW. We then send out the data to the shift register (in the correct bit orientation). Once the data has been sent, we pull the latchPin HIGH again to signal that we are finished. The delay instruction simply allows us to control how quickly we write out the bytes to the register, in this case, we are waiting 500milliseconds between increments.


Example 2 - Single Shift Register - LED Sweep

No article involving LEDs and shift registers would be complete unless it includes the trusty Knight Rider KITT Light sweep!

To do this, first we need to set up a byte array to store each of the values in the sweep. If you think about what is happening, all we are doing is turning each bit on and off in sequence, so it would be 10000000, 01000000, 00100000, 00010000, etc. etc. These values are then in turn written out to the shift register.
int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

int seq[14] = {1,2,4,8,16,32,64,128,64,32,16,8,4,2};       //The byte sequence

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    for (int n = 0; n < 14; n++)
    {
        digitalWrite(latchPin, LOW);             //Pull latch LOW to start sending data
        shiftOut(dataPin, clockPin, MSBFIRST, seq[n]);          //Send the data
        digitalWrite(latchPin, HIGH);            //Pull latch HIGH to stop sending data
        delay(75);
    }
}

There you have it, every 75ms the next byte in the sequence is written to the output.

Moving On - Dual Shift Registers

One of the nice features about the Shift Registers is they have a pin called Overflow, and what this basically allows us to do is write more data to the first shift register than it can handle, and it will overflow this excess to this pin. So to extend the chain, we simply connect the first shift registers Overflow to the second shift registers Data pin. This way, if we write out 2 bytes to the first shift register, the first byte is passed onto the second shift register, and the second byte is retained within the first shift register. The schematic below shows how this cascade is connected up, I have also added 3 additional LEDs so we can monitor the DATA, CLOCK and OVERFLOW pins.




For the dual shift register setup, the code is almost identical, the only difference is that we add a second byte array variable and an additional line to write the second byte. To demonstrate this, we will extend the binary counter example from 8 to 16 Bit.

Example 3 - Dual Shift Register - 16Bit Binary Increment

int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

int byte1 = 0;         //The counter for storing the byte #1 value
int byte2 = 0;         //The counter for storing the byte #2 value

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    for (byte2 = 0; byte2 < 256; byte2++)               //Outer Loop
    {
        for (byte1 = 0; byte1 < 256; byte1++)            //Inner Loop
        {
            digitalWrite(latchPin, LOW);           //Pull latch LOW to start sending data
            shiftOut(dataPin, clockPin, MSBFIRST, byte1); //Send the data byte 1
            shiftOut(dataPin, clockPin, MSBFIRST, byte2); //Send the data byte 2
            digitalWrite(latchPin, HIGH);          //Pull latch HIGH to stop sending data
            delay(250);
        }
    }
}

Example 4 - Dual Shift Register - KITT is back!


Just like the TV program, KITT made a re-appearance, but the new version had a dual sweep light on the front of the car. To achieve this, we need two arrays to store the left and right (Byte 1 + 2) values, and again an additional line to write out the second byte.

int dataPin = 2;        //Define which pins will be used for the Shift Register control
int latchPin = 3;
int clockPin = 4;

int seq1[14] = {1,2,4,8,16,32,64,128,64,32,16,8,4,2};  //The array for storing the 
                        // byte #1 value
int seq2[14] = {128,64,32,16,8,4,2,1,2,4,8,16,32,64};  //The array for storing the 
                        // byte #2 value

void setup()
{
    pinMode(dataPin, OUTPUT);       //Configure each IO Pin
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
}

void loop()
{
    for (int x = 0; x < 14; x++)         //Array Index
    {
        digitalWrite(latchPin, LOW);            //Pull latch LOW to start sending data
        shiftOut(dataPin, clockPin, MSBFIRST, seq1[x]);         //Send the data byte 1
        shiftOut(dataPin, clockPin, MSBFIRST, seq2[x]);         //Send the data byte 2
        digitalWrite(latchPin, HIGH);           //Pull latch HIGH to stop sending data
        delay(75);
    }
}


Going Further - 3 Registers and Beyond

As you can probably work out, you can continue to add more registers to the chain, by simply linking the OVERFLOW to the DATA as required. You will then need to think about how you want to control and handle the bytes and then write these out in the correct sequence. Of course, you will need to optimise the code to fit within the limitations of the Arduino, but hey, maybe one day you could even build a (no, it is not mine) 8x8x8 Arduino LED Cube or something bigger! I might even get round to having a go at one of these one day...

References




Comments

Popular Posts