Friday, May 02, 2014

A little bit shifty

Experimenting with Shift Registers and Arduino

Shift registers are probably the least understood class of devices used in microcontroller and hardware design, yet they are central to so many common interfacing techniques, so I thought I would spent a few evenings getting to know how to use them.

There are some very good online tutorials regarding the use of shift registers, so I will not try to improve upon the wealth of good material that is already available, instead I will attempt to document some of my recent findings, in the hope that someone finds it useful. Everyone can type Arduino and shift register into Google and come up with a whole bunch of useful information.

My experiments have been done with an Arduino, or rather a low cost clone that uses the ATmega328P microcontroller, but could equally be applied to any other microcontroller, as the firmware is written in C or C++.

Here are 10 facts about shift registers that make them very useful.

1. They convert serial data to parallel, and parallel data to serial.
2. They can be easily chained together, generally 8 bits at a time to make longer wordlengths.
3. They are cheap and readily available - about £0.30 for an 8 bit shift register.
4. They can be used for storing data
5. They are used in UARTs for serial communication
6. They are used in I2C and SPI interfaces
7. They can be used to extend the simple I/O capabilities of small pin-limited microcontrollers
8. They can come with power drivers stages for driving lamps, relays and stepper motors
9. They are ease to wire up, using few pins and easy to control with simple firmware
10. They often form the basis of many segment LED drivers

I will be looking at some very common types:

74HC595  - this is the ubiquitous 8 bit series to parallel shift register, used for outputing parallel data. It requires just 3 pins on the Arduino to control it, and can be chained together to make much larger word sizes. It is available as a breadboard friendly 16 pin DIP, but also as compact SOIC, TSSOP and QFN packages.  As a HC device is is compatible with both 3V3 and 5V logic and can be powered from 2V to 6V. Each output can supply 20mA - which is ideal for driving LEDs with a suitable series resistor. Available from several manufacturers the world over. Datasheet here

74HC165 - this performs the complimentary function to the '595 which is parallel to series conversion. It also requires 3 pins to drive, can be chained to make larger word sizes and is very often used to provide additional input lines on small microcontrollers. These are very useful for reading switches (such as DIP switches used for configuration), or any other parallel input data. Again the HC logic allow a wide range of supply voltages, and available in a 16 pin DIP or several other compact packages.  Datasheet here

TPIC6B595 - this is an 8-bit serial to parallel shift register followed by a DMOS power driver stage, capable of 500mA of drive. It can be used for driving higher current, inductive loads directly such as relays, solenoids and small stepper motors. Available as a 20 pin DIP (or smaller) package, it can be used to replace the popular combination of a 74HC595 followed by a ULN2803 octal darlington driver.  If you are short of pcb space - this device is worth considering.  About £1 each in 10 off quantity.  A typical application would be driving a "fruit machine" where a few hundred lamp drivers are needed. Datasheet here

If you follow the links to the datasheets, you will see that 2 of them are via the Sparkfun site, showing the popularity of shift register devices amongst hobbyists. Indeed, shift registers are available on breakout boards and Arduino Shields, like this 48 Channel Output shield that uses six of the 74HC595 devices.

My experiments are slightly less ambitious. I decided to breadboard two 74HC595 devices and two 74HC165 devices to make a 16 bit output LED display, and a 16-bit switch input in order to expand the capabilities of the Arduino. Additionally I wanted to explore the use of the TPIC6B595 device as a means of driving a pair of small stepper motors - as the basis of some robotics and automation projects.

Wiring Up

74HC595 Output

Connecting a pair of shift registers on a breadboard to make a 16 bit word size is fairly straightforwards - here is a circuit from a French tutorial. It has nice clear diagrams of the breadboard layout too.

You will need 3 spare pins on the Arduino, referred to as latch, data and clock.  If you use the Arduino ShiftOut() function, these can be any three spare pins, but I chose to use 8,9 and 10 in order to leave 11,12 and 13 spare for future SPI work. If you follow the tutorial above and the code example provided it is very simple, and you will be up and running in no time.

I wanted to speed up the time that the latch signal was present, so in order to lower the latch I substituted the usual digitalWrite() function for the direct port command

PORTB &= ~_BV(0);  // This lowers digital pin 8

and to raise it

PORTB |= _BV(0);      // This raises digital pin 8

Doing this small code change allows the shift register to be updated at about 8000 times per second.


74HC165 Input

Wiring this up was also fairly straightforward.  Again you will need 3 lines to control the shift registers. I used digital lines 5,6 and 7 - again to avoid the SPI pins.  Again this  French wiki provides nice neat circuit diagrams in EagleCAD format.  However, I used pull down resistors, rather than pull-ups, and I used the Arduino shiftIn() function.  At first I noticed some very odd behaviour, where the register appeared to be multiplying the input number by 2. I had had a past experience of this, and realised that it was caused by clocking on the wrong edge of the clock.  This was easily corrected by making sure that the clock line is raised after the shiftIn(), so that it idles in the HIGH state, not the low state.

Code

Now we look at the code to combine the reading of the input shift registers with writing to the output registers. First we need to define the 6 various pins that carry the control signals:

   int sinPin = 5;            // Serial input pin from 74HC165
   int sclkPin = 6;          // Clock pin to 74HC165
   int loadPin = 7;         // Parallel load pin of 74HC165  
   int latchPin = 8;        // Pin connected to ST_CP of 74HC595
   int dataPin = 9;         // Pin connected to DS of 74HC595
   int clockPin = 10;     // Pin connected to SH_CP of 74HC595

and then within setup() - set them up as INPUT or OUTPUT

    pinMode(d,OUTPUT);
    pinMode(loadPin, OUTPUT);
    pinMode(sclkPin, OUTPUT);
    pinMode(sinPin, INPUT);
    pinMode(latchPin, OUTPUT);
    pinMode(clockPin, OUTPUT);
    pinMode(dataPin, OUTPUT);
    digitalWrite(sclkPin,HIGH);    // initialise the clock HIGH


Once you have set up the pins it's just 4 lines of code to read the inputs

// Read incoming word from 74HC165

digitalWrite(loadPin, LOW);
digitalWrite(loadPin, HIGH);
int incoming = shiftIn(sinPin, sclkPin, MSBFIRST);
digitalWrite(sclkPin,HIGH);

and 3 lines of code to write to the outputs

// and write it out to 74HC595

PORTB &= ~_BV(0);
shiftOut(dataPin, clockPin, MSBFIRST, incoming);  // shift out the bits:
PORTB |= _BV(0);

This sketch is available in full at this Github Gist

Once the signals have been defined and initialised, the code to read and write to the registers is really very trivial.  Whilst I have used the shiftIn() and shiftOut() functions provided by Arduino, these functions are also fairly simple and can readily be substituted for routines that directly control the I/O pins - if additional speed is needed.

In the next part I will look at how this shift register functionality can be incorporated into real applications such as stepper motor driving.


More Information

Nick Gammon's tutorial on using the '595

Nick Gammon's tutorial on using the '165

These outline using the shift registers with the SPI bus. This is the next step if you need the fastest speed data transfer.

The flawed Arduino tutorial - do not connect the 1uF capacitor there!! It's supposed to be a 100nF capacitor across the 5V and 0V power pins!















No comments: