Multiplexing To Get More Outputs

by Tom Dickens

Introduction

Assuming that your system has some digital outputs, but you
need more outputs for the current project you're working on, what do you do?
This article will explain how to add as many digital outputs to your system as
you desire. The types of systems I'm referring to are typically
microcontrollers, such as the 68HC11, 68HC12, 68332, PIC chip, BASIC Stamp,
ATMEL processor, and a host of others. This technique can also be applied to
the parallel-port output of a PC. I will present this information in a generic
manner, which should apply to any target system. The usage examples will be in
pseudo-code and then in assembly code which are specific to the 68HC11, but
this could be easily adapted to any other system.

The Concept

You can use a limited number of digital outputs, along with
a simple digital circuit and a simple program, to control as many digital
outputs as you desire. The type of circuit we're talking about here is a
multiplexer, or MUX for short. With a MUX circuit, you take your digital
outputs from your system and group them into two types, data lines and address
lines. Depending on the number of system digital outputs you have to work with,
and how you group them into address lines and data lines, you can get various
numbers of resulting MUX outputs, plus varying complexity of the required
digital circuit.

Figure 1. Concept of system outputs generating more
outputs using a multiplexer circuit.

Let's walk through an example using 8 system digital output
lines to see the possibilities:

The top row in Table 1 shows us that if we use all 8 lines as data lines, we
get 8 resulting output lines (and our circuit is very simple, just wires). The
next row uses one of the lines as an address and the other 7 as data. With the
address set to 0, we can specify 7 data values and then with the address set to
1 we can specify 7 more data vales, giving us 14 resulting output lines.
Continuing down the rows we see that the same 8 system outputs can generate up
to 128 resulting outputs. Wow! There is a generic equation to determine the
resulting number of outputs we would get. Given N as the total number of
system outputs, A is the number of address lines, D is the number
of data lines, where A + D = N, we can calculate:

We can clearly see in the plot in Figure 2 that the more lines we use as
addresses, the more resulting outputs we will generate. We can, using Equation
1, see the number of outputs we can get for different numbers of address and
data lines.

However, there is a price to be paid for this; the complexity of the
required digital circuit, and the complexity and time required to execute the
resulting driving program, goes up with the number of address lines. Another
factor is the type and configuration of the digital chips that are available to
use in the support circuit. In a minute we will look at practical limitations
and implementations.

Considering using from 4 to 12 output lines. Figure 3 shows
how many outputs could be generated. The maximum for 11 bits is 1024, and for
12 bits is 2048, which is a very large number of outputs from a small number of
original output lines

Figure 3. Possible number of outputs for 4 through
12 lines.

Before we jump into building a 12-to-2028-output circuit, let's first
consider the building blocks available and the hardware/software complexity
required, so we can design a reasonable system which can be easily built and
easily used, but also gives us a good expanded-output capability.

Circuit Building Blocks

The digital chips available that we will use to build up our
multiplexing output circuit are the 74HCnnn family of chips, but similar
functionality can be found in the other 74-series (74LS for example), and also
in the old 4000-series CMOS devices.

Address Lines

For the address lines, we need to "fan out" the
address into N control lines (we'll see why in a moment). The type of device
we want here is called a "decoder," also referred to as a
"demultiplexer." These take A address lines, and produce 2A
outputs, where one output as specified by the address is at one logic level
(typically Low), and all the other outputs are at the other logic level
(typically High). The two most-used 74HCnnn devices for this are the 74HC138
(1-of-8 decoder) and the 74HC154 (1-of-16 decoder). The '138 (short
for 74HC138) takes 3 address lines and produces 8 control lines, while the
'154 takes 4 address lines and produces 16 control lines. The typical
logic-symbol for such a device is shown in Figure 4.

Figure 4. Decoder logic symbol.

The 3 address lines are A0, A1, and A2. There are also 3
enable lines which can be used to configure multiple '138 devices to directly
handle up to 6 input address. To enable a device, tie E1 and E2 low and E3
high. The 8 generated outputs are Y0 trough Y7. The circles on the diagram
indicate that the active level for that pin is inverted, thus we can see that
E1 and E2 should be low (inverted), E3 should be high, and the output addressed
in Y0 to Y7 will be low (all other 7 outputs will be high).

