You are on page 1of 5

projects microcontrollers

16 from 4
Port expansion using 74HC595s
Frank Link (Germany)

Not having enough ports on a microcontroller to drive all the functions of a circuit is a commonly-
encountered problem. The clever port expansion boards described here offer a solution, and need just
four signals from the microcontroller. The first board provides sixteen digital output bits; the second
board, equally simple to drive, can be used to interface to a HD44780-compatible LCD module. A C
library is available to facilitate using the boards with AVR microcontrollers.

boards, whose layout was subse-


Features quently refined at the Elektor labs.
Both boards are available from Elektor
• Port expansion from four outputs to sixteen outputs [1].
• Additional circuit board to interface to HD44780-compatible LCD The first printed circuit board is based
using four port pins around two 74HC595s. This provides a
• Controlled using software SPI emulation with free choice of port expansion to sixteen output bits;
microcontroller output pins the boards can be cascaded if even
• Boards may be cascaded more outputs are required.
• Maximum total load on all outputs approximately 70 mA Figure 1 shows how the 74HC595 is
• C library available for AVR microcontrollers to drive port expansion and LCD driven. The SDI (serial data in) signal is
interface boards used to send data to the device. With
each pulse on SFTCLK (shift clock) the
data bits are synchronously shifted one
place along the registers. Then a pulse
Some time ago the author developed of the ATmega8 were already commit- on LATCH CLOCK transfers the loaded
a board to drive a motorised poten- ted, and so the SPI protocol had to be data from a holding register in the
tiometer using an ATmega8. Several implemented in software. The result 74HC595 into the output register, and
outputs of the microcontroller were of the work is described here: a solu- the levels on the output pins change to
wired in parallel to increase the cur- tion which is suitable for all types of reflect the transmitted data.
rent available and thereby avoid the microcontroller, with four spare port The OE signal is dedicated to a special
need for an extra motor driver IC. Just pins required to drive the port expan- function of the 74HC595. In order to
five port pins were left over on the sion circuit. avoid the situation where the outputs
ATmega8, which made implement- We have made a C library available of the device go to an undefined state
ing the remaining functionality of the for free download: this supports AVR when power is applied, this signal can
board rather tricky. microcontrollers, but it is relatively be held high using a pull-up resistor
Undaunted the author carried out a straightforward to modify the source (via JP1 in Figure 1). This ensures that
thorough search of the Internet for code to adapt it to other device fam- all the 74HC595’s outputs go instead to
simple ways to expand the number ilies. The library allows the user to a high impedance state when power is
of available ports. Finally, however, control not just one, but any number of applied: a pull-up or a pull-down resis-
he decided to design his own hard- connected 74HC595s! tor can be connected to each output to
ware- and software-based solution to make sure that it carries the desired
the problem. The design was based on logic level in this state.
the well-known 74HC595 shift register,
Printed circuit boards If the jumper is not fitted OE can be
which can be driven using an SPI bus. In the interests of ease of use the driven from the microcontroller: this
Unfortunately, however, the SPI pins author produced two printed circuit requires a fifth spare output pin, of

66 elektor - 3/2009
VDD
K2 K1

VDD
VDD VDD
C1 C2

VDD 16
VDD VDD 100n 100n
16

16
R1 10
RST R1
K3 K4
VDD

VDD
IC1 QA 15
K2 11 1
10
RST QA
15 10
RST QA
15
1k

1k

SFTCLK QB K5 1 1
JP1 SHIFT CLOCK 12
LCHCLK QC
2 IC1 QB IC2 QB
JP1 1 2 11 2 11 2
LATCH CLOCK 3 SFTCLK QC SFTCLK QC
QD 3 4 12 3 12 3
OE 13 4 LCHCLK QD LCHCLK QD
OE QE 5 6 4 4
SERIAL DATA IN 5 QE QE
QF 7 8 13 5 13 5
SERIAL DATA OUT 14 6 OE QF OE QF
SDI QG 9 10 6 6
9 7 QG QG
SDO QH 7 7
QH QH
74HC595
14
74HC595 9 14 74HC595 9
GND SDI SDO SDI SDO
8 080682 - 13
GND

GND
8

080682 - 12

Figure 1. How to drive a 74HC595. Figure 2. Circuit diagram of the port expansion unit.

