Note on Parallel Port notation ------------------------------ The usual I/O base addresses for parallel ports are: 3BCh Port on Monochrome display adapter <- Often not used 378h Second parallel port <- Probably your first PP! 278h Third parallel port <- " " second PP! All references to parallel port hardware/firmware follow the conventions used in the Parallel Port FAQ: Dn represents bit n in the Printer Data Register base addr Sn represents bit n in the Printer Status Register base addr+1 Cn represents bit n in the Printer Control Register base addr+2 Where the register bit is followed by a negative sign, e.g. C3-, the hardware input/output associated with it is inverted, i.e. a "1" in the register bit corresponds to a logic LOW, and and "0" to a logic HIGH on the connector. Where a plus sign (or no sign) is given, the signal is not inverted and a "0" corresponds to a logic LOW, "1" to a HIGH. The parallel port pins are named according to their use when interfacing a printer. Presence or absence of preceeding minus signs here are not of relevance to this project. Pin numbers are given for "DB25" connectors - the standard 25-way D connector used on IBM PCs for the parallel port. Reg i/o DB25 Centronics bit pin name D0 o/p 2 D0 \ D1 o/p 3 D1 | D2 o/p 4 D2 | can act as inputs D3 o/p 5 D3 > as well as outputs D4 o/p 6 D4 | on BIDIRECTIONAL D5 o/p 7 D5 | parallel ports. D6 o/p 8 D6 | D7 o/p 9 D7 / S3+ i/p 15 -Error S4+ i/p 13 Select S5+ i/p 12 PaperEnd S6+ i/p 10 -Ack S7- i/p 11 Busy C0- o/p 1 -Strobe C1- o/p 14 -AutoFd C2+ o/p 16 -Init C3- o/p 17 -SelectIn GND - 18-25 GND The Analog to Digital Converter IC ---------------------------------- The GEC-Plessey ZN448E analog to digital converter IC is a good general-purpose ADC which I have used for numerous computer interfacing projects. It's been around for many years and should be available from all the usual component suppliers. ZN448E basic spec: 8-bit successive approximation ADC 9us conversion time (110kHz max sampling rate) +5 volt (4.5 to 5.5v) supply, 25mA current consumption inbuilt 2.55 volt reference Max error: +/- 0.5LSB 18-pin DIL package cost: around 8 pounds sterling (one-off quantities). --u-- -Busy 1| |18 D0 \ LSB -RD 2| |17 D1 | CLOCK 3| |16 D2 | -WR 4| |15 D3 | R ext 5| |14 D4 > Digital outputs Vin 6| |13 D5 | Vref in 7| |12 D6 | Vref out 8| |11 D7 / MSB GND 9| |10 Vcc ----- ZN448E N.B. signals preceeded by a "-" are active-low Typical circuit: Parallel Reg DB25 pins Bit r-------------<--- 1 (-Strobe) C0- +5v --@------@-- | r------<--- 16 (-Init) C2+ | | | | | ----------------------------- 330R [] | 10 2 4 | [] | Vcc -RD -WR | | | | (-OE) | LSB v | | D0 18|-->-- 2 (D0) D0 ---->----|--|6 Analog D1 17|-->-- 3 (D1) D1 | | in D2 16|-->-- 4 (D2) D2 | | ZN448E D3 15|-->-- 5 (D3) D3 | | D4 14|-->-- 6 (D4) D4 @--|8 Vref out D5 13|-->-- 7 (D5) D5 | | D6 12|-->-- 8 (D6) D6 @--|7 Vref in D7 11|-->-- 9 (D7) D7 | | | MSB ^ | | CLOCK GND R ext -Busy | | *WARNING | | 3 9 5 1 | see text | ----------------------------- | | | | | 1uF --- --- | [] 82k | (optional) --- --- | [] ------>--- 15 (-Error) S3+ | |100pF | | | | | | GND -@-------@------@-----|------------------ 18-25 (GND) | -5v --@-- *see text In this circuit the analog input should be in the range 0 to 2.55v. For an AC-coupled input, suitable for connecting to the output of an audio pre-amplifier or a radio/tape headphone output, bias the analog input (pin 6) at 1.28v by tying it to the centre of a potential divider formed of two 10k resistors between Vref out (pin 8) and GND (pin 9). Then couple the audio input to analog in (pin 6) via a 4.7uF or larger capacitor (use of a smaller capacitor will adversely affect bass response). * A negative supply (-5v) is required on pin 5 via an 82k resistor for the ADC to digitise properly down to 0v analog input. I have successfully used -3v from two AA cells with a 47k resistor (don't ask) during prototyping, but having not seen the IC's data sheet I don't really know what this pin does, and cannot guarantee that practice! [IC's for generating negative supplies from positive ones at low current are available at moderate cost.] * -WR is really a Start Conversion input. Pulse this line low momentarily (I've used 0.5us quite reliably, but don't know the spec.) to begin conversion. Pin 1, -Busy, will go low while converting, and return high on completion. Often you just wait at least 9us after starting before attempting to reading the data. (If you jump the gun you get to see the successive-approximation in progress!) * WARNING. Only connect the ADC output DIRECTLY to your parallel port data lines if they are BIDIRECTIONAL. The ZN448 asserts data when -RD (an Output Enable) is LOW, outputs are tri-stated (high impedance) when -RD is HIGH. If -RD is driven by one of the parallel port control lines then you can tell the ZN448 to assert data only once the parallel port has been configured for input. On my computer at least, the control outputs default to C3: LOW, C2,C1,C0: HIGH. C3 & C2 change state several times during the boot-up cycle. [The fancy printer status monitor program supplied with HP LaserJet printers (if enabled & expecting a printer on the port) writes to the printer control register periodically, every 10 or 30 seconds. Such programs should be disabled before using the port for other purposes.] I selected to use the C0- control line as the ZN448E output enable as it seems to consistently stay high during all normal circumstances. For added safety, you might still wish to put 1k5 resistors in series with each data line. [Don't fall into the trap of specifying a higher resistance (supposedly for added safety) as this is likely to prevent the parallel port (TTL) inputs from sourcing enough current to register logic low when required! TTL inputs source around 0.25mA: with a 10k resistor to ground the input will still be at 2.5 volts!] If you've only got a standard output-only port, you're not sure, you need the compatibility, or you just want to be ultra-safe, then use the circuit below. It should work on ANY PC parallel port, and uses a 74LS157 to feed the data, as two nibbles, into 4 parallel port "printer status" inputs. --u-- S 1| |16 Vcc -ENABLE is held low for normal I0a 2| |15 -ENABLE functioning I1a 3| |14 I0c Qa 4| |13 I1c S: LOW selects I0, I0b 5| |12 Qc HIGH selects I1 to appear on I1b 6| |11 I0d the Q outputs Qb 7| |10 I1d GND 8| |9 Qd ----- 74LS157 Suggested circuit: ---@--- +5v | | ------- --------------- Parallel Reg ZN448E | | 16 | DB25 pins Bit | LSB | V+ | D0 18|-->---->---|10 I1d S 1|---<-- 17 (-SelectIn) C3- D1 17|-->---->---|13 I1c | D2 16|-->---->---|6 I1b | D3 15|-->---->---|3 I1a Qd 9|--->-- 13 (Select) S4+ D4 14|-->---->---|11 I0d Qc 12|--->-- 12 (PaperEnd) S5+ D5 13|-->---->---|14 I0c Qb 7|--->-- 10 (-Ack) S6+ D6 12|-->---->---|5 I0b Qa 4|--->-- 11 (Busy) S7- D7 11|-->---->---|2 I0a | | MSB | | -RD | | GND -E | 2 | | 8 15 | 74LS157 ------- --------------- | | | | | | --@--------------------@-----@--- GND In this configuration the output of the ZN448E should be permanently enabled. The ZN448E's -RD pin (pin 2) is now connected directly to GND rather than to the parallel port -Strobe pin (pin 1). All other connections to the ZN448E are as before. The parallel port data lines are unused. Programming ----------- The program to run the first arrangement, that for bi-directional ports and using the data lines for input, is very simple: Set the parallel port data lines for input, usually by setting C5 to a 1 Set C0- to a 1 to enable the ADC output REPEAT Set C2+ to 0 briefly, then to 1 to initiate a sample. Wait at least 9us, or until S3+ goes to a 1 (if optional connection made) Read the data from the data register, D UNTIL DESIRED Set C0- to a 0 to tri-state the ADC outputs when you have finished. This arrangement will give the fastest data-throughput. If you're using the second arrangement, with the 74LS157, then data is input through the control lines and you need the program below: Set C2+ to 0 briefly, then to 1 to initiate a sample. Wait at least 9us, or until S3+ goes to a 1 (if optional connection made) Set C3- to 0 to select the less significant nibble Read S, XOR with 128 to correct sign of S7- AND with 240 to discard unwanted bits Arithmetic shift right 4 places to slide into the low-nibble position Put the result to one side Set C3- to 1 to select the more significant nibble Read S, XOR with 128 to correct sign of S7- AND with 240 to discard unwanted bits OR the result with low nibble stored earlier The final result is the sample. Use or store as desired. Repeat for the next sample. The data throughput with this setup will be slower as the data has two be loaded in two stages, but I still achieved about 35,000 samples per second on a 486sx20. Accessing the ports ------------------- In QBASIC, I/O ports can be accessed using the INP and OUT commands. e.g. PRINT INP(&H379) to read I/O address 379h (printer status register) OUT &H37A,16 to write the value 16 to address 37Ah (ctrl reg.) From C or PASCAL, you'll probably have to revert to in-line assembler. To read a port: asm{ mov dx,portaddr // 'portaddr' the I/O address (an unsigned int) in al,dx mov value,al // 'value' is of type unsigned char (or BYTE) } On exit, 'value' contains the contents of the port. To write to a port: asm{ mov dx,portaddr // 'portaddr' the I/O address (an unsigned int) mov al,value // 'value' is the byte to be written out dx,al } Tried & Tested example program (data-input through printer-status lines) ------------------------------------------------------------------------ // Program to read ADC connected to parallel port. // Tested & works 11/9/95 // Can achieve 500000 samples in 14 seconds on my 486sx20 // (with numerical output inhibited of course) // - equivalent to a mean rate of 35kHz. // (C) W.A.Steer 1995 #include <<>iostream.h<>> void main() { unsigned char value, tmp, adcval; unsigned int Port, PortCtrl, PortStat; Port=0x0378; // Parallel port base address PortStat=Port+1; PortCtrl=Port+2; for (long x=0; x<500000; x++) { asm{ mov dx,PortCtrl mov al,0 // Set C0,1,2,3 to 0 out dx,al // C2=0 sets -WR on ADC to LOW mov al,4 // Returns C2 (-WR on ADC) HIGH again out dx,al // initiates a sample. } do { asm{ mov dx,PortStat in al,dx // Read the port status register mov value,al } } while (!(value & 8)); // Wait until C3 turns to 1: conversion complete asm{ mov dx,PortStat in al,dx // Read the port status register xor al,128 // Invert top bit and al,240 // Discard all but top 4 bits shr al,4 // Low nibble, so shift right mov tmp,al } asm{ mov dx,PortCtrl mov al,12 // Set C3 high (without changing C2), causes out dx,al // the 74LS157 to select the high nibble } asm{ mov dx,PortStat in al,dx // Read the port status register xor al,128 // Invert top bit and al,240 // Discard all but top 4 bits or al,tmp // OR with the low nibble collected earlier mov adcval,al // store result in "adcval" } // remark out whole of line below for speed evaluation cout << (unsigned int)adcval << endl; // Display the sample } } // --------- end of program ----------- Testing ------- The best way to check that all is well is to connect the wiper of a 10k potentiometer to the ADC input, one end of the pot to the Vref and the other to GND. Rotating the potentiometer through its full sweep should yield digital outputs smoothly going from 0 through to 255. Any missing, inverted, or crossed data bits will be very apparent here. Ideas - uses ------------ 1) Use it as a digital voltmeter with range 0 to 2.55 volts in steps of 0.01 volt. 2) Write programs to use the hardware as a digital oscilloscope. 3) Write a program to do a Fourier analysis of the waveform & create a spectrum analyser. 4) Use it to digitise sound, & build up a collection of samples. 5) By adding a header, your stream of digitised sound can be made into a legitimate .wav file, playable with a sound card or using PC-Speaker. 6) Connect a DAC (e.g. ZN428E-8) to the data bits of the parallel port, (assuming you've used the latter ADC setup which does not use the data lines), and play back the sound by writing the 8-bit sampled values to the port data register. (see below) 7) Using the hardware from (6), experiment with real-time delays, echoes, pitch-shifting etc. etc. Digital to Analog converter --------------------------- The ZN428E-8 is a good complement to the ZN448E. Spec: 8-bit DAC 5 volt (4.5 to 5.5v) supply, 20mA current consumption Linearity error: +/- 0.5LSB inbuilt 2.55 volt reference output impedance: 4k settling time: 800ns cost: about 6 pounds sterling. --u-- bit 1 1| |16 bit 2 (LSB) bit 0 2| |15 bit 3 NC 3| |14 bit 4 -ENABLE 4| |13 bit 5 Analog OUT 5| |12 bit 6 Vref in 6| |11 bit 7 (MSB) Vref out 7| |10 Vcc +5v Analog GND 8| |9 Digital GND ----- ZN428E-8 Suggested circuit: +5v --@------@-- | | Reg | ----------------------------- DB25 pins Bit 330R [] | 10 | [] | Vcc | | | | LSB | | D0 2|--<-- 2 (D0) D0 ----<----|--|5 Analog D1 1|--<-- 3 (D1) D1 | | out D2 16|--<-- 4 (D2) D2 | | ZN428E-8 D3 15|--<-- 5 (D3) D3 | | D4 14|--<-- 6 (D4) D4 @--|7 Vref out D5 13|--<-- 7 (D5) D5 | | D6 12|--<-- 8 (D6) D6 @--|6 Vref in D7 11|--<-- 9 (D7) D7 | | | MSB | | Analog Digital | | | -Enable GND GND | | | 4 8 9 | | ----------------------------- | | | | 1uF --- | | | --- | | | | | | | GND -@-------@------@------@------------------ 18-25 (GND) N.B. If -Enable is made high the output will latch. The output (2.5v p-p) can be connected to the line-level input of an audio amplifier via a 4.7uF capacitor. [If you did use bidirectional data lines for ADC input, you could still use them for DAC output by appropriately tri-stating the ADC output, asserting and then latching DAC data (drive the DAC's -Enable input using C1 or C3 to control data-latching), then returning the parallel port to input-status before asserting & reading the ADC output.] Higher-speed sampling (for advanced constructors!) --------------------- If, despite efficient machine-code programming you can't achieve the data rate you would like, but can tolerate the data only coming in limited-size chunks (e.g. for a computer-oscilloscope) then you can devise a circuit to clock the ADC and store the data (rapidly) in an outboard RAM, ready for slower downloading to the PC. I have used a 6264 CMOS static RAM in the past, but more recently, for video-speed projects I found a high speed (22MHz) FIFO (first in first out) memory to be more convenient since they do not require external address-generator. With FIFOs it's also easy to start to read out data before you've finished sampling that "block". General Advice -------------- I have used ZN448 circuits very successfully with other computers, but am relatively new to PC parallel ports. The ZN448E/74LS157 circuit is thoruoghly tried and tested, but I have not been able to use the simpler data-line input arrangement as my parallel port is not bidirectional. Take all the usual ESD precautions, and avoid modifying the circuit while it is plugged in to the computer. It would be advisable to experiment with a parallel port on a plug-in card rather than one built onto the motherboard, for obvious reasons, just in case of disaster. 5 volt supplies (up to 100mA) can be obtained using a 78L05 regulator, itself powered by batteries or a wall plug-in (7 volts or more). [If using batteries, make sure they are in good condition. If the regulator output drops much below 5 volts the data may not be forthcoming, or worse still the circuit may largely appear to function, but you spend hours trying to figure out why certain ADC values cannot be yeilded!] It may be convenient to wire a 20-way ribbon cable to the 25-way D connector, making connections starting from one side of the cable in the order given in my first table (you only need one ground connection). Rainbow-cable makes for easier identification, but for prototyping I recommend using the IDC cable and fitting a 20-way DIL IDC Header to the 'electronics' end of the cable. The header will easily and positively plug into a proto-board, and avoids the problem of individual wires popping out of the board, and possibly then shorting when the cable is moved. Legal Note: ----------- You should check my diagrams and be sure in YOUR OWN MIND that everything is safe, similarly check any physical circuits you build against the diagrams BEFORE plugging in and/or applying power. I shall not be held responsible for any calamity you may have! Take care, & HAVE FUN! Andrew.
Please send any queries to w.steer@ucl.ac.uk .
Last modified 13th September 1995