Shift Register

Whilst you have quite a few outputs to play with on an Arduino board; sometimes there won’t be enough.  One way to get around this is with the use of a shift register.  This is a brilliant way to output eight pin’s worth of outputs – using just three pins.  What’s more – it’s a fascinating insight into some elementary computer science…

I’m using the 74HC595 – purchased from the good folks at Oomlout. Here’s the data-sheet for the chip (also courtesy of Oomlout).  There’s also a really nice article on the Arduino website.

First of all we need to build a circuit.  Although this one is a bit fiddly – there’s nothing too complex here.

We connect pin 15 to +5V, pin 8 to ground.  Pin 15 & pins 1-7 are our output pins – so we connect them to whatever we want to switch on and off (I’m using an LED + a resistor – but you could easily also wire a transistor into each output to drive anything you’d like).  Pin 14 is our data input pin – I’ll connect that to arduino’s output pin 2.  Pins 11 & 12 are the “clock” & “latch” pins – and we’ll connect these to pins 4 & 3 of arduino.  We won’t be using pins 10 or 13 – but we do need to set them to the correct logic state: so connect 10 to +5V & 13 to ground.

Here’s a schematic: And here’s a drawing of the circuit in breadboard form (both produced using Fritzing): Note the resistors on the cathode side of each LED.  I’m using 560Ω – as I had a whole box of them around – but anything between 220Ω and 1KΩ ought to be fine (I seem to recall 470Ω being the “correct” value for a 5V circuit and normal LEDs – but as I say, as long as the resistors are there, don’t worry too much).

Okay.  So now onto the fun part.

If you want to try something quickly to check it’s all working, then as this circuit is pin-compatible with Oomlout’s ARDX circuit 5, you can try their code.

If all is well you’ll see the LEDs counting up in binary.

That’s all well & good – but how does it work?  Well the shift register library in Arduino is taking an 8-bit number (a byte – 0-255) – and then setting the output pins of the chip according to the bit values of the eight bits.

Whilst using a byte like this, is an example of a very elegant piece of computer science – bitwise operations can be quite hard to understand.  So let’s simplify things – by using a more familiar “container” for our eight bits: an array of booleans.

Now, this isn’t such an efficient way to work (most, if not all, microprocessors do their most efficient work when using bitwise operations) – but it is a bit easier to understand.

// Shift Register with array of booleans.
// Code by AJP (cc 2010) – released under
// Pin Definitions

int data = 2;
int clock = 3;
int latch = 4;

#define bit_count 8

void setup()
{
// Set the three pins to output…
pinMode(data, OUTPUT);
pinMode(clock, OUTPUT);
pinMode(latch, OUTPUT);
}

void loop()
{
boolean bits[bit_count];
// Set each of the "bits" to 0…
for(int i=0;i<bit_count;i++)
bits[i]=false;

// Now we'll pick one, at random, and set it to a 1
int c=random(8);
bits[c]=true;

// Now we're ready to send the data to the shift
// register. We take the "latch" pin
// low – to get it ready to recieve…
digitalWrite(latch, LOW);

// Now we'll simply itterate through the "bits"
// and set the data pin either high or low
for(int i=0;i<8;i++)
{
if (bits[i])
{
digitalWrite(data, HIGH);
}
else
{
digitalWrite(data, LOW);
}

// We've sent our bit; now to tell the chip we're
// ready to go to the next bit – by pulsing the
// clock pin for 1 ms…
digitalWrite(clock, HIGH);
delay(1);
digitalWrite(clock, LOW);
}

// Finally we've updated all 8 bits – so we tell
// the chip we've done by setting the latch back
// to high
digitalWrite(latch, HIGH);

// To finish-up we'll delay for 125ms
// before going around again…
delay(125);
} This code makes it much easier to see what’s going on.  Once we’re ready to update the status, we set the latch: and then alternately setting the data pin (with the value of the bit) – and then pulse the clock HIGH for 1ms.

Now that we’ve seen how it works – let’s go back to the library code function:

shiftOut(dataPin, clockPin, MSBFIRST, dataValue);