course. Code to use OE when initial- applied to SFTCLK, data bits are first K1, and K2 allows this power to be
ising the 74HC595 is implemented in shifted through the first device and delivered to cascaded boards. K3 and
the author’s software library, but can then through the second. K4 carry the output signals from the
be excised if it is not required. The microcontroller is connected to K5. 74HC595s, while JP1 determines how
Figure 2 and Figure 3 show the circuit As all five pins are connected to both the OE signal is driven.
diagram and printed circuit board lay- rows of the two-row header it is pos-
out for the port expansion unit. Two sible to connect the signals through to
74HC595s are connected in cascade a second port expansion board and so
Software
to double the number of available out- use further 74HC595s. Note, however, As is conventional, the software
puts. Cascade connection involves wir- that the SDI pin of the second board librar y comes in two par ts. The
ing the clock signals in parallel, and must be connected to the SDO pin of first is a header file com74hc595.
the input of the second 74HC595 to the first. h which includes function declara-
the output of the first. When a pulse is Power is supplied to the board via tions and definitions of the port pins

3/2009 - elektor 67
projects microcontrollers

Listing 1. Function com74hc595_out()


void com74hc595_out()
{
unsigned char count = COM74HC595_SIZE;
unsigned char* serp = com74hc595 + COM74HC595_SIZE;
do
{
unsigned char bits;
/* fetch byte from array com74hc595[] */
unsigned char data = *--serp;
/* shift out 8 bits per byte */
for (bits = 8; bits > 0; bits--)
{
PORT_COM74HC595 &= ~(1<<PORT_SER);
if (data & 0x80)
{
PORT_COM74HC595 |= (1<<PORT_SER);
};
data <<= 1; Figure 3. Port expansion printed circuit board.
/* a pulse on the clock signal shifts the data synchronously */
/* by one place through all the shift registers */
PORT_COM74HC595 &= ~(1<<PORT_SCK);
PORT_COM74HC595 |= (1<<PORT_SCK); COMPONENT LIST
} LCD board
}
while (--count > 0); Resistors
/* a pulse on RCK transfers the data from the shift re- R1,R2 = 1kΩ
gisters to the output latches */ R3 = 4kΩ7
PORT_COM74HC595 &= ~(1<<PORT_RCK); P1 = 10kΩ preset
PORT_COM74HC595 |= (1<<PORT_RCK);
} Capacitors
C1,C2 = 100nF

Semiconductors
and number of 74HC595 devices con- are connected to which signal on the T1 = BC557
IC1 = 74HC595
nected (COM74HC595_SIZE). The sec- 74HC595 board. There is one restric-
ond is a .c file containing the C source tion: the pins used must all belong to Miscellaneous
code that actually drives the ports. The the same port, defined in the header K1 = 2-way pinheader
header file will generally need to be file as PORT_COM74HC595. K2 = 10-way pinheader (2x5)
JP1 = 3-way pinheader with jumper
modified by the user to specify which The application program must first call PCB no. 080682-1 [1]
port pins on the AVR microcontroller the function com74hc595_init(). This

Listing 2. Simple running light initialises all the variables and con-
stants used within the library. When
#include <avr/io.h> this call completes all the 74HC595
#define F_CPU 3686400 outputs will have been set low.
#include <util/delay.h> The functions com74hc595_setBit()
#include “com74hc595.h” and com74hc595_unsetBit() address
int main(void)
{
the individual output pins. Setting
com74hc595_init(); bits according to a specified bit pat-
while(1) tern can be achieved using the func-
{ tions com74hc595_setPor t() and
for ( int i = 0; i < com74hc595_BYTES; i++ ) com74hc595_unsetPort(). Functions
{ com74hc595_setall() and com74hc595_
com74hc595_setBit( i );
unsetall() can be used to set all port
com74hc595_out();
_delay_ms( 2000 ); pins high or low respectively with a
_delay_ms( 2000 ); single call.
com74hc595_unsetBit( i ); The actual work of driving the
com74hc595_out(); 74HC595s is carried out in the func-
_delay_ms( 2000 ); tion com74hc595_out(): see Listing 1.
_delay_ms( 2000 );
It synchronously clocks the individual
}
} bits, stored in a character array called
return 0; com74hc595[], through the chain of
} 74HC595 devices. The operation pro-
ceeds in reverse order, starting with

68 elektor - 3/2009
VDD LCD1

R1

1k
LUMEX 801602
JP1 2 x 16

LED+A
LED-C
VDD
VSS

R/W
RS
VL

D0
D1
D2
D3
D4
D5
D6
D7
E
VDD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
VDD
K1
C1 P1
C2
100n
100n
10k

T1

16
BC557

VDD
R2
10 15
RST QA 1k
IC1 QB 1
K2 11 2
SFTCLK QC R3
1 2 SHIFT CLOCK 12 3
LCHCLK QD
3 4 LATCH CLOCK 4

4k7
QE
5 6 OE 13 5
Figure 4. Printed circuit board for the LCD interface circuit. OE QF
7 8 SERIAL DATA IN 6
QG
9 10 SERIAL DATA OUT 7
QH
74HC595 VDD
14 9
COMPONENT LIST SDI SDO

GND
Port expansion board

8
Resistors 080682 - 11

R1 = 1kΩ

