Using shift registers to extend the microcontroller I/O

V1.01 28-Jul-04

1. Introduction

When selecting the microcontroller of choice for your robot, one parameter will be the number of I/O channels available. However, it is quite likely that the ideal processor in terms of special functions may not have enough I/O for the purposes required. The solution to this is to use a shift register chain to expand the I/O potential. This page is a combined hardware/software page since the shift register chain itself is also described.

2. The shift register chain

2.1. How it works

A block diagram of the shift register chain is shown below:

The shift register chain is made up of a number of output shift register devices (74HC595 or 74HC4094s) and a number of input shift register devices (74HC165). Both these chips are 8-bits wide, so the number of inputs and outputs available will be a multiple of 8. Only as many devices as are required may be fitted.

2.2. How to drive it

The circuit description covers use of the 74HC595. Use of the 74HC4094 (which may be cheaper and easier to find) is covered later in this document. First the strobe line is dipped low and back high again. This latches all the inputs into the 74HC165 input shift registers. Then the clocking begins. With each clock pulse, the data line is set to an output, and the appropriate data bit is presented on the line to be clocked into the output shift register. On the same clock pulse, the input shift register presents its next data bit to the microcontroller data pin. The data pin is set to an input, and this data bit read.

The number of clock pulses required is the larger of the number of inputs and the number of outputs. After this number of clock pulses, all the required output states have been shifted into position in the 74HC597 output shift registers, and another dipping of the "strobe" line is performed to set these states on the shift register output pins.

A timing diagram for this operation is shown below:

The points in this diagram are:

You can make the chain any length you want, with as many output stages or input stages as required. It may be all input stages, or it may be all output stages too. There are constants in the code where the dimensions of the shift register are defined, and they control how the software drives it.

2.3. Protected outputs

The outputs will be in an undefined state when the circuit is powered up. This may be dangerous if the lines that drive the output must not be turned on unless special circumstances are observed. Therefore, the output shift registers chosen have three-state outputs. This means that their outputs can be turned off (not high or low but effectively open-circuit). They will then need to be pulled high or low as required by the circuit that the output is connected to. Using this three-state option requires a further output line from the microcontroller as shown below:

2.4. The circuit diagram

The circuit diagram of the shift register chain is shown below. This example uses two output shift registers (16 outputs) and one input shift register (8 inputs). The data pin must be able to be switched between an input and an output, but the clock and strobe pins are always outputs.

Click on the circuit diagram to open it in a new window.

R1 is the feedback resistor so the data pin can read the shift register and write to it. R2 causes the output shift register's outputs to be tri-state until the microcontroller code can start and set the output enable line high to turn off the output shift register outputs.

Any inputs that are not used should be tied either high or low. CMOS inputs should never be left floating since they can be switched by external noise which may cause the IC to draw a lot more current than is necessary.

2.5. Using the 74HC4094 instead of the 74HC595

These two devices are functionally very similar, but there are slight differences in signals, and the pinout is completely different. Since the HC595 came from the bipolar TTL 74 series family, and the HC4094 came from the CMOS 4000 series family, the active state of signals are often opposite. TTL generally used active low inputs whereas CMOS used active high inputs. The reason for this is too complex to be described here.

The table below shows what pins should be replaced for what, and whether the active state is different:

74HC595 pin 74HC4094 pin Function Active state swap?
12: STcp 1: STR Strobe / storage register clock No
14: Ds 2: D Serial data input No
11: SHcp 3: CP Shift register clock No
15: Q0 4: Q0 Parallel data outputs No
1: Q1 5: Q1
2: Q2 6: Q2
3: Q3 7: Q3
4: Q4 14: Q4
5: Q5 13: Q5
6: Q6 12: Q6
7: Q7 11: Q7
9: Q7' 9: QS1 Serial data output No
not on 595 10: QS2 Second serial data output -
13: /OE 15: OE Output enable Yes
10: /MR not on 4094 Master reset -

One correspondent wanted to use the 74HC4094 device, and the parallel port of a PC running Windows to drive the shift register chain. This presented its own problems which I thought may be relevant to share here. The email discourse is here.

3. The software

The code to drive the shift register chain is in the following C source file and its associated header file:

The code is heavily documented and is not very complicated.

At the top of the source file are three definitions:

// These define the chain dimensions
#define CHAIN_LENGTH 24 /* Length of chain in bits */
#define NUM_OUTPUTS 16 /* Number of outputs in chain */
#define NUM_INPUTS 8 /* Number of inputs in chain */

This is where the dimensions of the shift register are defined.

