Driving Common LED Displays
7-Segment LED displays have been around for a long time, it seems there are still a pretty decent supply of them for the time being.
There's a variety of ways to drive these, Common Anode types will have their individual segments pulled low thru a limiting resistor. Common Cathode types require their individual anode segments to be driven high thru a limiting resistor.
TTL is pretty good about supplying a 'low' for Common Anode displays, but since they can only drive at low current levels large displays require an interface that's more capable of sinking larger currents. While TTL LS are pretty good for sinking current, that is supplying a path to ground, they aren't so great for sourcing current. This usually leads the designer to a Common Anode type of display, with limiting resistors placed between the LED segment and the chip used for sinking the current.
On the original 8279 Keyboard Interface for the Space-Time Productions board, each segment was wired by way of transistor driver circuit, directly to an output bit of the 8279's display port. This means it requires a full 8-bit byte to drive a single digit. The advantage of course is that other characters than 0-9,A-F can be accomplished. Because each segment was controlled separately underlines, equal signs, and certain alpha letters could be accomplished. For example a phrase like PC=301F _ _ _ _ could be prompted with little problem.
The downside to this is that if you only require 0-9, or 0-F hex, it wastes pins of the 8279's output. If each byte is broken into two 4-bit numbers, you can effectively double the display. This would allow you to have two digits per byte of display space.
The classic chip to drive the 7-segment LED digit was the 7447. There are a few other chips in this family, some with built-in counters, latches, varying levels of output voltage drivers, etc. I think, if I remember my Engineering Notebook from Radio Shack, there was even one nice chip that included counter, 7-segment decoder and driver, plus a nice little multiplexing circuit to handle 3 digits, with the help of 3 external transistors to enable each digit in succession.
Problem with the 7447 was it was strictly BCD, rendering 0 thru 9. Once your 4-bit input went over "9", if you applied A-F to the inputs, you'd get garbage on the LED. My solution was to build a 'decoder' into a 16L8 PAL (Programmable Array Logic), so it would correctly display everything from 0-9,A-F for any 4-bit input.
Approaching the Problem
As you first look at a 7-segment LED digit display and think thru each digit 0-9,A-F you start to realize it's easier to keep track of what segments are OFF rather than which ones are ON. This is great, because the outputs of the PAL16L8 are active low. If you use those to drive a ULN2003 open collector driver for example, this works just fine and the ULN2003 can take all the LED segment current while keeping the PAL outputs safe from harm.
Here's the list of "Off" segments:
- 0 - G is off.
- 1 - D,E,F,A and G are off.
- 2 - C and F are off.
- 3 - E and F are off.
- 4 - A,D and E are off.
- 5 - B and E are off.
- 6 - Not to be confused with "b", segment B is off.
- 7 - D,E,F and G are off.
- 8 - All segments on.
- 9 - E is off.
- A - D is off.
- B - Diplayed as "b", segments A and B are off.
- C - B,C and G are off.
- D - Displayed as "d", sgments A and F are off.
- E - Segments B and C are off.
- F - Segments C and D are off.
Next, if you take this segment by segment, you can generate a function table. This table will define the program for the PAL.
- A - Off during 1,4,B,D
- B - Off during 5,6,B,C,E,F
- C - Off during 2,C,E,F
- D - Off during 1,4 7,9,A
- E - Off during 1,3,4,5,7,9
- F - Off during 1,2,3,7,D
- G - Off during 0,1,7,C
You can reduce a few of the terms down using a Karnaugh Map, but since there are quite a few rows for programming output conditions, there isn't a whole lot of savings here.
The 16L8 PAL is a pretty fundamental logic array of AND/OR gates where there are 10 direct inputs into the array, and 6 inputs which come from other outputs. This is pretty handy if one of your output terms is complex, and you need that same term for other conditions. All the inputs and outputs cross each other in an array of Each term is connected by a set of fuses, which are either retained or destroyed during programming (see the diagram below).
A Little History
In the face of today's FPGA's, ASICS, CLPD's and other massive logic arrays available from Xilinx and other fine companies, the 16L8 looks pretty small and archaic. True, this is some older technology, but it is from essentially the same era as the rest of this computer. My first encounter with the PAL was at Tandy Corporation, where there were 3 on the TRS-80 Model IV boards that we spent all day repairing assembly line failures. The PAL seemed like the weak chink in the armor, as they were first suspects in any board failure. I don't know why they failed so often, if it had something to do with the people running the gang programmers or what. My recent experiences with the PAL16L8 and 16R8 has been really good, though.
My eprom programmer uses the "Old Skool" fuse map method, you can either load a JEDEC of 'jed' file containing all the fuse on's and off's, or you can manually program the map from the software for the eprom programmer.
XILINX still has a program called "ABEL" which was used to create easy text files that identified the device pins and boolean formulas for each output. This was convenient and I'll leave that to your own research.
A JEDEC or 'jed' file contains text which gives the row for the PAL fuses as a program 'line'. Each line is considered a Boolean "term". The line (e.g. *L000000) is then followed by a series of 1's and 0's which define whether a fuse is retained or blown during programming. The act of programming a device is sometimes called "burning" the PAL. Once an older PAL is programmed, it can't be reprogrammed. So if you make a mistake, you can toss that one in the trash. Newer models of GAL/PAL can be 'flashed' which means they can be erased and re-written. Pretty cool!
Here's the first block of data from the PAL program for "4-bit conversion to 7-segment LED". The first line for each term, L000000 in this case, is the logic line that determines when the output for lines L000032 thru L000224 are enabled. You'll need to check the diagram to see what I mean here. Notice there is only one "0" in that line in my sample. It's in the second pair of bits which is connected to Pin 1 on the chip. This means Pin 1 is used as a "enable when low" for the rest of the term. Actually I use this pin for all terms, so it essentially makes Pin 1 of the PAL16L8 the \CE, or 'chip enable'.
*L000000 11101111111111111111111111111111
*L000032 10111011101110111111111111111111
*L000064 10111011101101111111111111111111
*L000096 10110111011101111111111111111111
*L000128 01110111101110111111111111111111
*L000160 00000000000000000000000000000000
*L000192 00000000000000000000000000000000
*L000224 00000000000000000000000000000000
This sample block controls the G segment (center segment of the LED display). G is off during the display of numbers 0,1,7, and C. So there are 4 terms which set these conditions TRUE. Notice in lines L000160-L000224 are all "00". We don't want to use those input rows to the overall OR-gated formula, so the inputs are set to "0". This means that row's AND gate will never go TRUE and trigger the output. They are effectively elimated from the formula, ignored.
Each set of binary digits is paired "11", the first digit is the input as it arrives at the PAL, the second is the inversion of that signal. This is ideal for detecting when an input becomes low. For example if /MREQ (a common active-low Z80 signal) is to be used as a term [Let's say you are using the PAL to decode various memory chips] you would want to use the inversion, or 2nd bit, so it would be read as true when /MREQ is at low coming into the PAL, eg. "10".
If a pair is set to "10", the input line must be "0" for the term to be true.
If a pair is set to "01", the input line must be "1" for the term to be true.
The easiest way I can think of to explain the 1's and 0's is to say that this sets the quiescent, or resting state of the PAL with no signals tied to the inputs. In this way, the 1's are pulled high into the final AND gate, and the zeroes are pulled low. The zeroes, however can be overcome, or driven to "1" with the external signal. A row in which all possible gates are tied to "1" are considered "True". A row with some zeroes must have each zero input term *driven* to 1 in order for the term become "True".
In the file where the PAL program is stored, the text appears (as you've seen) as 1's and 0's.
These files are called "JEDEC" or "JED" files for short. The term "JED" comes from the .jed file extension.
This is a text file and can be opened in most text editors.
In simplistic terms:
To initially 'design' the program for a PAL16L8, you'd print the graphic below for the logic array. Then you'd mark each term want to keep with an "X" at the intersection of the two lines. In my eprom programmer's editing software, these appear as "X" and "-" for 'keep' or 'remove'. However inside the JED text file that "X" is stored as a "0", and "-" stored as "1". I realize this seems confusing and I've tried to explain it best I can. Sorry I'm so wordy on these things.

One note of caution: Any row that is not used as a condition of the output MUST be set to all "0" (or "X") in order to keep the final AND gate for that row from going high and overcoming the other rows for that output. On the fuse map, you will have entire lines that appear as
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX".
These are inactive rows, and no formula is used to trigger that particular row. All inputs to the final AND gate at the end of that row are tied to ground, and that input term to that output's final OR game cannot ever go "true".
In fact, many 16L8's that I have downloaded from various machines and circuit boards just for grins (to see what makes that particular board 'tick' ) are set up only for a few certain conditions, and most of the IC goes largely unused. You may have 8 inputs and only 2 or 3 out pins used. And so in a program listing, you will see a lot of "XXXX"'s filling the unused condition rows. In the JED file, these appear as long rows of all "00000000"'s.
Next, the input pins on the 16L8 PAL and the bit-pair in that row line up as follows:
INPUT PIN 2,1,3,18,4,17,5,16,6,15,7,14,8,13,9,11.
Looking at L000000, you can see the first pair (pin 2) is not used, it's set to "11" or "--" in the program. The second pair, (pin 1) is set to "10". The input to this pin must be "0" for this row to become true. All the following pairs are all set to "11"; their inputs are not needed for this term.
To wit, I used Pin 1 as a chip enable. I use this condition is the same for each output gate. Therefore, Pin 1 must be grounded in order for the PAL outputs to enable.
Here's a diagram, courtesy TI, of their TIBPAL 16L8 programming array. You can see there is the first term which enables the output inverter, and 7 following Boolean "terms" which can be programmed.
You can save and print this one out if you need it, or download the PDF file from TI and print the appropriate page for the type of array you need. The TIBPAL 16R8 is similar in structure to the 16L8, but it has D-latch registers which can clock the logic conditions in and hold them. There are variants of these all over the place, some have 'programming cells' in the output stages which can be programmed to latch or not, invert or not and so on. As I mentioned earlier, many can now be flashed instead of burned, so the chip device is erasable - a great convenience for prototyping or designing.
Here's a sample PAL16L8 that I removed from a I/O controller board. The inputs on Pins 8, 9 and 11 are tied to DIP switches. See if you can figure out what it does! I did.
Sample PAL
Here's the program for a TIBPAL 16L8 that uses pin 1 as a chip enable, pins 2-5 as inputs A,B,C and D and 7 segment LED outputs g,f,e,d,c,b,a from 19 thru 13. Tie these to a ULN2003 or ULN2004 or ULN2803 and use the high current drivers to sink the segments on an LED display! Enjoy!
4-bit to 7-Segment LED
APPLICATION
The main point of this is to show how a PAL can be used as a decoder to display a 4-bit number as a Hexadecimal reference. Since the Space-Time Productions board uses a p8279 as it's keyboard / display interface, it follows that an application circuit where the PAL is used to create a numeric interface for the Z80 computer is needed.
This circuit requires an interface board to be assembled between the 8279 connector and the actual 7-segment displays. By utilizing the high-current capability of the ULN2803 (50V block, sink up to 500mA per input) you can drive a pretty hefty size display with it. You can use multiple LED's, or even incandescent lamps for the segments and make them as large as you like.
In this case the 8279 is set into a mode where the column lines are un-decoded, 8 bytes of display. The 74LS138 3-to-8 decoder IC is then used to create 8 columns of output. Since the output data byte is then broken into two 4-bit digits, you can display 16 digits at a time. This is all thanks to the PAL16L8, which decodes the 4 bits into 0-9,A-F. You may note in the schematic that I have used the first PAL input to indicate when the 1st digit is being strobed. In this way, I can surpress leading 0's by adjusting the PAL program.
Power to the columns is switched thru PNP transistors, which are gated on when their base is pulled low by the 74LS138. The display is then broken up into two rows of 8 digits. The yellow colored resistor networks limit the current thru the LED based on which displays you are using, and how bright you want them to be. For lower power applications, you could just as easily substitute a 74LS240 8 bit inverter for the ULN2803, watching the appropriate chip pinouts. This design is easily modified for your own uses. Another add-on might be to use a PAL input as the enable and some pins off the p8255 PIO to enable or disable a particular row of digits. A good example for this would be a racing timer, where you want to only display one lane until the 2nd place finishes the race.