Capacitors Figure 5. Circuit diagram of the LCD interface circuit.


C1,C2 = 100nF

Semiconductors
IC1,IC2 = 74HC595 then back low. ure 4 and Figure 5. The LCD is con-
A simple running light application nected to the outputs of the 74HC595:
Miscellaneous example is shown in Listing 2. as the module is to be used in four-bit
K1,K2 = 2-way pinheader
K3,K4 = 8-way pinheader mode, pins D0 to D3 of the LCD are
K5 = 10-way pinheader (2x5) connected to ground. The functions of
JP1 = 3-way pinheader with jumper
LCD interface K2, K1 and JP1 are as described above
PCB no. 080682-2 [1] It is very common in microcontroller- for the port expansion board.
based applications to want to display The 10 kΩ trimmer should be soldered
the values of internal variables. This to the track side of the board to sim-
gives rise to the idea of controlling plify adjusting the LCD contrast after
a HD44780-compatible LCD module assembly. All the other components are
over just four wires with the help of hidden underneath the LCD module.
the last bit. One bit is transferred into a 74HC595. In addition, we can also The author has also written functions
the chain by taking the clock signal allow the microcontroller to switch a to control the LCD and made them
low and then back high. When all bits backlight on and off. into a library. Again this comprises a
have been sent, RCK is taken high and The resulting circuit is shown in Fig- header file (com74hc595_LCD.h) and

Advertisement

3/2009 - elektor 69
projects microcontrollers

the main C code file. The central func- article [1]. The author is continuing to
tions are com74hc595_lcd_nibble() work on further expansion plans, and Internet Link
and com74hc595_lcd_enable(); all the interested readers are invited to con- [1] www.elektor.com/080682
other functions are built upon these tact him by e-mail at FrankLink61@
routines. aol.com.
In com74hc595_lcd_nibble() (Listing 3) (080682-I)
the four data bits are first set to zero.
Each bit is then checked to see if it
needs to be set: the backlight flag must Listing 3. Function ..lcd_nibble()
be included in the same bit array, and
so its state must also be tested and the
void com74hc595_lcd_nibble ( unsigned char d )
appropriate bit set if necessary. Finally
{
the information is sent to the 74HC595 com74hc595_unsetBit( LCD_B4 );
using com74hc595_out(). com74hc595_unsetBit( LCD_B5 );
com74hc595_unsetBit( LCD_B6 );
com74hc595_unsetBit( LCD_B7 );

if ( d & 1<<4 ) com74hc595_setBit( LCD_B4 );
if ( d & 1<<5 ) com74hc595_setBit( LCD_B5 );
if ( d & 1<<6 ) com74hc595_setBit( LCD_B6 );
if ( d & 1<<7 ) com74hc595_setBit( LCD_B7 );

if ( BackLightState == ON ) com74hc595_unsetBit( LCD_LIGHT );


if ( BackLightState == OFF ) com74hc595_setBit( LCD_LIGHT );

com74hc595_out();

com74hc595_lcd_enable();
}

Listing 4. Function ..lcd_enable


void com74hc595_lcd_enable(void)
{
com74hc595_setBit( LCD_EN );
com74hc595_out();
The function com74hc595_lcd_enable()
_delay_us(20);
(Listing 4) tells the HD44780-compat- com74hc595_unsetBit( LCD_EN );
ible LCD controller that new data bits com74hc595_out();
are available. This is done by setting }
the LCD’s enable signal (EN) high for
20 μs and then low again.
Before the library can be used a cou-
ple of settings need to be made in the
header file [1]. The first is the clock fre- Listing 5. ‘Hello World!’
quency at which the microcontroller is
operating: this is needed to ensure that #include <avr/io.h>
the timing for the LCD can be calcu- #include “com74hc595.h”
lated exactly. If the LCD board is used #include “com74hc595_LCD.h”
as one member of a chain on 74HC595s,
int main(void)
the macro LCD_PORT needs to be
{
adjusted to reflect its position within com74hc595_init();
the chain. It is also of course neces- com74hc595_lcd_init();
sary to specify which port pins are to com74hc595_lcd_light_on();
be used for the SPI port. com74hc595_lcd_data(‘T’);
Listing 5 shows our version of the com74hc595_lcd_data(‘e’);
familiar ‘Hello World’ example program, com74hc595_lcd_data(‘s’);
com74hc595_lcd_data(‘t’);
demonstrating correct initialisation of com74hc595_set_cursor(0,2);
the two libraries and subsequent use com74hc595_lcd_string(“Hello World!”);
of the LCD driver functions. The full while(1)
range of commands available in the {
library is described in the supplemen- }
tary documentation available for down- return 0;
}
load (along with source code) from
the project pages accompanying this

70 elektor - 3/2009

You might also like