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