Table 2 is a truth-table for the 74HC138, showing the states of the outputs
for all input combinations. The '-' entries are "don't care"
states. H is logic high, or 5 volts. L is logic low, or 0 volts.
Note that any enable input can disable the device.

Table 2. Truth-table for the 74HC138
decoder/demultiplexer..

Address

Lines

Enable
Lines

Output
Lines

A2

A1

A0

E3

E2

E1

Y7

Y6

Y5

Y4

Y3

Y2

Y1

Y0

-

-

-

-

-

H

H

H

H

H

H

H

H

H

-

-

-

-

H

-

H

H

H

H

H

H

H

H

-

-

-

L

-

-

H

H

H

H

H

H

H

H

L

L

L

H

L

L

H

H

H

H

H

H

H

L

L

L

H

H

L

L

H

H

H

H

H

H

L

H

L

H

L

H

L

L

H

H

H

H

H

L

H

H

L

H

H

H

L

L

H

H

H

H

L

H

H

H

H

L

L

H

L

L

H

H

H

L

H

H

H

H

H

L

H

H

L

L

H

H

L

H

H

H

H

H

H

H

L

H

L

L

H

L

H

H

H

H

H

H

H

H

H

H

L

L

L

H

H

H

H

H

H

H

Data Lines

For the data lines, we need to
capture the data and hold their values until we want it to change. The type
of device we want here is called a "latch" or a
"flip-flop" (FF). These devices take D data lines and will
capture their values when commanded to do so. The commonly-used 74HCnnn
devices for this are the 74HC74 (dual flip-flop, 2 in one chip), 74HC173
(quad flip-flop, 4 in one chip), 74HC174 (hex flip-flop, 6 in one
chip), and the 74HC374 (octal flip-flop, 8 in one chip).

Figure
4. 8-bit latch
logic symbol.