Note that MSBFIRST is a constant denoting that we’re sending the most-significant bit first (aka big-endian) – we could also use LSBFIRST to send the least-significant bit first (little-endian). For more on the shiftOut function, see here [arduino.cc]; and for more information on big-endian vs. little-endian byte orders, have a look at this article on wikipedia.

Now that we have a feel for manipulating bits (by way of the analogous process of manipulating the array): how do we do it for real?

How do we, for example, tell the shiftOut function to turn on pins 4 & 6?

The best way is to return to those bitwise operators I mentioned earlier.

If we want to turn all eight pins on – then we need to send a bit pattern consisting of eight 1’s … 111111111 – which in decimal is 255 (i.e. 1+2+4+8+16+32+64+128).

For each pin we don’t want to turn on – we simply reduce this value by the corresponding binary value.  So if we only wanted to turn on all of the pins, except pins 0 & 7 – then we’d send 255-128-1 = 126.

Subtraction isn’t very efficient, or very elegant though: so instead – let’s use a bitwise OR…

The OR operator (which in C, uses this symbol: | ) works by comparing two bit patterns – and for each bit sets the output to 1 – if either one of the bit’s it’s looking at is 1 – or 0 if they’re both 0.

Using this, we can mask the bits we don’t want to change: if we OR  any pattern against 00001000 (for example), then the outcome will be to leave seven of the bits unchanged – and to set the 3rd bit as 1 (if it wasn’t already).

The last thing we need to do is work out which bit pattern corresponds to the bit we want to turn on.  The easy way is to remember some elementary mathematics.

To set bit 0 – we want 00000001 = 1, bit 1 is 00000010 = 2, 2 is 00000100 = 4, 3 is 00001000 = 8, and so on…  Using this we can see that 2^n will give us the value we want (2^0=1, 2^1=2, 2^2=4 … etc).

Given that we’re dealing with powers of two – there’s on last trick we can use, to avoid having to multiply: bit shifting…

Bit shifting does exactly what it sounds like – if moves (shifts) the bits either up or down…  So if we shift 00000001 two places to the left – we get 00000100.  So if we shift 1 by 0 places we 1, if we shift 1 by 1 we get 2, and so on: exactly what we want: and (critically) bit shifting is another very efficient operation.  In C the operator to shift to the left is <<.

Putting all of that together we get the following code:

state |= 1 << bitNum; shiftOut (data, clock, MSBFIRST, state);

That works for turning bits on – what about turning them off?

We can use a very similar trick: except that this time we need to use an AND operator (& in C), and the complement of the value we used last time.

AND works by setting the output to a 1 – if (and only if) both bits of the two bit patterns it’s comparing are a 1 – and 0 otherwise.

If all eight bits are turned on (11111111) and we want to turn 0th and 1st bits off – then we AND 11111111 with 11111100.  Again this works by masking: the bits that are set to 1 in the mask will be unaffected (if the corresponding bits in the original bit pattern were 1’s then the result will be a 1 – and it will be a 0, if they were a 0…).

So to turn off bit 4 – we need 11110111: the complement of 00001000 (8 in decimal).  To get the complement we use the complement operator (~ in C).

state &= ~ (1 << bitNum); shiftOut (data, clock, MSBFIRST, state);

Pulling all of this together, we get the following code:

// Shift Register with bitwise operators
// Code by AJP (cc 2010) – released under

//Pin Definitions
int data = 2;
int clock = 3;
int latch = 4;

void setup()
{
pinMode(data, OUTPUT);
pinMode(clock, OUTPUT);
pinMode(latch, OUTPUT);
}

void loop()
{
int state=0;
for (int i=0;i<8;i++)
{
state=setBit (state, i, true);
delay(125);
}
}

int setBit(int state, int bitNum, bool value)
{
digitalWrite(latch, LOW);
if (value)
state |= 1 << bitNum;
else
state &= ~ (1 << bitNum);
shiftOut (data, clock, MSBFIRST, state);
digitalWrite(latch, HIGH);
return state;
} 