The input and output pins in this code are defined as follows in a separate header file for controlling the hardware. All hardware-dependant values should be defined in a single file so if the code is ported to another processor, hopefully only one file will need changing - this is a common feature of embedded programs.

// Extension shift register

#define SR_DATA P8DR.0
#define SR_CLOCK P8DR.1
#define SR_STROBE P8DR.2
#define SR_INPUT P8DDR = b00011110
#define SR_OUTPUT P8DDR = b00011111

The SR_DATA, SR_CLOCK, and SR_STROBE definitions define the port pins (in this example port 8 pins 0, 1, and 2 of a Hitachi H8S), and two more definitions are given (actually code macros) for changing the SR_DATA port to an input or output. The binary values b00011110 are defined in a separate file - all 256 values defined in the file binary.h. Binary numbers are not native to C unfortunately, so I include this binary definition file in nearly all my embedded software projects.

The following functions are available to the outside world:

void UpdateChain(void);
void InitialiseShiftRegisterChain(void);
void ChainSetBit(int BitNumber, int State);
void ChainSetBitNow(int BitNumber, int State);
void ChainSetByte(int ByteNumber, unsigned char Value);
void ChainSetByteNow(int ByteNumber, unsigned char Value);
unsigned char ChainReadBit(int BitNumber);
unsigned char ChainReadBitNow(int BitNumber);

There are two ways of setting or reading bits - an immediate mode and a delayed mode. The immediate mode is if you want to read or set a single bit and it must happen immediately. The functions ChainSetBitNow() and ChainReadBitNow() will perform this function. However, using these functions will perform a full clocking cycle on the shift register of CHAIN_LENGTH clock operations. If you wanted to set three bits in one go, you wouldn't want to drive CHAIN_LENGTH x 3 cycle, so the delayed form functions ChainSetBit() and ChainReadBit() are used. These just set the bits to the desired state in the shadow register inside the microcontroller. Once all the required bit states have been defined, the UpdateChain() function is called, and this will perform CHAIN_LENGTH clock cycles, update the outputs, and read in the new set of inputs.

The InitialiseShiftRegisterChain() function simply clears all the outputs to zero, which may (or may not) turn off all devices driven by these outputs.

The outputs can be written to a byte at a time as well if required (for example, I use one output shift register to drive an 8-bit data bus to an LCD controller, so all communication with that is done a byte at a time). Again, there is an immediate form, ChainSetByteNow(), and a delayed form ChainSetByte() that allows other outputs to be set up before a call to UpdateChain().

The comments in the UpdateChain() function explain exactly how the I/O lines are driven to write the appropriate bits to the shift registers.

4. Advantages and disadvantages

The main advantage of this method is that it allows an unlimited number of I/O pins to be used, even if the processor has a very small number. The main disadvantage is that all these inputs and outputs are going to be accessed a lot slower than normal I/O ports. Using these outputs for PWM signals to drive speed controllers would be unrealistic for example - the maximum frequency obtained would be very low. However, the output would be perfectly acceptable for weapons driving for example, where the delay of a few tens of milliseconds would be unnoticed. Basically, anything that is controlled directly by the human driver will probably be ideal for the shift register chain since the few tens of milliseconds delay is negligible compared with the human reaction times.

Obviously, the more shift register chips that are added to the chain, the slower will be the response time of writing outputs or reading inputs. If the time becomes too great for your application, you will either have to move the input or output that needs to be faster to a direct I/O line, or you can create a second register chain. If you have a second chain with the same number of input and output shift register chips, then the "output control", "clock" and "strobe" lines can be commoned for both chains, and just a separate "data" line used, one for each chain. The code will then have to be modified so that there are two sets of shadow registers, and the data read and data write lines in UpdateChain() must be doubled up to cope with the extra chain.

5. Devices used in this circuit

The following devices were used in this circuit. Click on the manufacturer’s name to go to their web site, or the device name to go to the device datasheet.

Manufacturer Device Description
Philips Semiconductors 74HC165 8-bit shift register with parallel inputs
74HC595 8-bit shift register with parallel outputs
74HC4094 8-stage shift-and-store bus register

6. Links

These links are scored for interest and quality of information given from 0 to 5. A preceding 'C' means it is a commercial company selling stroboscopes, and a preceding 'E' means it is an educational site.

[E5] "Circuit Cellar" article describing ways to increase I/O capability:
The above as a single PDF file

[E2] JAL (Just Another Language) GNU compiler for the PIC range has a library for driving shift register chains for extending the I/O
The JAL page