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 upto the circuit developer.
For the coding, we will use the started Arduino IDE available for the Arduino Homepage[^].
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; int latchPin = 3;
int clockPin = 4;
void setup()
{
pinMode(dataPin, OUTPUT); pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
}
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; int latchPin = 3;
int clockPin = 4;
int counter = 0;
void setup()
{
pinMode(dataPin, OUTPUT); pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
for (counter = 0; counter < 256; counter++)
{
digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, counter); digitalWrite(latchPin, HIGH); 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; int latchPin = 3;
int clockPin = 4;
int seq[14] = {1,2,4,8,16,32,64,128,64,32,16,8,4,2};
void setup()
{
pinMode(dataPin, OUTPUT); pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
for (int n = 0; n < 14; n++)
{
digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, seq[n]); digitalWrite(latchPin, HIGH); 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; int latchPin = 3;
int clockPin = 4;
int byte1 = 0; int byte2 = 0;
void setup()
{
pinMode(dataPin, OUTPUT); pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
for (byte2 = 0; byte2 < 256; byte2++) {
for (byte1 = 0; byte1 < 256; byte1++) {
digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, byte1); shiftOut(dataPin, clockPin, MSBFIRST, byte2); digitalWrite(latchPin, HIGH); 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; int latchPin = 3;
int clockPin = 4;
int seq1[14] = {1,2,4,8,16,32,64,128,64,32,16,8,4,2}; int seq2[14] = {128,64,32,16,8,4,2,1,2,4,8,16,32,64};
void setup()
{
pinMode(dataPin, OUTPUT); pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
}
void loop()
{
for (int x = 0; x < 14; x++) {
digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, seq1[x]); shiftOut(dataPin, clockPin, MSBFIRST, seq2[x]); digitalWrite(latchPin, HIGH); 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...
What Else
Visit my other articles or my website for more Arduino bits and bobs. Search CodeProject for Arduino, there are also more by others. Oh, and why not search Youtube for Arduino, there are heaps of projects you can have a go at. Keeps you occupied for a while anyway!
References
History
- 10th January, 2011 - First version of article