© James R. Strickland 2016

James R. Strickland, Junk Box Arduino, 10.1007/978-1-4842-1425-1_6

6. TTL: The Missing Link

James R. Strickland

(1)Highlands Ranch, Colorado, USA

There are billions and billions of FETs in any modern desktop computer. But what do they do exactly? How do you get from unimaginable billions of transistors to computation?

You already know some bitwise logic. Transistors can do that.

Last chapter, we talked about NPN transistors with a load resistor on the collector. In this configuration, as the current is raised on the base, the voltage available at the node where the load resistor and the collector are connected drops. If that sounds like a logical NOT, as usual, you’ve got a good ear. Let’s work with that.

Imagine for a moment that you had a circuit like that in Figure 6-1. On the left are two NPN transistors , Q1 and Q2, with their collectors and bases tied together. The bases are wired to the positive bus via a large resistor. Call it 4kΩ.

A340964_1_En_6_Fig1_HTML.jpg
Figure 6-1. NAND Gate

If we ground both emitters, Q1 and Q2 both conduct, raising the base of Q3 so that it conducts, which raises point Y high.

If we raise the emitter of Q1 or Q2 close to the positive bus, that transistor ceases to conduct, as current no longer flows from its base to its emitter. The other continues, however. Q3’s base remains high, and Q3 goes on conducting, and point Y remains high.

If, however, we raise both emitters, it turns off both Q1 and Q2. Q3’s base goes low. It will stop conducting, and point Y gets pulled low. Let’s turn that into a truth table.

Table 6-1 shows the truth table of the circuit we’ve just created. If point A, the emitter of Q1, is high and point B, the emitter of Q2, is low, point Y, which is on the emitter of Q3, is pulled high by the transistor. The same thing happens if A is low and B is high. If both A and B are low, Q3 goes on conducting just fine, and Y remains high. But if A and B are both high, Y is pulled low by the emitter resistor to ground on Q3.

Table 6-1. Truth Table for the NAND Gate in Figure 6-1

Q1 Emitter

Q2 Emitter

Point Y

Low

Low

High

Low

High

High

High

Low

High

High

High

Low

In terms of logic, this is an AND of the inputs A and B, with a NOT applied to it. The logical name of this is a NAND, for NOT-AND.

If you got the Texas Instruments datasheet for the 74xx00 (available here: http://​www.​ti.​com.​cn/​cn/​lit/​ds/​symlink/​sn74ls00.​pdf) flip to page 3. You can see the rest of the schematic. It looks a little different. Q1 and Q2 are combined into a special tetrode transistor with two emitters. It does the same thing. The diodes protect the tetrode transistor by preventing the inputs from going lower than the circuit’s ground. The Q3 transistor, in turn, is tied to what’s called a totem pole output, which is an amplifier circuit. If you set out to build the gate the way it’s shown in that schematic, it should work.

Now imagine you need a lot of NAND gates. Consider that for each time we use AND or NAND in the Cestino, we’re processing eight bits at a time. Imagine etching all the components in the NAND circuit into a tiny piece of silicon, many times over, into an integrated circuit.

The datasheet gives it away. The 7400 is exactly that. It was released in 1964 by Texas Instruments. The rest is history.

The Stuff You Need

This project takes remarkably few parts, and all of them can come from the junk box.

New parts

None!

New or Used Parts

You will need some 74xx00 series logic ICs. Nearly anything in a 5v family will do, and most of the types you can plug into your breadboard are 5v. Some types are more interesting than others. If you’re buying new ones or borrowing from friends, try to get a 74xx00 and a 74xx92 or 74xx93, where XX is LS, or AHCI or F, or even nothing at all (the plain 7400, and 7492 will work fine.)

What TTL Is

TTL. There are those initials again. Didn’t it mean 0 to 5 volts, as opposed to RS232 levels which are + or - 12v? Yes, it did. Didn’t it have something to do with our 20Mhz full can TTL oscillator? Yes, it did. How does all of this tie together? What is TTL, anyway?

TTL stands for Transistor-Transistor Logic. TTL chips like the 7400 are designed to do two things. They implement one (or more) logic functions and they also have inputs designed to handle exactly the kind of signals their outputs produce, so you can interconnect them. For 7400 series logic , these signals run between 5V and 0V. The inputs require a certain amount of current to trigger them, and the outputs are capable of generating many times that much current, so multiple inputs can be connected to a single output pin.

There are other kinds of electronic and electromechanical logic . You can do logic with diodes, but the voltage and current drops limit how much you can chain together. You can do it with relays, but it’s not very fast, not power efficient, and it’s enormous. You can do it with vacuum tubes (valves, if you’re in the United Kingdom), and in fact, that’s how it was done in the earliest days of computers, but that’s a topic for a whole different book.

TTL is more than a type of IC , however. It’s the way those ICs communicate, the +5 volt to 0 volt communication. The timing. The lack of analog circuitry. I said that the 20Mhz full can oscillator that drives the Cestino is a TTL device, and it is. By nature, crystal oscillators produce a sine wave: a series of voltages that form a curve over the wavelength, with peaks and valleys of the specified voltage. That’s not what a TTL device wants to see, so a TTL oscillator adds output circuitry that turns on when the oscillator’s output goes above a certain voltage, and off when it goes below. The output circuitry further ensures that the positive and zero pulses are of equal length.

Not all oscillators are used to produce symmetric on and off pulses. In the datasheet for the full can TTL oscillator we’re using for the Cestino, this is expressed in terms of output symmetry. It should be 50 percent under typical conditions. Some datasheets may also refer to this as the duty cycle, which is the percentage of time in a full wave (one off and one on pulse) the output of the oscillator is on. We’ll revisit duty cycles, among other things, in Chapter 11.

The Extended 7400 Series Family

The family of logic we’re going to deal with most, because it’s the most common and I’m most familiar with it, is the 7400 series. This series takes its name from the 7400, which we just examined, but it’s enormous. The Wikipedia article at https://en.wikipedia.org/wiki/List_of_7400_series_integrated_circuits lists hundreds. All of them are input/output compatible. They use the same voltages, the same signaling conventions, (although whether they’re active high or low is a matter for the datasheet), and have similar speeds. They’re designed to be compatible with each other.

There is, of course, a catch. Quite a few catches, in fact. There is more than one 7400 TTL standard. Usually the whole family is referred to as the 74xx family for this reason. I mentioned in Chapter 1 that you needed a 5V Arduino to get through chapters 2 and 3. This is because the newest Arduinos are 3.3V TTL, and can only be connected safely to TTL operating at 3.3V. If you hook them to 5V TTL, they will fry. There are whole branches of the 74xx family (the 74LV and 74LVC types) designed to operate at lower voltages with such ICs, although you usually encounter them only in surface mount (SMD) packaging. They’re often 5V input compatible, but they want a Vcc that’s lower.

Also, different families of 7400 have different levels of current available at their outputs, and their trigger voltage thresholds are different, so a 74HC00, a high-speed CMOS version of the 7400, would have real trouble producing the current and voltage needed to drive its 7400 or 74LS00 ancestors. When you build with 7400 series logic, make sure the families you use are compatible with each other.

Types of TTL

7400 series TTLs come in a huge variety of types, each one a little piece of a larger piece of logic. The 7400 is a quad-two-input-NAND gate IC. It has four gates, each with two inputs and one output, and each gate has NAND functionality—the output goes to 0 (low) when both inputs are 1 (high).

There, many other types. Some have specific logic functions like the 7400 does, but many others implement some other small computing need. There are also variants with different input or output specifications, for interfacing with other ICs.

Some examples:

  • The 7402 is a quad 2-input NOR gate. Each output is 1 (high) only on when both inputs are low (0).

  • The 7403 is another quad 2-input NAND device like the 7400, but it has open collector outputs, meaning the output is tied to the collector of an internal NPN transistor. This requires the use of an external pullup resistor, but it allows the output pin to sink 25mA.

  • The 7404 is a hex inverter. Six logical NOTs. These are useful when you have an active low input that you’d like to trigger with an active high output. Or six of them, in this case.

There are also hex-inverting Schmidt triggers, which are mostly useful for following analog voltages and switching on at one voltage, but off at a different one. Like I said, very specific jobs.

The 7492 is a divider IC. Actually three of them in one package, although you can only access two. If you feed one of its inputs, it will divide your pulse count by two, turning on the output for every other pulse. If you tie its dividers together, it will divide by 12. You can use these ICs as counters as well, although it’s important to keep in mind what each stage is doing so the results aren’t unexpected. We’ll talk about this one at more length in the TTL Tester project section later in this chapter.

The 7493 is nearly identical to the 7492, except that it is a binary counter, will count from 0 to 15, and will produce correct binary values on its outputs. It can also be used as a divide by 16, divide by 8, or divide by 2, depending on how it’s wired.

There are buffers and line drivers, which simply repeat the signal that’s sent to them, but allow more current to be drawn or isolate the logic circuitry from whatever’s on the other end, and so on. There are shift registers, which allow one or more bits to be put in, and then bit-shifted up or down through the outputs. These are useful, for example, when you want to hold a specific binary value on the outputs, but you’re not in a hurry and don’t want to spend a lot of your controlling device’s pins on the input.

Latches also take a binary value in, but they take it in parallel and hold it until instructed not to.

There are even specialists like the 74141, which takes binary input and drives an ancient display device called a Nixie tube, a neon light–based device used before the advent of LEDs. Nixies have been obsolete a long time, so their dedicated driver devices are hard to find (look on eBay) and pricey. The 74143 and 74144 are similar devices for driving 7 segment LEDs. Given the limited current capabilities of some logic, it’s easy to understand why these specialists were necessary, but when it comes time to drive our own 7 segment LEDs in Chapter 10, we’ll do it with a Cestino port.

There are flip-flops, which allow you to set an output bit to a given state, reset it, or toggle it from one state to the other.

There’s even the 7483, a 4 bit full adder, meaning you can put a 4 bit binary in on each of its two inputs, as well as a carry input, and it will add the two numbers and give you the result. If you stack two of these together with the carry-output of one connected to the carry-input of the other, you can add eight bit numbers.

Whew. This is almost microprocessor stuff.

Actually, it’s part of a CPU. An adder of some bit-width, usually 16 bits for the earliest 8 bit processors, is a necessary part of any CPU, where it is used as the program counter: how the CPU keeps track of where it is in memory.

If you’re asking yourself if our nerd ancestors might have wired up whole CPUs out of TTL as a hobby, you’re asking the right question. Not only did they do it as a hobby, they did it in industry too. And when they did, the minicomputer was born.

Why TTL

74xx TTL was, and is, a big deal. In the 1960s and 1970s, when the 74xx family was first introduced, it allowed much more rapid prototyping, simply by allowing boards to be wire-wrapped together, or later using solderless breadboards like ours. Instead of taking days just building and testing a given logic gate, you pulled one out of the box and hooked it up.

What could you build with it? Well, with enough logic, you could build a whole CPU. Consider that for a moment. A CPU is more or less a group of logic functions bundled together that run in synchronous (usually) to an external clock pulse. Your program calls these logic functions in a particular sequence. A CPU, in the end, is logic. TTL made it possible to build CPUs smaller and faster than ever before. As I said earlier, thus was the minicomputer born.

Minicomputer didn’t mean what it means today. A minicomputer wasn’t a tiny board like the Raspberry Pi, or (arguably) the Arduino itself. These were decades away. Minicomputers were computers whose size was reduced to merely a few file cabinets from the room sized mainframes that preceded them, by the extensive use of TTL. They exploded onto the market, prying it away from the likes of IBM and other mainframe makers, who built their logic up from single transistors. TTL made that possible. If this seems like ancient history of no special relevance today, consider this:

Prior to the age of minicomputers, most programmers and analysts interacted with their machines with stacks of punch cards, or at best magnetic tapes . You coded up your project, handed the stack of punch cards or the tape to your friendly operator. When your time slot came up on the computer, they loaded your cards or tape, ran your job, and collected the output, either on another tape or on printouts. You might get it back tomorrow. You certainly got a bill. Mainframes and their human infrastructure were expensive.

The rise of minicomputers , by contrast, began the era that we in the 21st century would recognize. You talked to minicomputers with print and later video terminals. You ran your own programs, and you probably created them on the computer. In the late 1970s, your minicomputer might well have run Unix, ancestor to Linux, IOS, Android, and most modern, non-Windows operating systems. In a very real sense, our modern microcomputers (technically, computers built around microprocessors) are superpowerful, superfast minicomputers with graphical front ends.

By way of comparison, the minicomputer that served my entire undergraduate school was a VAX 11/750 . It ran at 3.125Mhz, and I don’t know how much memory it had, but certainly less than 20megabytes. Not gigabytes. Megabytes. Even its disk storage didn’t number in gigabytes. Next to the Mac I’m writing this on is a Raspberry Pi. Its CPU runs at 900Mhz, and it has a full gigabyte of RAM, and the SD card it uses for storage is an 8 gibibyte device. Nevertheless, it runs Linux, and can execute exactly the same kinds of programs that old VAX could. So can my Mac, orders of magnitude more powerful than the Pi, and so can my desktop Linux box. When microcomputers became powerful enough to run minicomputer software, the computer age we live in today was born, and all of that, all that we use today, came about because TTL ICs like the 7400 let you build logic circuits faster, easier, and cheaper than you could build them by hand. They’re still used today, still made today, in fact. they’re the “glue logic” that ties more complex ICs together.

So let’s take a look at some TTL. If you don’t have a 7400, or a 74LS00, or 74AHC00 or whatever, look the 74xx chips you do have up online and get their datasheets. You can use the lessons in this chapter to play with those chips, but you’ll have to work out what the chip does and how to drive it with the Cestino for yourself.

This brings us to the topic of datasheets.

How to Read a Datasheet

We’ve already touched on datasheets in the other chapters, starting at the beginning with the 372-page monstrosity that is the ATmega1284P datasheet . Even with simple LED projects, the datasheet has important information, but as we move forward, they become critical. In today’s marketplace, datasheets can be hard to come by. Some companies even require non-disclosure agreements to get them. Fortunately, the kinds of ICs we’re dealing with are either hobbyist-friendly, with available datasheets, or old enough to be part of the era when a datasheet was considered part of the marketing effort for a given IC. The Texas Instruments 74xx00 datasheet is one of the latter.

The first page of the datasheet tells us what the Texas Instruments name for this IC is: the SN7400. It also tells us that it applies to the SN5400, which is the military spec version of the same IC, as well as the SN54L00 and SN54S00 variants. In the 7400 version, this datasheet covers the SN7400, the SN74S00, and the 74LS00.

Next, it tells us what packages the SN54x00 and SN74x00 is available in: mostly DIPs like the one we’ll be looking at in this chapter, but also in the FK ceramic chip carrier, and the small outline PS package that only has two gates in an 8 pin DIP that I’ve never actually seen. Note especially that the pinouts differ between the SN5400 in the W package, lower left, and the package above it which includes the 7400 and 74LS00 version . All these things are important. It’s not likely you’ll fry the 7400 unless you get the Vcc and GND mixed up, but it won’t work unless it’s wired right.

Page 2 gives you ordering information and temperature tolerance information . If you’re designing satellites or other extreme environment equipment, this matters to you. For us, tinkering as we are at room temperature, any old 74x00 should be fine. Tape, tube, and reel packaging refer to how they’re shipped in bulk from the manufacturer. Useful if you’re running an assembly plant. Not so much if you’re tinkering with reused parts as we are.

Page 2 also shows the function, or truth table of the 74x00, and tells us the critical information that the 7400 is a positive logic chip. This means that for a pin to be on, or a logical 1, the voltage must be high. Negative logic chips exist. The distinction is important.

We’ve already seen page 3. If you’re wondering why they tell us how to build the gates in their IC from scratch, they don’t. Not really. What this is, is an electrically equivalent circuit, so that when we’re designing other circuits to connect to the inputs and outputs of the 74x00, we can plan for their current, voltage, and resistance needs.

Page 4 contains the data we’ve been skimming for in our LED datasheets: the absolute maximum ratings. These are the highest and lowest values a given pin can have and still operate the IC within its rated capacity. They don’t guarantee that the IC will fry if you exceed them, but they do promise that it shouldn’t fry if you don’t.

Under this are the recommended operating conditions. This section lists the supply voltage (Vcc) as between 4.75V and 5.25V for the 74xx00. You already know what that means. Note the high and low level input voltages. These refer to the voltages required to put the pin in a given logical state. For example, ((VsubIH)), calls out the logical high input level as two volts. Any signal above two volts will be considered logical high. Any signal below it may not be. Likewise, the ((VsubIL)) voltage is the voltage below which a signal will be considered logical low. It must be at or below 0.8v.

Below that are the current limits on the output pins. You’ve already seen this on the ATmega1284P’s pins , It’s a little confusing that the high level output current is -0.4mA. What this means is that the pin can source 0.4mA, or 400μA. It can sink 16mA to ground. By contrast, this is a lot lower than the 40mA the ATmega1284P can source or sink on each pin.

Below that and continuing onto page 4 are various operating parameters over temperature. The short version of all this is that the IC changes characteristics depending on its temperature. Also on page 5 is the switching speed . With a 400Ω, 15pF (Pico-Farad) load, the 74x00 described here will switch high in between 11 and 22 nS (nanoseconds) and low between 7 and 15 nS. How fast is that? Well, a 1MHz wave repeats every microsecond, and a nanosecond is 1/1000 of a microsecond, so if we divide 1000 by the switching speed in nanoseconds, we get MHz. 12 nanoseconds is about the middle of the value we can expect for switching both high and low, so 1000/12=about 83.3MHz. If we assume the worst case, 22nS, 1000/22=about 45.5MHz. Nothing we’ll be doing in this chapter will approach those speeds, but I would not expect a 74x00 to go any faster than 45.5MHz.

When you start chaining logic together, it’s tempting to think that things happen instantly. They don’t. It takes (worst case) 22nS for each gate in this IC to switch states. That’s not the only factor. Any time you have resistance and capacitance together, you have a time delay. A bad connection can go slower than you expect because the resistance goes up and by the way, a solderless breadboard can have significant capacitance too. Hopefully it’s clear now why I said to keep the clock line as short as possible and as close as possible to both the TTL oscillator and the ATmega1284P. We’re pushing the speed limits of breadboards a little.

Back on the datasheet, Note 4 is especially important. It says that all unused inputs must be held either to Vcc or GND to ensure proper operation. Using only one gate in the 74x00? Tie both inputs high or low, otherwise the outputs may go high or low, or may oscillate. Worst case, it can damage the IC.

The rest of the information on page 5 and on to page 6 repeats the same data we’ve had, but in different test scenarios—different operating temperatures, different loads, different versions of the IC, and so on.

Page 7 shows the schematics by which the test data was achieved, and timing diagrams . The important fact to note here is that while we think of TTL as being either on or off, there’s time in between, where the voltage is rising or falling, and if you’re designing a timing-critical circuit, we need to know that.

Don’t worry. We’re not. The circuits we’ll be building in this chapter don’t get out of the kilohertz range. For us, 22nS is instant enough.

The rest of the datasheet is ordering information, a list of all the varieties the IC comes in, (There’s a space-qualified version, the SN54LS00-SP . I wonder if you can get those on EBay.) bulk packaging information, exact mechanical drawings of the IC in various packages, thermal information, so you don’t fry ICs by overheating them while wave-soldering them to boards, and a big, fat disclaimer that says not to use these ICs in life support equipment without a special agreement with TI, and so forth and so on.

Datasheets can be dense. They’re written by electronic engineers for electronic engineers, and as they promise specific performance of a device in specific circumstances, they’re quasi-legal documents as well. Some are better than others. TI’s, in addition to having friendly licensing terms, are also well written. Some, particularly for low-cost components originating in non-English speaking countries, can be truly challenging to decipher. Fortunately, the 74xx series TTL family has been around for decades, and versions are made by a number of corporations around the world. If you find the Fairchild Semiconductor datasheet incomprehensible, you can get the datasheet for the TI version. The performance of a given IC (say, a 74LS00) will be similar enough across versions to keep you out of trouble. By contrast, if you find the ATmega1284P’s datasheet a tough read (it’s not, it’s just big) you’re stuck with it, as only Atmel makes the ATmega line of microcontrollers.

How to Read Your IC

There’s a wealth of information in the markings on an IC. If you’re over a certain age, you may need a magnifying glass or very good light (or both) to read it, but an IC should always have its number, such as 74LS00. There may be letters and numbers before or after the main IC number, which tell you things like what kind of package the IC is in, whether it’s got special ratings or capabilities, and so on. These extra numbers and letters vary by manufacturer, so if you need extra capabilities, it’s important to find the manufacturer’s trademark or name on the IC and get their specific datasheet for it. There’s an excellent website for identifying these often cryptic, often outdated marks here:

http://how-to.wikia.com/wiki/How_to_identify_integrated_circuit_(chip)_manufacturers_by_their_logos

Often, but not always, there is a date code, and this usually takes the form of YYWW, the last two digits of the year, followed by the two-digit week number of that year. So 1412, the date stamp on one of my ATmega1284Ps, would mean it was made in the twelfth week—the middle of March—2014. Not all ICs have date codes. Some may have date codes relative to an arbitrary event, such as a ruler’s coronation.

Finally, there’s a notch at one end, just like there was with the ATmega1284P. That tells you which end pin 1 is on. If there’s not a notch, pin 1 will have a spot in the top of the package over it, or perhaps a paint mark, or some other clear indication that pin 1 is here. This is important. TTLs really don’t like having their power backward.

Build the TTL Explorer

This project is going to be a little different from most of the others. I don’t know what 7400 series logic you have in your junk box. We’ll be working out of mine, and I’ll talk about specific strategies for poking at each IC, with respect to what it can do, and showing output. If your logic ICs are different, you’ll have to work up your own sketches, but hopefully you’ll find my examples helpful.

74xx00

Let’s start with the 74xx00 quad two-input NAND , from which the whole family takes its name. As you might be able to see in Figure 6-2 below, mine’s a 74LS00, made in the 48th week of 1981 by Fairchild in Singapore. At that time, Fairchild Camera and Instrument (now known as Fairchild Semiconductor) was owned by Schlumberger Limited, an oilfield management company. Go figure. We’ll go ahead and use the Texas Instruments datasheet . If we were pushing toward the specification limits of the 74LS00 in any way, we’d want to work from the Fairchild sheet, but we’re not, and the TI datasheet is nicer.

A340964_1_En_6_Fig2_HTML.jpg
Figure 6-2. 74xx00 TTL Explorer

Suppose that you wanted to wire the ATmega1284P to allow hardware resets only when the software is ready for one? Instead of tying the reset button to the - bus, and to the reset pin on the ATmega, you could tie it to the + bus, and connect it to one input of a NAND gate in a 74LS00. You could then tie the other input of the NAND gate to pin 1, aka pin B0, where the LED is connected, and tie the output to the reset line on the ATmega. The result would be that when pin B0 is off, pushing the reset button does nothing. The output stays high. Turning pin B0 on also does nothing. The output stays high. Only if you press the button AND pin B0 is high does the output go low and reset the ATmega. This is the kind of job you do with a 7400. Simple jobs. Little jobs. It does, after all, take two of them to output a complete byte.

Nevertheless, let’s hook this one up and throw bit patterns at it, and watch it actually do what the datasheet says it should do.

Figure 6-3 shows how to wire up your 74xx00, hereafter referred to as the LS00. Note that the pins in the schematic are out of order so the logic gates can be in order. When you’re wiring your 74xx00 up, make sure you work from the right pinout diagram in the datasheet. It’s the one on the top left of the first page of the datasheet. Make sure the pin 1 notch or spot is at the end of the LS00 closest to the edge of the breadboard, the same as the ATmega1284P’s is.

A340964_1_En_6_Fig3_HTML.jpg
Figure 6-3. 74xx00 TTL Explorer Schematic

Wire pin A0 (pin 40 on the Atmega1284P) to pin 1 of the LS00. A1 goes to pin 2 of the LS00. These two outputs are connected to the inputs of the first NAND gate on the LS00. Gate 1’s output is on pin 3 of the LS00, so wire that to pin C0 (pin 22) of the ATmega. The next gate is the same way. Wire A2 and A3 to pins 4 and 5 of the LS00, and pin C1 to pin 6.

For reasons known only to the original designers, the other side of the LS00 is essentially backward from this one. Wire pin 8 to C2 on the Cestino. It’s the output of Gate 3. Then wire A4 to pin 9 and A5 to pin 10 to control the gate. Likewise, pin 11 of the LS00 is the output of Gate 4. Wire it to pin C3 on the ATmega, and wire pins 12 and 13 to A6 and A7, respectively.

Don’t forget power and ground. Power (Vcc) is on pin 14 of the LS00. Tie this to the + bus. Ground (GND) is on pin 7. Wire this to the - bus.

That’s all there is to it.

The code for this project is about as simple as it comes. If it looks like I derived it from the Binary Numbers on Display code in chapter 4, again, you’ve got a good eye. Cycling through all the values between 0 and 256 is a good way to get all the possible permutations of inputs for a device.

#define DELAYTIME 10

The first thing we do, once we wade through all my comments, is set DELAYTIME to 10ms. Skimming through the datasheet, you may recall that this IC is specced to respond in less than 22ns (nanoseconds). Why a delay of more than 10,000 times that? It makes it easier for humans to read.

Next comes a function. It’s not strictly necessary for this sketch, and if you don’t want to use it, you don’t have to, but I find it annoying to read binary output where the length of the variable changes constantly. Particularly when you’re comparing bit fields, it’s very hard (at least for me) to keep straight what bit I’m looking at, when I may have 10 on one line and 10000001 on the next. This function returns a String object which contains the full representation of all the bits passed to it in the byte, and prepends a 0b, which by now we’re all getting used to as the way the GCC compiler represents binary numbers. Let’s dig into how it works.

String zerobee(byte input) {
  String temp = "";

The first thing we do after declaring the function is to initialize the string we’re building to an empty string. This is very, very important, because we never explicitly set the string. We only add characters to it. If it contains garbage to begin with, nothing in this code will change that. Initializing it makes sure that it doesn’t.

for (int c = 0; c <= 7; c++) {
    if (input % 2) {
      temp = "1" + temp;
    } else {
      temp = "0" + temp;
    }
    input = input / 2;
  }

This code is where the work gets done. It iterates from 0 to 7. Each time, it checks to see if the input byte divided by 2 has a modulus, or a remainder, using the modulus operator. By now, my habit of commingling integer values of zero as false and not zero as true is probably very familiar. I’m doing it here, too. If there is a modulus, we prepend the character “1” to temp. If there isn’t, we prepend a “0”. Then and only then do we actually divide the input by 2, and the loop iterates again.

  temp = "0b" + temp;
  return (temp);
}

Once we exit from the loop, we prepend “0b” onto temp, and return the String to the calling function.

Serious microcontroller programmers are screaming right now. Do I know how many cycles that much division chews up? Do I know how inefficient that is?

Yes. I do know. Division is very expensive, and we’re doing sixteen of them every time we call the function. But the truth is, this is not a high speed, memory constrained environment. The huge capacity of the ATmega1284P (compared to the ATmega328 in most 5v Arduinos) gives us the luxury of wasting a few cycles and some flash space in order to make things easier to read. So that’s what we’re doing.

Onward.

void setup() {
  Serial.begin(115200);
  DDRA = 0b11111111;
  DDRC = 0b11110000;
  PORTA = 0;
  PORTC = 0b00001111;
  Serial.println("Starting...");
}

This code is, by now, pretty familiar. It sets the console to 115200 baud, sets port A up to write with all pins, and sets port C up to read with its first four pins. Why not use all 8 pins? We’re displaying the full width of PINC, and if we leave those pins unconnected, their values are unknown. We’ll mask the high order bits out when we print them later.

We initialize PORTA to 0, and set the internal pull-up resistors on the lowest four pins of PORTC.

Why pull-up resistors? It’s always nice to have your inputs in a known state, in this case on. Pull-up resistors are unnecessary on a totem pole output like the ones on this 74LS00, but they don’t hurt anything. (A totem-pole output is a push-pull amplifier circuit with a PNP and an NPN transistor forming a voltage divider, and the output between them.)

The loop function is where the action is, and there isn’t much to it.

void loop() {
  Serial.print("Port A:"+zerobee(PORTA));
  Serial.println(" PORTC:"+zerobee(PINC));
  PORTA++;
  delay(DELAYTIME);


  if (PORTA == 0) {
    while (true) {}
  }
}

Remember that the loop() function , shown here, is called over and over by the Arduino core, so just like we did with the binary numbers on display sketch, we increment the port’s value until it hits zero. Yes, it is initialized at zero. Look where the PORTA++ is. It will never, ever reach the test where we check to see if PORTA is 0 with the initialized value. The only way it will ever pass that test is when the iteration begins with PORTA at 255 and it increments it. As a single byte value, PORTA rolls over to zero, passes the test, and the while(true){} loop runs forever, doing nothing.

So what does the output of this sketch look like? Well, if you have your own 74xx00, it’s more fun to run the sketch. If you don’t, here’s the trimmed version of my output.

Starting...
Port A:0b00000001       PORTC:0b00001111
Port A:0b00000010       PORTC:0b00001111
Port A:0b00000011       PORTC:0b00001110

Notice what just happened. Pins A0 and A1 are both 1, so gate 1 of the LS00 goes low, outputting zero. Our input value is 3. This pattern repeats further down.

Port A:0b00001011       PORTC:0b00001110
Port A:0b00001100       PORTC:0b00001101
Port A:0b00001101       PORTC:0b00001101

You can see gate 2 going low when PORTA hits 12. Notice how it stays low through the entire subsequent range of numbers as long as bits 8 and 4 are turned on.

Port A:0b00110000       PORTC:0b00001011
Port A:0b00110001       PORTC:0b00001011

Gate 3 is rolling over as we hit 48.

Port A:0b10111111       PORTC:0b00001000
Port A:0b11000000       PORTC:0b00000111
Port A:0b11000001       PORTC:0b00000111

As expected, gate 4 goes low when we reach 192.

Port A:0b11111111       PORTC:0b00000000

And here, at 255, all the gates finally go low at the same time.

Here’s the full listing of the sketch, including comments.

//TTL_Explorer_74xx00
//----------------------------------------------------------
//This sketch counts on PORTA from 0 to 255, sending those
//values to a 74xx00 connected as described below. It then
//reads the 74xx00's output pins with the first four bits
//of PORTC. Both values are displayed.
// ---------------------------------------------------------
//Hardware:
//74xx00, any flavor (mine's an LS)
//
// Wiring:
// Outputs are wired to PORTC, in order, lowest to highest.
// Inputs are wired to PORTA, in order, lowest to highest.
//Cestino Pin   74xx00  Cestino Pin
//(Pin 40) A0   1   14  Vcc (+ Bus)
//         A1   2   13  A7
//         C0   3   12  A6
//         A2   4   11  C3
//         A3   5   10  A5
//         C1   6   9   A4
// (- Bus) GND  7   8   C2


// ----------------------------------------------------------
// James R. Strickland
// ----------------------------------------------------------
//precompiler definitions.
#define DELAYTIME 10 //How many mS (milliseconds) per update?


//-----------------------------------------------------------
//Zerobee
//-----------------------------------------------------------
//This function returns a string with the binary equivilent of
//the value passed to it in input expressed in 0bxxxxxxxx
//notation, with 8 bits for each byte regardless of their value.
//IMHO this makes binary values easier to read and compare.
//-----------------------------------------------------------
String zerobee(byte input) {
  String temp = "";
  for (int c = 0; c <= 7; c++) {
    if (input % 2) {
      temp = "1" + temp;
    } else {
      temp = "0" + temp;
    }
    input = input / 2;
  }
  temp = "0b" + temp;
  return (temp);
}


//setup() function - runs only once.
void setup() {
  Serial.begin(115200);
  DDRA = 0b11111111; //Set DDRA to all outputs.
  DDRC = 0b11110000; //Set DDRC so pins C0-C3 are inputs.
  PORTA = 0;     //Initalize PORTA to 1.
  PORTC = 0b00001111; //Set the pullups on bits C0-C3
  Serial.println("Starting...");
}


//loop() function - runs forever.
void loop() {
  Serial.print("Port A:"+zerobee(PORTA));
  Serial.println(" PORTC:"+zerobee(PINC));
  PORTA++;
  delay(DELAYTIME); //Wait DELAYTIME milliseconds.


  if (PORTA == 0) { //If PORTA reaches zero, loop forever.
    while (true) {}
  } //otherwise let loop() execute again. And again. And again.
}

74xx92

The next IC I’m going to demonstrate is the 74xx92, in this case a 74LS92N . As you might be able to see in Figure 6-4, mine was made by Sygnetics, part of Phillips Semiconductor, now known as NXP, apparently manufactured in January of 1978. Where I got it, I have no earthly idea. It doesn’t appear to be unsoldered from anything, so it was either out of some old junk where it was socketed, or it was a leftover from another project and sat on a distributor’s shelf for a few decades.

A340964_1_En_6_Fig4_HTML.jpg
Figure 6-4. 74xx92 TTL Explorer

The 74LS92N is a counter, or more precisely a divide by 12 device. It does not produce normal BCD (binary coded decimal) values, because the most significant bit is a 6 bit, not an 8 bit.

Looking at the datasheet for this IC (Futurelec has the Motorola version here: http://www.futurlec.com/74LS/74LS92.shtml ) the first thing to note is that this sheet covers three different counters, the 74LS90, 92, and 93, which are a decade counter (divide by 10), divide by 12 counter, and a 4 bit binary counter, respectively. If you have the 90 or the 93, you can still use this code and mostly the same wiring, but your output will be different.

If you scroll down to the truth table for the LS92, you can see that, from a count of 0 to 5, it produces normal BCD values on output pins Q0 through Q3. Once the count reaches 6, however, everything becomes weird. If it were outputting BCD values, it would count from 0 to 5, skip 6 and 7, and continue at 8.

You should also note that the divide by 2 counter (which outputs to Q0) is not internally connected to the divide by 6 counter. You have to wire them together externally. This is so that you can use the counters separately. If, for example, you wanted to take our 20MHz clock and get a 10MHz clock from it, you could connect pin /CLK0 to the clock. According to the absolute maximum ratings, the 74LS92 can handle signals up to 32MHz on the /CLK0 pin, so we’re good there. If we connect the Q0 output to the /CLK1 input, we feed the frequency divided by two to the divide by six counter. If we wanted 10MHz, we could pick it off the Q0 output. If we wanted roughly 1.67MHz, we could pick it off the Q4 output—20MHz/12. And so on. Note that /CLK0 and /CP0 are different notations for the same pin. /CLK1 and /CP1 are the same as well.

But enough talk. Let’s wire it up and get it counting.

As you can see from the schematic in Figure 6-5, we’ll use PORTA to control the 74LS92, and PORTC to read it, mostly because they’re conveniently located. Note that the pins on the 74LS92 are not shown in order once again, and that the connection between pin 12 and pin 1 does not also connect to pin 14.

A340964_1_En_6_Fig5_HTML.jpg
Figure 6-5. 74LS92 TTL Explorer

Wire pin A0 (pin 40) to pin 14 of the 74LS92 (hereafter referred to as it is in the datasheet, the LS92.) Pin A1 goes to pin 6 on the LS92, and pin A2 goes to pin 7 on the LS92. These lines are the clock or input line, and the reset lines, respectively. These lines control the LS92, but they don’t read its output.

To read the output, wire pin C0 (that’s pin 22 on the ATmega1284P) to pin 12 on the LS92, C1 to pin 11, C2 to pin 9, and C3 (pin 25 on the ATmega1284P) to pin 8 on the LS92. Then on the LS92, wire pin 12 to pin 1. Finally, on the LS92, wire pin 5 to the + bus, and pin 10 to the GND bus . TTL isn’t magic. It needs power too.

The code itself is fairly straightforward. We start by defining delaytime at 100ms.

#define DELAYTIME 100

Our setup, too, is basically boilerplate. We set the console to 115200 baud, set DDRA so that PORTA is writing with all pins, set DDRC so PORTC is reading with all pins. We also set the pull-up resistors on the first 4 pins of PORTC.

void setup() {
  Serial.begin(115200);
  DDRA = 0b11111111;
  DDRC = 0b00000000;
  PORTC = 0b00001111;
  Serial.println("Starting...");
}

In the loop function , we begin by setting PORTA so that the reset pins of the LS92 are both high, resetting it. Then hold them both low, getting the LS92 ready to count. We print that so the user can see it, using the zerobee function (not shown—it’s the same as in the 7400 demo.) Every time we call PINC, we AND it with 0b1111, that is, 0b00001111. Because pins 5-8 of PORTC are unconnected, we don’t want to look at their output.

void loop() {
  PORTA = 0b00000110;
  Serial.println("Holding reset pins high. PORTA:" + zerobee(PORTA));
  Serial.println("PIN C should be all zeros. PINC:" + zerobee(0b1111 & PINC) + " ");
  PORTA = 0b00000000;
  Serial.println(" Reset lines now low. PORTA:" + zerobee(PORTA));
  Serial.println("PIN C should still be 0. PINC:" + zerobee(0b1111 & PINC) + " ");

After that, we run a loop with 24 steps. Each cycle delays DELAYTIME ms, so we can read the output, and on negative steps (as ∼CP0 is active low) we display the value of PINC , which is reading the output pins of the LS92.

Serial.println("
Giving the ∼CP0 pin 12 negative pulses");
  for (int c = 1; c <= 24; c++) {
    PORTA = (PORTA + 1 & 0b00000001);
    if (PORTA == 0) {
      Serial.print("Pulse " + (String) (c / 2) );
      Serial.println(" PIN C: " + zerobee(0b1111 & PINC));
    }
    delay(DELAYTIME);
  }

After that, we loop forever so the whole thing doesn’t run again. As before, it’s more fun to watch the patterns emerge in your own console window. If you don’t have a 74xx92 of your own, however, you can follow along in my output. It’s here in its entirety, as it’s short.

Starting...
Holding both reset pins high. PORTA:0b00000110
PIN C should be all zeros. PINC:0b00000000      


Reset lines now low. PORTA:0b00000000
PIN C should still be 0. PINC:0b00000000      

In the previous section, we reset the 74LS92. Remember the use case we talked about in the 74xx00, where I talked about having a software-enabled reset for the ATmega? That’s pretty much what the reset lines for the LS92 do. Why, you ask? Sometimes you don’t want the counter to go all the way to 12. You could wire those two inputs to any of outputs of the LS92 and chose any value with two bits turned on to reset at. In our case we’re controlling them with the ATmega.

Next, we pulse Cestino pin A0 12 times to give the counter some pulses to count. Remember that the CP0 pin on the LS92 is active low, so the counter is counting negative pulses.

Giving the CP0 pin 12 negative pulses

Pulse 1        PIN C: 0b00000001
Pulse 2        PIN C: 0b00000010
Pulse 3        PIN C: 0b00000011
Pulse 4        PIN C: 0b00000100
Pulse 5        PIN C: 0b00000101

Everything is normal through this point, but look what happens below. What should be our 8 pin (bit 4) is really representing 6, so it turns on at pulse 6. This is not normal binary. It’s very important to keep that in mind when reading the output.

Pulse 6        PIN C: 0b00001000
Pulse 7        PIN C: 0b00001001
Pulse 8        PIN C: 0b00001010
Pulse 9        PIN C: 0b00001011
Pulse 10       PIN C: 0b00001100
Pulse 11       PIN C: 0b00001101

Once again, you can see that bit 4 represents 6 and not 8, and makes our output some strange reading. The timer rolls over at 12.

Pulse 12        PIN C: 0b00000000

Here’s the sketch in its entirety.

//TTL_Explorer_74xx92
//----------------------------------------------------------
//This sketch raises the reset lines of the 74xx92 to reset
//it, then holds them low. It then generates 12 negative
//pulses on CP0 (pin 14 of the xx92), delaying DELAYTIME ms
//in both the on and off states. In off states, it reads
//PINC for the results, formats them with zerobee binary
//pretty printer, and sends them up to the console.
// ---------------------------------------------------------
//Hardware:
//74xx92, any flavor (mine's an LS)
// PORTA0 (pin 40) to pin14
// PORTA1 to pin6
// PORTA2 to pin7
// PORTC0 to pin12
// PORTC1 to pin 11
// PORTC2 to pin 10
// PORTC3 to pin 9
// Pin12 to Pin1
// Pin 5 to + bus
// Pin 10 to - bus
// Note that pin Q3 is outputting /6/ and not /8/
// ----------------------------------------------------------
// James R. Strickland
// ----------------------------------------------------------


//precompiler definitions.
#define DELAYTIME 100


//-----------------------------------------------------------
//Zerobee
//-----------------------------------------------------------
//This function returns a string with the binary equivilent of
//the value passed to it in input expressed in 0bxxxxxxxx
//notation, with 8 bits for each byte regardless of their value.
//IMHO this makes binary values easier to read and compare.
//-----------------------------------------------------------
String zerobee(byte input) {
  String temp = "";
  for (int c = 0; c <= 7; c++) {
    if (input % 2) {
      temp = "1" + temp;
    } else {
      temp = "0" + temp;
    }
    input = input / 2;
  }
  temp = "0b" + temp;
  return (temp);
}


//setup() function - runs only once.
void setup() {
  Serial.begin(115200);
  DDRA = 0b11111111; //Set DDRA to all outputs.
  DDRC = 0b00000000; //Set DDRC to all inputs.
  PORTC = 0b00001111; //Turn C0 to C4 pullups on
  Serial.println("Starting...");
}


//loop() function - runs forever.
void loop() {
 PORTA = 0b00000110;
 Serial.println("Holding both reset pins high. PORTA:" + zerobee(PORTA));
 Serial.println("PIN C should be all zeros. PINC:" + zerobee(0b1111 & PINC) + " ");
  PORTA = 0b00000000;
  Serial.println(" Reset lines now low. PORTA:" + zerobee(PORTA));
  Serial.println("PIN C should still be 0. PINC:" + zerobee(0b1111 & PINC) + " ");
  Serial.println(" Giving the CP0 pin 12 negative pulses");
  for (int c = 1; c <= 24; c++) {
    PORTA = (PORTA + 1 & 0b00000001);
    if (PORTA == 0) {
      Serial.print("Pulse " + (String) (c / 2) );
      Serial.println(" PIN C: " + zerobee(0b1111 & PINC));
    }
    delay(DELAYTIME); //Delay DELAYTIME ms for each on or off.
  }


  while (true) {} //Loop forever so we don't run again.
}

Credit Where Credit’s Due

A long time ago now, in a musty basement closet-turned-lab that belonged to the physics department of Concordia College, I needed a device that would take two pulsed inputs, count them, and display the count on seven-segment LEDs. Sounds like a job for 7400 series TTL? You bet. It was then that I had my first lessons in 7400 series logic, from a hardcore nerd and genius I’ll call Gene (it was his name). That’s where it started. I didn’t get very far, and I got distracted by other things (graduation, dating, etc) but that’s where it started. I should mention that the source of the pulses was a rat dropping a marble between an LED and a phototransistor, by way of a somewhat makeshift basket. There were two of these circuits. It was a scoreboard for rat basketball. Seriously. It’s a long story, but it involved four 7 segment drivers, two 74LS393s, and some other parts I no longer remember.

Ultimately I had to pick up TTL again for myself, and the inspiration, when the time was finally right, was this guy, madmaxx, on youtube: https://www.youtube.com/watch?v=bCVT1BtlZn0 , followed shortly by Quinn Dunki’s Blondihacks blog, here: http://quinndunki.com/blondihacks/ , and EEVblog, here: https://www.youtube.com/user/EEVblog . You’ll see Blondihacks again in the credit where credit’s due section of another chapter.

Further—More Chips, More Pins, Automatic Configuration

So where could this project go? It’s conceivable that with a very large sketch, by hooking up all the pins of the 74xx00 series IC to a Cestino port, even power and ground, you could build a tester that would test a very large number of different 74xx00 series ICs.

This isn’t as pointless as it sounds. A couple of years ago, I built my second computer from ICs and bare boards, a PC clone. It was starting to show functionality when I got a piece of steel wool on my work bench across the power supply lines and (presumably) shorted the 12v supply to the 5v rail. In addition to nearly starting a fire, this wiped out a bunch of the logic on the board. Being able to test the ICs instead of shotgun replacing them all (I have another device that could test most of them) saved me quite a lot of money.

As for going further with TTL, the sky is more or less the limit, so long as you’re not in too much of a hurry. People can and do still make up their own CPUs from scratch, starting with nothing but TTL and a plan. There’s a whole web-ring of people who’ve done it (and more—some have built CPUs up from discrete components like individual transistors.) The ring home is here: http://members.iinet.net.au/~daveb/simplex/ringhome.html .

Dr. Harry Porter (and others) have even done it with relays. http://web.cecs.pdx.edu/~harry/Relay/ (If you speed the sound of Dr. Porter’s computer running up in your mind, it’s easy to imagine it as the sound of the computer in the original Star Trek. I always wonder if there’s a story there, and what that original sound effect was a recording of.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.75.165