These devices have data inputs, data outputs, a clock input,
and output enable inputs. The data on the inputs is captured and presented at
the outputs when the cock input goes from a low to a high state. Tie the output
enable(s) to enable the device (low on the '374).

Figure 4 shows the typical
logic-symbol for a 74HC374 8-bit latch.

There is a device, the 74HC574 which is identical in
functionality to the '374, but all the data inputs are on the left side of the
chip, and all the outputs are on the right side of the chip. This makes it much
easier to wire up; many people commonly use the '574 for this reason even
though the two devices are logically identical.

Putting it All Together

How does this work? Let's use a example to walk through the
setup and use of a typical system.

The Hardware

Assume we have 8 system outputs (O0
trough O7) from a microcontroller and we want more outputs. Let's call 2 of
these outputs address lines, and 6 of them data lines. With 2 address lines, we
will generate 4 control outputs (Y0 through Y3 in Figure 5). Each of these is
connected to the clock input of one of the flip-flip devices. The 6 data lines
(O2 through O7) are connected to the data input lines (D0 through D5) of all 4
of the hex flip-flops. The resulting outputs are generated from the 4
flip-flops as outputs R0 through R23. Thus, with 5 common off-the-shelf chips,
we have turned 8 outputs into 24 outputs.

Figure 5. 8-bit to 24-bit multiplexer circuit.

The Software

To use the circuit detailed above, the software in the
system must know about the specific hardware connected to the 8-bit output
lines. Given a 24-bit number to output, what does the software need to do?
Simply stated:

We need to present the correct data to the 6 data lines
and then cause the correct output from the ‘138 to go from low to high to
capture the data on the data lines into the correct latch.

OK, we’ll walk through this specific example step-by-step.
But first, we need to diverge into the topic of Gray Codes.Frank Gray, a research scientist at Bell
Labs, on 3/17/1953, filed patent no. 2,632,058 for the Gray code encoding
vacuum tube. We’re not using vacuum tubes, but the principle is very important
here too. Gray code is a sequence of binary numbers such that any two
consecutive numbers differ only in a single position. Let’s consider the
repeating cycle of numbers 0 through 3 as seen in Table 3.

Table 3. Gray Code for a 2-bit number cycling from
top to bottom.

Decimal

Binary

Gray Code

0

00

00

1

01

01

2

10

11

3

11

10

0

00

00

1

01

01

2

10

11

3

11

10

The red cells in the table cause a problem in our decoding
circuit; two bits are changing at the same time as we enter the cell. An actual
digital circuit CANNOT change two things at exactly the same time; there will
be a slight lag in one of them. So in switching from binary number 00 to 11,
for example, the circuit will momentarily pass through either 01 or 10. This
will cause a very short change in the corresponding Y1 or Y2 output of the
decoder, which will cause the wrong data to be captured in the corresponding
latch. I’ve seen this problem in embedded software; it is hard to find and it
really messes things up! The key is to only change 1 bit at a time, even from
the last value back to the fist value, so the decoder will change its Y outputs
in a very controlled manner, which is goodness. We don’t need to change
anything in the circuit to achieve this goodness, we just need to remember to
use Gray Code switching in our controlling software, or else our outputs will
be very strange and not what we want them to be, and you may think you have a
hardware circuit error. Now, on to our code.

Starting assumption: The address bits on the output line are all low from
the previous write.

Set the address lines O1-O0
to L L (should already be in this state).

Set the data values V5-V0 on
the data lines O7-O2.

Set the address lines O1-O0
to L H. This causes the data values to be captured in the first
(right-most) flip-flop as line Y0 goes from LOW to HIGH. Also note that Y1
will now be LOW.

Set the data values V11-V6 on
the data lines O7-O2.

Set the address lines O1-O0
to H H (remember, we need to use Gray Code!). This causes the data values
to be captured in the second flip-flop.

Set the data values V23-V18
on the data lines O7-O2.

Set the address lines O1-O0
to H L. This causes the data values to be captured in the forth flip-flop.

Set the data values V17-V12
on the data lines O7-O2.

Set the address lines O1-O0
to L L. This causes the data values to be captured in the third flip-flop.

Of course, in your system you would write a subroutine to do
this, so your program would just invoke the subroutine to get the job done.

What does this software look like? How does it do the 8 steps details above?

Listing 1 shows a detailed assembly-language program for the 68HC11. The
operations used are bit-level manipulations, such as bit-shifting, bit-masking,
and bit-setting and bit-clearing. You can accomplish these operations using
math-type operations, but the clearest way to do these operations is to use the
bit-level operations in your system (I bet you always wondered what they were,
and why anyone would every want to use them). In assembly code these operations
are the logical shift and bitwise AND, OR, and NOT operators. In C, C++, and
Java, look at the shift (<<
>>) operations, the bitwise (|
& ^) operators.

Practical Limitations

OK, we've seen the theoretical fan-out we can achieve, but
let's consider the common building-blocks and what would be practical to build
and use. We could use devices without using all of their capabilities, but if
you're building an external circuit for your system you would want to keep it
as small as possible for reasons of cost, footprint (size), and complexity to
build.

We see the 74HC138 decoder is a great 1-chip workhorse that can directly
take 3 address lines and produce 8 control lines. Two '138 devices can be
combined with 4 address lines (3 in common, and the 4th selecting which '138 to
use, for example, address lines A0-A2 connected to the address inputs of both
'138s, and the last address line, A3, connected to E1 on one '138 and E3 on the
other). Two '138s will produce 16 control lines, or you could use a single
74HC154 (The '138 is a 16-pin chip while the '154 is a wider 24-pin chip.
Multiple '138 chips are commonly used). A single '138 can be used with only 2
address lines (A0 and A1, connect A3 to ground), which would then only generate
4 usable control lines, Y0 through Y3.

The other type of device required is a flip-flop to capture the outputs. The
four devices discussed handle 2 bits, 4 bits, 6 bits, and 8 bits. The 4, 6, and
8 bit devices all have a common clock, which means that all 4, 6, or 8 bits
will be changed at the same time. For some applications this is a problem, but
for our purposes this is what we want. Therefore, the "best"
combination is one which uses 6 or 8 data lines, since these can be captured in
a single flip-flop device for each address. If you use 5 data bits you would
use a 6-bit flip-flop and not use on of the bits, and so on.

Let's revisit Table 1, but add columns for the number of chips required to
implement each circuit. I've highlighted the interesting numbers in Table 4 in
red, showing the number of decoders and flip-flops needed to implement the
circuit.

Table 4. Numbers of devices needed for circuits with
various latch sizes.

Starting
outputs

Address
lines (A)

Data
Lines (D)

Total
Outputs

74HC138
3-8 decoder

74HC74
2-bit FF

74HC173
4-bit FF

74HC174
6-bit FF

74HC374
8-bit FF

8

0

8

8

0

0

0

0

0

8

1

7

14

1

0

0

0

2

8

2

6

24

1

0

0

4

0

8

3

5

40

1

0

0

8

0

8

4

4

64

2

0

16

0

0

8

5

3

96

4

0

32

0

0

8

6

2

128

8

64

0

0

0

8

7

1

128

14

64

0

0

0

Let's suppose you really need 128
outputs. The two solutions above can accomplish this, but need from 72 to 78
chips. I don't know about you, but I wouldn't want to build that! Let's consider
different combinations of address and data lines (assuming you have a few more
than 8 starting system outputs to work with) to achieve 128 outputs. Note that
some of these circuits will not use all control outputs generated. Again, I've
highlighted the interesting numbers in table 5 in red:

Table 5. Numbers of devices needed for circuits with
various latch sizes to generate 128 outputs.

Starting
outputs

Address
lines (A)

Data
Lines (D)

Total
Outputs

74HC138
3-8 decoder

74HC74
2-bit FF

74HC173
4-bit FF

74HC174
6-bit FF

74HC374
8-bit FF

8

6

2

128

8

64

0

0

0

9

5

4

128

4

0

32

0

0

10

5

5

128

4

0

0

26

0

11

5

6

128

4

0

0

22

0

12

4

8

128

2

0

0

0

16

We clearly see, if we can spare a few more system outputs we can easily
generate 128 outputs with much less external circuitry (and effort). If I
needed 128 outputs I know I would try to come up with 12 starting outputs and
build the 18-chip circuit.

I should mention that you could use a two-level fan-out
scheme, such as using 5 starting outputs to fan-out to 12 outputs (2 address
lines and 3 data lines), then use these 12 intermediate outputs to fan-out to
128 outputs as we saw above. The software would need to use 4 writes to
generate a single 12-bit number, and it would require 16 12-bit numbers to set
all 128 output bits. There is also the issue of timing with the edge-triggered
flip-flops, which would really force you to use 6 starting bits (2 address
lines and 4 data lines) to allow the 4 address lines for the second stage to be
changed all at the same time. Using multi-level designs you can fan-out
to as many outputs as desired with a very small number of starting outputs, but
the required hardware and software would become more and more complex.

Example Circuits and Software

Let's choose a couple of desired configurations and look at
the detailed circuits and software. Two that I'm currently adding to 68HC11
systems for robotic use will go from 8 system outputs to 24 outputs, and 11
system outputs to 64 outputs. We will then go on to detail how to use these in
the supporting software. Of course, my hope is that you can take the
information presented here and customize it to build up the desired circuit and
software for your particular application.

8 Outputs to 24 Outputs

The program to drive the miltiplexer circuit is organized as
a method that is called to set the entire 24-bit bit number. The application
program just calls this method whenever it wants to change these outputs. The
pseudo code for this is what we saw before. The hardware requires a 2-bit
address and a 6-bit data value. Let's assume this is connected to the B port on
the 68HC11, which resides at address $1004.

The software to do this in 68HC11 assembly language is shown
in Software Listing 1. Anything after a ";" sign is a comment, which
are used describe the logic.

11 Outputs to 64 Outputs

Building on the ideas we’ve discussed, here is a 9-chip
solution to expanding 11 system outputs (3 address and 8 data) to 64 outputs.
It uses a single 74HC138 decoder and uses the 8 lines from it to drive eight
8-bit latches for a total of 64 bits.

Figure 6. 11-bit to 64-bit multiplexer circuit.

The circuit can be easily wired up
from the connections in Figure 6, and the software will be an expansion of the
code for the 10-to-32 multiplexer circuit in Software Listing 2.

Conclusions

We have discussed the technique of expanding a small number
of digital outputs into a very large number of outputs. We have considered the
available off-the-shelf devices commonly used to implement a multiplexer
circuit, which in turn dictates the practical limits to these designs.

About The Author

Tom Dickens is an engineer and Associate Technical Fellow at
The Boeing Company, where he is a technical architect working on both pure
software systems and embedded systems. He also teaches at Seattle colleges in
the evenings, currently at the Washington branch campus of the University of
Phoenix. Tom is an active member of the Seattle Robotics Society (
http://seattlerobotics.org/)
and maintains a web-site of 68HC11 information and examples
(
http://tomdickens.com/68hc11/intro.html
).