📄 interfacing the standard parallel port.htm
字号:
state the pin on the Parallel Port is high (+5v). When in this state,
your external device can pull the pin low and have the control port
change read a different value. This way the 4 pins of the Control Port
can be used for bi-directional data transfer. However the Control Port
must be set to xxxx0100 to be able to read data, that is all pins to be
+5v at the port so that you can pull it down to GND (logic 0). </P>
<P>Bits 4 & 5 are internal controls. Bit four will enable the IRQ
(See Using the Parallel Ports IRQ) and Bit 5 will enable the
bi-directional port meaning that you can input 8 bits using (DATA0-7).
This mode is only possible if your card supports it. Bits 6 & 7 are
reserved. Any writes to these two bits will be ignored. </P></UL><A
name=6><I><FONT size=+2>Bi-directional Ports</FONT></I>
<HR>
</A>
<UL>
<P>The schematic diagram below, shows a simplified view of the Parallel
Port's Data Register. The original Parallel Port card's implemented 74LS
logic. These days all this is crammed into one ASIC, but the theory of
operation is still the same. </P>
<CENTER><IMG alt="Internal's of bi-directional Port"
src="Interfacing the Standard Parallel Port.files/bidi.gif"
border=0></CENTER>
<P>The non bi-directional ports were manufactured with the 74LS374's
output enable tied permanent low, thus the data port is always output
only. When you read the Parallel Port's data register, the data comes
from the 74LS374 which is also connected to the data pins. Now if you
can overdrive the '374 you can effectively have a Bi-directional Port.
<I>(or a input only port, once you blow up the latches output!)</I> </P>
<P>What is very concerning is that people have actually done this. I've
seen one circuit, a scope connected to the Parallel Port distributed on
the Internet. The author uses an ADC of some type, but finds the ADC
requires transistors on each data line, to make it work! No wonder why.
Others have had similar trouble, the 68HC11 cannot sink enough current
(30 to 40mA!) </P>
<P>Bi-directional ports use Control Bit 5 connected to the 374's Output
Enable so that it's output drivers can be turned off. This way you can
read data present on the Parallel Port's Data Pins, without having bus
conflicts and excessive current drains. </P>
<P>Bit 5 of the Control Port enables or disables the bi-directional
function of the Parallel Port. This is only available on true
bi-directional ports. When this bit is set to one, pins 2 to 9 go into
high impedance state. Once in this state you can enter data on these
lines and retrieve it from the Data Port (base address). Any data which
is written to the data port will be stored but will not be available at
the data pins. To turn off bi-directional mode, set bit 5 of the Control
Port to '0'. </P>
<P>However not all ports behave in the same way. Other ports may require
setting bit 6 of the Control Port to enable Bi-directional mode and
setting of Bit 5 to dis-enable Bi-directional mode, Different
manufacturers implement their bi-directional ports in different ways. If
you wish to use your Bi-directional port to input data, test it with a
logic probe or multimeter first to make sure it is in bi-directional
mode. </P></UL><A name=7><I><FONT size=+2>Using The Parallel Port to Input
8 Bits.</FONT></I>
<HR>
</A>
<UL>
<P>If your Parallel Port doesn't support bi-directional mode, don't
despair. You can input a maximum of 9 bits at any one given time. To do
this you can use the 5 input lines of the Status Port and the 4 inputs
(open collector) lines of the Control Port. </P>
<CENTER><IMG
alt="Schematic showing how to input 8 bits using Control and Status Port"
src="Interfacing the Standard Parallel Port.files/8inputs.gif"
border=0></CENTER>
<P>The inputs to the Parallel Port has be chosen as such, to make life
easier for us. Busy just happens to be the MSB (Bit 7) of the Status
Port, then in ascending order comes Ack, Paper Out and Select, making up
the most significant nibble of the Control Port. The Bars are used to
represent which inputs are Hardware inverted, i.e. +5v will read 0 from
the register, while GND will read 1. The Status Port only has one
inverted input. </P>
<P>The Control port is used to read the least significant nibble. As
described before, the control port has open collector outputs, i.e. two
possible states, high impedance and GND. If we connect our inputs
directly to the port (For example an ADC0804 with totem pole outputs), a
conflict will result if the input is high and the port is trying to pull
it down. Therefore we use open collector inverters. </P>
<P>However this is not always entirely necessary. If we were connecting
single pole switches to the port with a pull up resistor, then there is
no need to bother with this protection. Also if your software
initializes the control port with xxxx0100 so that all the pins on the
control port are high, then it may be unnecessary. If however you don't
bother and your device is connected to the Parallel Port before your
software has a chance to initialize then you may encounter problems.
</P>
<P>Another problem to be aware of is the pull up resistors on the
control port. The average pull-up resistor is 4.7k. In order to pull the
line low, your device will need to sink 1mA, which some low powered
devices may struggle to do. Now what happens if I suggest that some
ports have 1K pull up resistors? Yes, there are such cards. Your device
now has to sink 5mA. More reason to use the open collector inverters.
</P>
<P>Open collector inverters were chosen over open collector buffers as
they are more popular, and thus easier to obtain. There is no reason,
however why you can't use them. Another possibility is to use
transistors. </P>
<P>The input, D3 is connected via the inverter to Select Printer. Select
Printer just happens to be bit 3 of the control port. D2, D1 & D0
are connected to Init, Auto linefeed and strobe, respectively to make up
the lower nibble. Now this is done, all we have to do is assemble the
byte using software. The first thing we must do is to write xxxx0100 to
the Control Port. This places all the control port lines high, so they
can be pulled down to input data. </P>
<UL></FONT><PRE>outportb(CONTROL, inportb(CONTROL) & 0xF0 | 0x04);
</PRE><FONT face=ARIAL></UL>
<P>Now that this is done, we can read the most significant nibble. This
just happens to be the most significant nibble of the status port. As we
are only interested in the MSnibble we will AND the results with 0xF0,
so that the LSnibble is clear. Busy is hardware inverted, but we won't
worry about it now. Once the two bytes are constructed, we can kill two
birds with one stone by toggling Busy and Init at the same time. </P>
<UL></FONT><PRE>a = (inportb(STATUS) & 0xF0); /* Read MSnibble */
</PRE><FONT face=ARIAL></UL>
<P>We can now read the LSnibble. This just happens to be LSnibble of the
control port - How convenient! This time we are not interested with the
MSnibble of the port, thus we AND the result with 0x0F to clear the
MSnibble. Once this is done, it is time to combine the two bytes
together. This is done by OR'ing the two bytes. This now leaves us with
one byte, however we are not finished yet. Bits 2 and 7 are inverted.
This is overcome by XOR'ing the byte with 0x84, which toggles the two
bits. </P>
<UL></FONT><PRE>a = a |(inportb(CONTROL) & 0x0F); /* Read LSnibble */
a = a ^ 0x84; /* Toggle Bit 2 & 7 */
</PRE><FONT face=ARIAL></UL>
<P><B>Note: Some control ports are not open collector, but have totem
pole outputs. This is also the case with EPP and ECP Ports. Normally
when you place a Parallel Port in ECP or EPP mode, the control port
becomes totem pole outputs only. Now what happens if you connect your
device to the Parallel Port in this mode? Therefore, in the interest of
portability I recommend using the next circuit, reading a nibble at a
time. </B></P></UL><A name=8><I><FONT size=+2>Nibble Mode.</FONT></I>
<HR>
</A>
<UL>
<P>Nibble mode is the preferred way of reading 8 bits of data without
placing the port in reverse mode and using the data lines. Nibble mode
uses a Quad 2 line to 1 line multiplexer to read a nibble of data at a
time. Then it "switches" to the other nibble and reads its. Software can
then be used to construct the two nibbles into a byte. The only
disadvantage of this technique is that it is slower. It now requires a
few I/O instructions to read the one byte, and it requires the use of an
external IC. </P>
<P>
<CENTER><IMG alt="Schematic - 8 inputs using 74ls157 Multiplexer"
src="Interfacing the Standard Parallel Port.files/74hc157.gif"
border=0></CENTER>
<P>The operation of the 74LS157, Quad 2 line to 1 line multiplexer is
quite simple. It simply acts as four switches. When the A/B input is
low, the A inputs are selected. E.g. 1A passes through to 1Y, 2A passes
through to 2Y etc. When the A/B is high, the B inputs are selected. The
Y outputs are connected up to the Parallel Port's status port, in such a
manner that it represents the MSnibble of the status register. While
this is not necessary, it makes the software easier. </P>
<P>To use this circuit, first we must initialize the multiplexer to
switch either inputs A or B. We will read the LSnibble first, thus we
must place A/B low. The strobe is hardware inverted, thus we must set
Bit 0 of the control port to get a low on Pin 1.
<P>
<UL></FONT><PRE>outportb(CONTROL, inportb(CONTROL) | 0x01); /* Select Low Nibble (A)*/
</PRE><FONT face=ARIAL></UL>
<P>Once the low nibble is selected, we can read the LSnibble from the
Status Port. Take note that the Busy Line is inverted, however we won't
tackle it just yet. We are only interested in the MSnibble of the
result, thus we AND the result with 0xF0, to clear the LSnibble. </P>
<UL></FONT><PRE>a = (inportb(STATUS) & 0xF0); /* Read Low Nibble */
</PRE><FONT face=ARIAL></UL>
<P>Now it's time to shift the nibble we have just read to the LSnibble
of variable a, </P>
<UL></FONT><PRE>a = a >> 4; /* Shift Right 4 Bits */
</PRE><FONT face=ARIAL></UL>
<P>We are now half way there. It's time to get the MSnibble, thus we
must switch the multiplexer to select inputs B. Then we can read the
MSnibble and put the two nibbles together to make a byte, </P>
<UL></FONT><PRE>outportb(CONTROL, inportb(CONTROL) & 0xFE); /* Select High Nibble (B)*/
a = a |(inportb(STATUS) & 0xF0); /* Read High Nibble */
byte = byte ^ 0x88;
</PRE><FONT face=ARIAL></UL>
<P>The last line toggles two inverted bits which were read in on the
Busy line. It may be necessary to add delays in the process, if the
incorrect results are being returned. </P></UL><A name=9><I><FONT
size=+2>Using the Parallel Port's IRQ</FONT></I>
<HR>
</A>
<UL>
<P>The Parallel Port's interrupt request is not used for printing under
DOS or Windows. Early versions of OS-2 used them, but don't anymore.
Interrupts are good when interfacing monitoring devices such as high
temp alarms etc, where you don't know when it is going to be activated.
It's more efficient to have an interrupt request rather than have the
software poll the ports regularly to see if something has changed. This
is even more noticeable if you are using your computer for other tasks,
such as with a multitasking operating system. </P>
<P>The Parallel Port's interrupt request is normally IRQ5 or IRQ7 but
may be something else if these are in use. It may also be possible that
the interrupts are totally disabled on the card, if the card was only
used for printing. The Parallel Port interrupt can be disabled and
enabled using bit 4 of the control register, Enable IRQ Via Ack Line.
Once enabled, an interrupt will occur upon a low to high transition
(rising edge) of the nACK. However like always, some cards may trigger
the interrupt on the high to low transition. </P>
<P>The following code is an Interrupt Polarity Tester, which serves as
two things. It will determine which polarity your Parallel Port
interrupt is, while also giving you an example for how to use the
Parallel Port's Interrupt. It checks if your interrupt is generated on
the rising or falling edge of the nACK line. To use the program simply
wire <B>one of</B> the Data lines (Pins 2 to 9) to the Ack Pin (Pin 10).
The easiest way to do this is to bridge some solder from DATA7 (Pin 9)
to ACK (Pin 10) on a male DB25 connector. </P><BR>
<UL></FONT><PRE>/* Parallel Port Interrupt Polarity Tester */
/* 2nd February 1998 */
/* Copyright 1997 Craig Peacock */
/* WWW - http://www.beyondlogic.org */
/* Email - cpeacock@senet.com.au */
#include <dos.h>
#define PORTADDRESS 0x378 /* Enter Your Port Address Here */
#define IRQ 7 /* IRQ Here */
#define DATA PORTADDRESS+0
#define STATUS PORTADDRESS+1
#define CONTROL PORTADDRESS+2
#define PIC1 0x20
#define PIC2 0xA0
int interflag; /* Interrupt Flag */
int picaddr; /* Programmable Interrupt Controller (PIC) Base Address */
void interrupt (*oldhandler)();
void interrupt parisr() /* Interrupt Service Routine (ISR) */
{
interflag = 1;
outportb(picaddr,0x20); /* End of Interrupt (EOI) */
}
void main(void)
{
int c;
int intno; /* Interrupt Vector Number */
int picmask; /* PIC's Mask */
/* Calculate Interrupt Vector, PIC Addr & Mask. */
if (IRQ >= 2 && IRQ <= 7) {
intno = IRQ + 0x08;
picaddr = PIC1;
picmask = 1;
picmask = picmask << IRQ;
}
if (IRQ >= 8 && IRQ <= 15) {
intno = IRQ + 0x68;
picaddr = PIC2;
picmask = 1;
picmask = picmask << (IRQ-8);
}
if (IRQ < 2 || IRQ > 15)
{
printf("IRQ Out of Range\n");
exit();
}
outportb(CONTROL, inportb(CONTROL) & 0xDF); /* Make sure port is in Forward Direction */
outportb(DATA,0xFF);
oldhandler = getvect(intno); /* Save Old Interrupt Vector */
setvect(intno, parisr); /* Set New Interrupt Vector Entry */
outportb(picaddr+1,inportb(picaddr+1) & (0xFF - picmask)); /* Un-Mask Pic */
outportb(CONTROL, inportb(CONTROL) | 0x10); /* Enable Parallel Port IRQ's */
clrscr();
printf("Parallel Port Interrupt Polarity Tester\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -