In this page, I'll explain how to control the AC line and relays, input and output voltage levels, and other similar things with the parallel port. If you are not familiar with the parallel port, and with the program DEBUG to control it, please refer to DEBUG and the Parallel Port.
The outputs of the (original) parallel port were specified to be able to drive one LSTTL input. This implies 20 μA output in the high state, and 0.4 mA input in the low state. Many more recent ports are stronger than this, but you should test to make sure. The parallel port should supply no current itself, and should not be exposed to voltages outside the range 0 to +5 V. From these properties, we see that the parallel port must almost always be buffered if it is expected to do any real work.
The easiest interface of all is one to give ON-OFF control of the AC power line. One can use what is called a solid-state relay for this purpose. A typical one takes 3 - 30 V DC input with very little current, and controls 6 A at 140 V. The two sides are completely isolated, with two terminals each. The DC must be applied with the proper polarity. A positive voltage turns the device ON. When the device is off, there is a small leakage current, so the AC is not completely interrupted. This seldom causes any problem, but should be kept in mind. All that is required is the relay itself.
It is important to keep in mind safety considerations when using the AC line. The two wires are called line (L) or "hot," and common or "identified." The line wire often has black insulation, and the common white. On zip cord, the line has smooth insulation, while the common is ridged. On a polarized plug, the wide prong is the common. The common wire should be continuous back to the point where it is grounded at the service entrance, and switches put in the line only. AC components should be put into the nonconducting wiring boxes that are easily available. Any contact between conductors and any exposed metal surface should be strictly avoided. When there are exposed metal surfaces, they should be grounded to the green wire of a 3-prong plug, which is a ground connection. The common wire should never be used instead (the plug may be reversed, or the wiring installed improperly). The grounding of one side of the circuit means that the line wire is always "hot" with respect to any grounded object, including water and the ground. This system is intended to open a protective device (fuse) if the insulation of the hot wire fails at any point and contacts the grounds that surround it. This prevents fire, but not shock. Take care not to ground yourself in any way when working with the AC line. There should be no metallic connection between the AC line and the parallel port.
For anything except a solid-state relay, you will need an interface buffer. There are many choices, but the 2N4401 NPN transistor, the MPSA13 NPN Darlington, and the 74LS05 open-collector inverter, will handle practically any case. The first thing to do is to see just how much the parallel port can do using a high (1) state to turn the interface ON. When the computer comes up from cold, the pins usually assume a noncommittal 1 state (some of the control pins, however, come up 0 since their logic levels are inverted. This is true of pin 1 on the DB-25 connector, for example. We would really like for the interface to be OFF in this case, and we'll see how to arrange this later.
First of all, the port can certainly drive a 74LS05. The output can sink up to 8 mA (without coming above 0.2 V). This increases the "sinking" power of the parallel port by 20 times! Since the LS05 is open-collector, it cannot supply any current in the high state, and acts like an open circuit. This is what we usually desire. If we are interfacing to digital logic, and not a transistor, then the 74LS04 is the correct choice. The LS04 can supply up to 0.4 mA in the high state. This is not much, but is a lot more than the port is rated to do on its own.
On test, my port successfully drove a 2N4401 transistor supplying about 100 mA, with a collector voltage of 0.22 V. This is well beyond the call of duty, but shows what can happen with a good port and a good transistor. More conservative is the use of a Darlington transistor, which requires very little base drive, but a base voltage above 1.5 V. The MPSA13 delivered 85 mA with a collector voltage of 0.76 V. Darlingtons will not pull lower than this, so they are not suitable for driving logic. The simple circuits for testing the port are shown at the right. The 50Ω 1/2 W resistor is two 100Ω, 1/4 W resistors in parallel. At 100 mA, they will be dissipating close to their limits, and may get warm. Both the 2N4401 and MPSA13 are based EBC, as shown looking at the flat side.
A relay with a 120 V coil can be driven with a triac optoisolator, itself driven by a transistor, as shown at the left. A triac optoisolator consists of a GaAs IR LED and a light-triggered triac switch. The triac switch conducts in both directions when it is triggered, so it can control an AC circuit. Conduction ceases when the current passes through zero twice in each cycle. Triacs will not turn off if you try to use them on DC. Some triacs are made to switch only at these zero-crossing points, which avoids inductive effects. The triac optoisolator I used was an H11J3, a very typical type that should be easily available, if not under this number. It comes in a 6-pin DIP. The LED has a maximum forward current of 60 mA, though 10 mA is guaranteed to trigger the triac. The forward voltage is 1.2 V maximum, and the reverse voltage 3 V. The maximum blocking voltage of the triac is 250 V, and the maximum current is 100 mA rms. This current is not enough to do anything strong, but can operate a relay or trigger a larger triac. The isolation between the LED and the triac is 3200 V peak.
The transistor should be selected for high beta at the ON current. Contrary to common belief, not all NPN TO-92 transistors are alike. Their current gain is optimized for the collector current at which they are to be used. The 2N4124 has a beta of 120 minimum at 2 mA, the 2N3904 at 10 mA. Both are commonly used, and are very good transistors, but not for the present purpose. The PN2222A, 2N2907 and 2N4401 guarantee a beta of 100 at 150 mA, so any one of these is suitable for driving relays and other heavy-duty interfacing. Their beta may be as high as 360, so we are safe in assuming a beta of 100 for our transistors. A collector current of 100 mA requires, then, a base drive of 1 mA to bring the transistor to the edge of saturation. By increasing the base drive, the transistor is brought more solidly into saturation. The actual ratio of collector to base current is called the forced beta, and a satisfactory value in the present case is 25. Therefore, for 100 mA collector current, we should arrange for 4 mA of base current.
Although we have just seen that the parallel port can drive transistor bases directly, it is more conservative not to rely on this, and use the greater "sinking" power instead. The transistor is turned on in the normal state by base current supplied by a resistor to +5. A high level from the port does not disturb this, so an output high level gives a low level at the transistor collector. When the port goes low, it robs all the base current and the transistor turns OFF.
Since the port can sink 0.4 mA, this is enough for a guaranteed 10 mA output current. An LS05 gives us 8 mA base current, or 200 mA output current (this is about as much as a TO-92 transistor should have to deal with, but a 2N4401 will do it without difficulty). The base pullup resistor to +5 is 12k in the first case, 620Ω in the second. As we have seen, ports and transistors may surprise us a little with their capacities.
A relay is an excellent device for controlling the AC line. It gives complete isolation to the AC circuits, as well as complete disconnection on one hand and low-resistance connection on the other. DC relays are commonly made for +12 and +24 V, but the ubiquity of +5 V logic supplies has given rise to 5-volt relays as well. Make certain that the contact rating (usually printed on the relay itself) is sufficient for your intended load. Incandescent lamps have a large turn-on surge (10 X the rated current) but it is brief and seldom noticed. It occurs when the contacts close, so it does not damage them (unless they bounce). Special measures should be taken with inductive loads--small motors and such--which do not like to be turned off, and will cause the contacts to spark.
A DC relay is an inductive load itself. A 12 V relay can give a 100 V kick when disconnected, and this must be suppressed when the relay is switched by a transistor. This is easily done by a catching diode across the terminals of the relay coil. The switch-off transient goes through this diode, so the inductive kick is completely eliminated. If you do not use a catching diode, the transistor will not survive long. Circuits for relay interfaces are shown at the right. Where +5 is shown, it must be +5. +V is the appropriate voltage for the relay coil, not over 30 V for the 2N4401 and MPSA13. If higher voltage is required, there are high voltage transistors that will work well, but at somewhat lower betas. The MPSA42, for example, can take 300 V.
At this point you should have no problem designing interfaces to control the AC line, whether through solid-state relays or mechanical relays. This is open-loop control, and you have no guarantee that the lamp actually went on or off. The loop can be closed by detecting the light or its heat, or by detecting the current in the lamp circuit. For a light detector, see the page Optoelectronics. This alternative has the problem of interfering lights, but current detection is nearly foolproof. If the lamp is drawing the usual current, it is probably lit. Let's design a circuit to tell us if the current is flowing.
This is done by passing the current through a small series resistor, robbing a bit of the applied voltage as the cost of our information. The voltage across this resistor is rectified and smoothed (the smoothing may be unnecessary, but it is done here for neatness), then used to drive the LED in an optoisolator. The optoisolator neatly separates the AC voltages from our logic circuit. The output of the optoisolator is then arranged to be a digital bit, low when the light is on, and high when it is off.
The optoisolator I used is the common 4N28, an LED-phototransistor combination supplied in a 6-pin DIP. Similar optoisolators are available under other part numbers. The 4N28 has a maximum diode current of 80 mA, with a forward voltage of 1.5 V maximum, 1.1 V at 10 mA. The maximum reverse voltage is 3 V, as typical for LED's. All three terminals of the phototransistor are brought out, but the base connection at pin 6 is not normally used. The transfer ratio, Icoll/Idiode is guaranteed to be at least 10%. The maximum VCEO, the voltage between collector and emitter, is 30 V, and the maximum collector current is 100 mA. The circuit is shown at the left, as arranged for detecting a 60 W light bulb, which draws about 0.5 A, rms. This gives a rectified voltage of nearly 5 V. The output logic level is 0.22 V, which is satisfactory.
The circuit must be altered as the AC current is changed, to provide sufficient current for the optoisolator, but not too much. This can be done by adjusting the main sensing resistor, 10Ω here, so that the voltage across it is about 5 V rms. This is not critical, and some freedom exists. The LED current should probably be kept between 10 mA and 50 mA. The LED resistor can also be adjusted for the proper current. If you connect an inverter and an LED at the output, the LED will be seen to light when the lamp is off, and go dark when it is on. This bit can be read from the status port of the parallel port, and it is even possible to arrange an interrupt when it signals that the lamp is off. I could get the circuit to work with only a 2.5 V penalty, but this drastically reduces the dynamic current range.
The process of creating an external voltage that can be varied by a program in the computer is usually called Digital-to-Analog Conversion (D/A conversion). There are lots of ways to do this, but we shall discuss only one here, that is of general application and satisfies most non-critical requirements. A special case is producing a rapidly-varying waveform (such as an acoustic signal) where the speed may require special procedures. We are talking only about slowly-varying voltages here, say below 1 kHz, and even DC values.
The device to be used is an "8-bit multiplying DAC" that takes 8 data bits and sinks a variable current proportional to the binary number that they represent. The particular chip used is the MC1408, which, at least at one time, was very popular and inexpensive. The circuit is supplied with a reference current into pin 14 given by Vref/R, where R is a resistance in series with the reference voltage Vref. The output current, which is sunk into pin 4, is given by Vref/R(b7/2 + b6/4 + b5/8 + b4/16 + b3/32 + b2/64 + b1/128 + b0/256). The converter is called "multiplying" because the output current is proportional to the product of the reference voltage and the binary input. The output current can be turned into a voltage with the help of an op-amp, such as the LF411. By choosing the feedback resistor appropriately, it is possible to get any output voltage in the range of the op-amp.
Preparing the MC1408 for operation is simple. The 8 bits are applied to pins 5-12 in the order high to low. Pins 1 and 2 are grounded. +5 V is supplied to pin 13, and the negative supply voltage to pin 3. This can be anything from -5 to -12 V. It is rather annoying that a bipolar supply is required, but it is. A resistor connected to the reference voltage is connected to pin 14. It is recommended to use a reference supply separate from the +5 V logic supply for the best accuracy. A 2.5 V voltage source is a good choice for this, although we will just use +5 here for convenience. The reference current must not exceed 4 mA, and 1 or 2 mA are better. A capacitor is connected between pin 16 and the negative supply to compensate (stabilize) the current amplifier. If the resistor at pin 14 is 1k, the capacitance can be 15 pF, rising to 75 pF as the resistor value increases to 5k. Pin 15, which is just a transistor base, is connected to ground through a resistor equal to the one at pin 14 (but may be omitted). The connections are now complete, and all you have to do is use the variable current into pin 4. The complete test circuit is shown at the right.
To test the circuit, apply digital levels to the digital inputs, making sure that you use the correct order of bits. This can be done manually, by wiring the various inputs, but it is much easier if you have hex switches that allow you to select any 8-bit hexadecimal number. Measure the output voltage with a DMM. Find the voltage increment per bit. The MC1408 can be connected directly to the parallel port, since its inputs add less load than LSTTL. The settling time is a few hundred ns, so the speed of conversion will probably be limited by the speed of the parallel port. It is possible to produce acoustic signals up to 10 kHz or so, limited by the port speed.
This method of D/A conversion relies on an elegant circuit, the R-2R ladder, which can provide a series of currents related by factors of 2 using resistors of all the same size. To see how it works, wire up the circuit shown at the left, using 1% resistors if you have them. Measure the voltages that result at nodes a, b, c and d, as well as the applied voltage. It is more accurate to measure from +5 down rather than from ground up, as usual. Compute each current from the voltage drop and resistance. The currents I5 and I6 are the same, and represent the smallest current increment. In the converter, the currents for each bit are passed through transistor switches that guide them either to the output pin (for input data = 1), or else to +5 (for input data = 0). In this way, the currents corresponding to each bit are summed. The actual circuit is given in the data sheets for the MC1408.
The inverse to D/A is Analog-to-Digital Conversion, A/D. There are many ways to do this, all of which are quite interesting, but we shall concentrate on one commonly-used method, the "successive-approximation" (SA) converter. The method is straightforward, and uses a chain of 256 resistors that provide all possible voltages in the range. The input voltage is simply compared with these voltages until the closest match is found. Then, the digital address of that voltage is the output. Some D/A converters actually compare with each of the voltages, but the successive-approximation converter uses the rapid method of a binary search. The voltage to be converted is first compared to V/2. If greater, the bit is 1. If less, then the bit is 0. This process continues for each bit, each time halving the search interval. After 8 comparisons, we have the complete 8-bit conversion. This gives quite fast conversion, and an ordinary SA converter can convert in a hundred nanoseconds or so.
The most important warning to the user of SA A/D converters is that the input voltage must remain absolutely constant during the conversion. This means that in most practical cases, a sample-and-hold (S/H) circuit must be used in conjunction with the converter. When we test the A/D converter in the laboratory, we will use constant voltages that bypass the problem. Of course, the input voltage can vary between conversions; it is only during conversions that it must be held constant. The errors produced by a varying input voltage can be large; if the voltage crosses V/2 during the conversion, the error can be 50%. Therefore, in any practical case where accuracy is important, a S/H must be used. A good, inexpensive circuit is the National LF398, which comes in a 4-pin DIP, and is easy to use. Sample-and-hold circuits are discussed in Sample-and-Hold.
The precision of an A/D converter is specified by the number of bits, N. The smallest step is then V/2N, where V is the total range. We'll investigate the inexpensive 8-bit ADC0804LCN from National Semiconductor, long a familar standby. If you need more accuracy, then converters with 10 and 12 bits or more can be used, but they provide illusory accuracy unless used with great care, and, of course, with a good S/H. For high precision and accuracy, other methods are more suitable.
The external voltage to be measured should be translated into the range of the A/D converter by op-amps or other means. The normal range of the converter is 0 to +5 V. If we are thinking of measuring the position of a potentiometer slider, for example, then we can apply the same voltage to the potentiometer as to the A/D converter, so that even if the voltage varies, the relative position will remain the same. This is the principle of ratiometric conversion that can contribute to accuracy. Alternatively, the range of conversion can be adjusted by applying a reference voltage of V/2 (usually 2.5 V) to pin 9. When this pin is not used, simply bypass it to ground with a .01 μF capacitor. We will use ratiometric conversion with a potentiometer for test purposes.
The ADC0804 is designed so that it can be used on a computer bus. This means that its digital output pins are in a high-impedance state unless the RD (read) pin is pulled low. Also, the chip is disabled unless the CS (chip select) pin is pulled low. For static tests, we simply ground both pins. If CS is low, and the chip is active, a low pulse on the WR (write) pin causes a conversion to begin. Nothing is written to the ADC; this is simply a message that a computer can use quite naturally. When the conversion is finished, a low level appears on the INTR (interrupt request) pin to signal the fact. It does not have to cause an interrupt, but can merely be sampled to find out the converter status. If we connect INTR to WR, the end of each conversion will begin a new one, and the converter will always display the latest data at its output. We will trigger conversions manually.
The circuit is shown at the right. The 150 pF capacitor and 10k resistor create an RC oscillator using an internal inverter with hysteresis. These values give a clock frequency of around 640 kHz, which causes a conversion to be completed in from 103 to 114 μs. 8 clocks are used for each digit, for a total of 64, plus a variable delay of up to 8 clocks when a conversion is initiated. The slower the clock, the longer a conversion takes, of course. An external clock signal can be fed to pint 4 instead of using the internal oscillator. In this case, the 10k resistor is not used, and pin 19 is left open. The output bits can be displayed on buffered LED's. Every time the WR pin is pulsed low, a new conversion will appear. It is convenient to use a debounced pushbutton if you have one to get just one conversion (but this does not matter). The voltage step corresponding to one count is (V+ - V-)/(2N - 1). If the total reference voltage is 5.12 V, then this is 20 mV. Make a series of conversions, then divide the voltage by the counts to see what you get. It is recommended that the VCC pin be bypassed by a 10 μF capacitor to help keep it steady.
If you have both the A/D and the D/A converters breadboarded simultaneously, connect the digital output of the A/D to the digital input of the D/A and compare input and output voltages. If you want them to be the same, both converters must be adjusted to have the same ranges by trimming the feedback capacitor on the D/A op-amp.
The next problem is getting the 8 bits in the computer, when the parallel port has only 5 input bits. If you have a bidirectional parallel port, it can be configured for input by setting bit 5 of the control port to 1. Let's do the harder problem instead and work with just the original 5 input bits. The answer, of course, is to multiplex the 8 bits into two 4-bit nibbles, and read the nibbles one at a time, then combine them. We will need a control to strobe the WR input to begin a conversion, as well as a bit to select nibbles. Let's use pin 1 (of the DB-25 connector) to strobe WR, and pin 14 to select the nibble. These are bits 0 and 1 at port address 37A, and both are inverted upon output. Presuming the normal state of the other bits is with 00 written to 37A, we start a conversion by writing 01 to 37A, then a 00 to bring things back to normal. We can use pin 15, bit 3 of the status read at 379, to tell us when INTR goes low and the conversion is ready for input. Actually, with the present connection INTR just strobes low, so we cannot use it as a completion flag. Instead, we will simply wait a sufficient length of time for the conversion to be complete. There will normally be no apparent lag. When we input this byte, the upper four bits will be one of the nibbles of the conversion. If we use a 74LS157 quad one-of-two data selector, we can put the other nibble on the line by writing a 02 to 37A, and then read the second byte at 379. Now we have the two nibbles, and they can be joined by the computer to get the complete byte. This is done by clearing the lower four bits of the high byte (reading 379 puts it in the upper four bits). The other input byte containing the low nibble is shifted four places right, and OR'ed with the byte containing the high nibble.
|100||mov dx,37a||trigger conversion|
|10C||mov cl,ff||wait a while|
|115||in al,dx||read high nibble|
|116||and al,f0||mask off lower 4 bits|
|118||xor al,80||fix bit 7|
|11A||mov ah,al||save high nibble|
|11C||mov dx,372||address low nibble|
|125||in al,dx||read low nibble|
|126||xor al,80||fix bit 7|
|12C||or al,ah||combine nibbles in al|
|12E||jmp 0||return to debug|
There are two parts to this problem, the circuits and the program. The two cooperate to give the desired result, and each must be designed with the other in mind. The program to read a byte from the ADC is shown at the right in DEBUG assembler format. The circuit for the interface is shown at the left. For other assemblers, it will be necessary to put "H" after the hex numbers, but little other modification should be required. To create the file, start DEBUG and begin assembling at 0100 with the "a" command. Enter each instruction in order. At the end, just press Enter without any input. Now use "u" to disassemble the program, and check that it is correct. Now execute the command "n readadc.bin" to create the FCB in DEBUG. Use the "r" instruction to set CX to the length of the file. CX = 0032 allows an extra byte or two. Now execute "w" and Debug will tell you that it wrote 32 (hex) bytes. The DOS dir display will show the file as 50 bytes long. The file could be given a .COM extension, and it would run in DOS, but this would be pointless since the program does not include any way to display the result. The program is intended only for testing the interface in DEBUG, and as a basis for programs that actually do something with the result.
The program begins by writing zeros to the control port, which we presume is the normal state, with the WR signal attached to Pin 1 high. Then we pull Pin 1 low by writing 1, and then restore it to 0. This triggers a conversion, which now takes place. We now wait for 255 loop cycles of decrementing the CL register and checking if it is zero. Depending on the processor speed, this should allow enough time for the conversion. If the program were running on a fast processor, some other way of implementing the delay would have to be used. I tested the program with a 10MHz 386SX, and the delay seems long enough. Now the byte containing the high nibble is read from the status port. It is necessary to mask off the low four bytes (which will be 1's), and then to invert the high-order bit. It is a peculiarity of the parallel port that this bit is inverted. Now the high nibble is saved in AH. The low nibble is now addressed by changing the select input of the 74LS157 from 0 to 1, which is done by writing 2 to the control port. Now the byte containing the low nibble can be read into AL. Bit 7 is inverted, and the byte is shifted four places right, bringing in zeros at the left, and letting the rubbish fall off at the right. We want the logical shift right, SHR, and not the arithmetic shift right, SAR, that repeats the high bit for sign extension. Finally, the nibbles are combined by ORing them into the same byte in AL.
To test the program, start DEBUG with DEBUG readadc.bin, which will load the file automatically. The command g 12e will execute the program, and stop just before the JMP 0 instruction. Use "r" to inspect the value in AL, which should be the conversion. Return to DEBUG by excuting "g." Just excuting "g" from the beginning will not change the value of AX in the "r" instruction when the program terminates, unfortunately. The command -g=100 12e should give you a conversion, which can be read with the "r" instruction. Just repeat this every time you want a new value, and don't even use the JMP 0.
This routine can easily be written in C and embedded in a C program to read a conversion and do something with it. The subroutine for reading a byte as two nibbles can be used for any similar application. My C program (1) displays the conversion in hex and decimal, and gives the corresponding voltage; (2) allows input of the reference voltage, something that is important for finding the voltage accurately, and (3) shows the temperature in C and F, using an LM335 temperature sensor.
There is much more to A/D and D/A conversion, but the examples given here are practical and should get you started economically. The parallel port, even in its simplest forms, offers valuable opportunities for computer control. For digitizing audio signals, a much faster converter is required, one that works on different principles and is optimized for speed rather than accuracy. A completely different approach is to convert between voltage and frequency instead, so that a frequency measurement can be used. This approach has many advantages, and is not difficult to implement.
If you have more than one byte to read with the parallel port, an interesting solution uses two 74LS170 4 x 4 register files to permit the reading of any one of four bytes. The circuit is shown at the left. Note that the data port is used for the address information, selecting which byte is used. The outputs of these chips are open-collector, and so can be wire-OR'ed (OR in negative logic). We can load the bytes into two cascaded chips, then connect corresponding outputs of the two nibbles, together with a pull-up resistor, so that either one nibble or the other can be read, as desired. Of course, the bytes have to be addressed, and this requires two control lines (which may be manually controlled in a pinch, as can the begin conversion pulse) in addition to the signal controlling which nibble is read. This line is connected to the read enable of one LS170, then is inverted and connected to the other. In this case, either one nibble or the other will always be ready to go, and the nibbles can be exchanged by toggling this line. Two more LS170's give us four more bytes, and so on. The addressing demands are more complex, and perhaps the data output of the port can be used for this purpose as 8 additional control lines. In fact, quite an impressive byte input facility can be created in this way. The arrangment of the write strobes, that store the values, depends on the application. The /INTR output of the ADC0804 can be used for this purpose.
If you do not need to store conversions on the interface, the solution using LS170's is a bit too complex, though it shows the use of open-collector ouputs. A simpler solution uses the 74LS125 and 74LS126 quad buffers with 3-state ouputs. These chips are identical except that the LS125 has an active-low output enable, while the LS126 output enable is active high. This is perfect for selecting one nibble or the other. The outputs may be tied together since one is always high-impedance and will not contend with the other.
A circuit using the LS125 and LS126 buffers is shown at the right. The 28-pin ADC0809 has an analog multiplexer to choose one of eight inputs, which makes it an interesting chip for data aquisition. The circuit shows how an ADC0809 can be interfaced to the ordinary parallel port to give eight input analog channels. A serious design error in the chip was not bringing the multiplexer output and the comparator input to external pins, so that a single sample-and-hold circuit could be used. The ADC0809 requires eight separate sample-and-hold circuits, if they are necessary. The interface shown assumes the analog signals are steady, so the conversions will be accurate. Any of the inputs could be equipped with a S/H if necessary, controlled, for example, by pin 17, which is still uncommitted. In the 16-channel ADC0816, this error was corrected. Unfortunately, this comes in an even larger, 40-pin DIP. There is very little discussion of the errors in converting a dynamic or noisy signal in the National data book, though this is a very nasty problem that must be solved in any practical case. 12-bit converters give 12-bit precision, but usually not 12-bit accuracy!
The ADC0809 has no on-chip oscillator, so an external oscillator is used. Be sure to use an HC chip for this, not an LS chip. The frequency is not critical; the specifications say 10 kHz to 1.28 MHz, with 640 kHz as a typical value. The circuit uses a chain of 1k resistors forming a voltage divider with 8 taps for the test input to the converter. Of course, in a practical interface, these would be replaced by 8 terminals for connecting the desired signals. The pins on the DB-25P parallel port connector are shown. There is still one uncommitted control bit, and five unused data output bits, that can serve as bit I/O.
My C program for exercising the converter checks the EOC (end of conversion) output to see when the data is ready. In contrast with the ADC0804, a conversion is begun by cycling the ST input high and then low again. Conversion starts when ST goes low, and EOC goes low at the same time. EOC remains low until the conversion is complete, when it goes high again. After initiating a conversion, the conversion routine examines bit 3 in the status byte, looping until it goes high, then reading the data. If, for some reason, it never goes high, the loop times out after 100 iterations, and reports the error on the video screen. The program uses routines to (1) output the channel number and latch it with the ALE signal; (2) input a byte as two nibbles, making all the necessary corrections; and (3) trigger a conversion, wait until it is done, and report the value. Menu choices are to read all 8 channels, read a specified channel, set the reference voltage, and to read a specified channel and interpret the voltage as temperature, assuming an LM335 sensor. It is very satisfying to see the values of all eight channels appear practically instantaneously.
Yet another solution is to read the bits in one by one, as they are presented serially. All that is required is an 8-bit shift register that can be loaded in parallel (all 8 bits at once) and a shift pulse to control it. Each serial channel is implemented with one output and one input bit, so the ordinary parallel port can support five such channels. The same shift pulse can be used for all channels (be sure to buffer it so it can handle the load). Serial interfaces are, of course, not news, and their advantages are well known (as in the USB interface, and the familiar RS-232 serial port). A suitable shift register is the 74LS165 8-bit parallel to serial shift register. The circuit for a serial interface is shown at the left. Note that the bit output is taken from /Q, since bit 7 of the byte read from port 379 is inverted. Normally, one would use the Q output at pin 9. A low-to-high edge on the clock input CK shifts all the bits one place right. There are two CK inputs (2,15) that are OR'ed internally. The content of the highest-order bit is output at Q (9) and /Q (7) in direct and inverted form. The byte is loaded by a low level on the PL (parallel load) pin (1). The /INTR pulse at the end of conversion can be used to load the shift register. This idea can easily be extended to more than eight bits.
In any serial method, it is very important to have a good clock signal, without any ringing or other disturbances that might be interpreted as extra clock transitions. If you find that the serial input works improperly, the reason is probably a bad clock. Test this by connecting the DS (serial input) pin to 0 and seeing if the conversion has extra zeros on the end, and then by connecting it to 1 and looking for extra 1's. When I tried it, it happened that there were as many as six extra clocks among the eight that were programmed! The clock line happened to be long and dangly on the breadboard, which is the probable cause. This was cured by grounding the clock line through a .001 μF capacitor.
I plan to make a control and data aquisition board based on an ISA bus card with two parallel ports. One port will have an 8-channel A/D interface as described above, and the other port will have a 2-channel D/A interface. Any bits left over will be used for bit I/O. The D/A interface will use two LS374 octal D latches to store two bytes, which will be converted to two analog values. This should stimulate interesting applications.
Composed by J. B. Calvert
Created 28 July 2002
Last revised 3 August 2002