============================================================================== C Scene Issue #04 Parallel Port Programming Moe Elzubeir =============================================================================
Parallel port programming is easier than it sounds. The lack of literature on it is surprising, but not a problem. Once you find a few resources of information, you will be set. In this article, I will try to give you the inform ation that you need, without the baffling technicality. Simple does it.
The Basics:
Access to the parallel port is via a female DB25 on the back of the PC.
|
Pin |
Description |
Notes |
|
1 |
/Strobe |
PC Output (OC) |
|
2 |
Data_0 |
PC Output |
|
3 |
Data_1 |
PC Output |
|
4 |
Data_2 |
PC Output |
|
5 |
Data_3 |
PC Output |
|
6 |
Data_4 |
PC Output |
|
7 |
Data_5 |
PC Output |
|
8 |
Data_6 |
PC Output |
|
9 |
Data_7 |
PC Output |
|
10 |
/ACK |
PC Input |
|
11 |
Busy |
PC Input |
|
12 |
Paper Empty |
PC Input |
|
13 |
Select |
PC Input |
|
14 |
/Autofeed |
PC Output |
|
15 |
/Error |
PC Input |
|
16 |
Init Printer |
PC Output |
|
17 |
/Select_Input |
PC Output |
|
18 |
Ground |
|
|
-25 |
Table 1 - Parallel Port Terminal Designations
Every parallel port has three port addresses: Data, Status, and
Control. The three of them are in sequential order. (I.e., if the Data
port is 0x378 then the Control port is 0x378+1, and so on).
So, how do you find out what the port address of your parallel port
it? That depends on your platform. If you are under DOS, you can do
the following:
C:\>debug
-d 0040:08 L8
0040:0008 78 03 78 02 00 00 00 00
The above is an example of an output from the debug command d 0040:08
L8. The port address resides in the memory location 0040:08.
Outputs:
Note that there are eight output on the Data Port, and four additional
outputs on the low nibble of the Control Port (/SELECT_IN, INIT,
/AUTOFEED, and /STROBE).
Luckily, all outputs to the Data Port are true logic. However, the
/SELECT_IN, /AUTOFEED, and /STROBE outputs on the Control Port are
inverted.
Let's see some sample code:
/* codecodecodecodecodecodecodecodecodecodecodecodecodecodecodecodecode */
#include <stdio.h>
#include <unistd.h> /* needed for ioperm() */
#include <asm/io.h> /* for outb() and inb() */
#define DATA 0x378
#define STATUS DATA+1
#define CONTROL DATA+2
int main(void)
{
int x = 0x32;
int y = 0x08;
if (ioperm(DATA,3,1)) {
printf("Sorry, you were not able to gain access to the ports\n");
printf("You must be root to run this program\n");
exit(1);
}
outb(DATA, x); /* Sends 0011 0010 to the Data Port */
outb(CONTROL, y^0x0b);
/* SELECT_IN = 1, INIT = 0, /AUTO_FEED = 0, /STROBE = 0 */
return (0);
}
/* codecodecodecodecodecodecodecodecodecodecodecodecodecodecodecodecode */
The above code will work on a Linux. As far as SunOS is concerned there
are two files you need to include (sys/ddi.h) and (sys/sunddi.h) for
the inb() and outb() functions.
You also need to compile with -O or -O2 or similar. The reason for that
is that inb() and family are defined as inline macros, and compiling
them without optimization enabled will cause unresolved references at
link time (Someone in #c came and kept on asking about it, and I thought
I would add this part in). [If people would only read the man pages!].
Inputs:
There are five status leads for the Status Port. (BSY, /ACK, PE,
SELECT, /ERROR). The reasons they are named like that is that it was
originally designed for a printer only. So, we have things like PE
(paper empty), etc.
Beware, the BSY is inverted using hardware, so when receiving input
from there, make sure you invert this bit so you can have what
represents a true logic.
This is how to read the five most significant bits in true logic:
Value = ((inb(STATUS)^0x80) >> 3);
Notice how we inverted the BSY bit using the exclusive-or function.
Then, we shifted the bits 3 times to the right, resulting in the upper
five bits ending up in the lower five bit positions.
0 0 0 BUSY /ACK PE SELECT /ERROR
In conclusion, we should have realized by now that there are 12
outputs (eight on the data port, and four on the lower nibble of the
control port). There are five inputs, on the highest five bits of the
status port. Three output bits on the control por t and one input on
the status port are inverted by the hardware.
Problems and Solutions:
It may have occurred to you that there are only five bits on the
parallel port for input. This could be a real inconvenience when
interfacing with 8-bit analog to digital converters. One solution to
this problem is to add external circuitry to store the 8-bit result
and gate in 4-bits at a time.
So what can we do about it?
What I did not mention about the Control Port (other than having four
outputs) is that they are bi-directional. They can be outputs and
inputs! Dont you just love how confusing they can get?
Using the control port bi-directional bits is not as simple as just
reading them using inb(). We have to force all the four outputs on the
lower nibble of the control port to logic one. This will result in
external signals being forced on these inputs and then we can read
using the inb().
int i;
outb(CONTROL, 0x0f^0x0b);
/* inverting to go around the hardware inversion */
i = (inb(CONTROL) & 0x0f) ^ 0x0b;
Doing so, we would have solved our little problem there.
Other Handy Macros
Now, let's say you want to send a word to portX and portX+1 instead
of just sending a byte to portX and then another byte to portX+1.
outw(value,portX) will do the trick. inw(portX) will return a word
from portX and portX+1.
Now usually when sending data to the parallel port you need a slight
delay to ensure that the data has been sent, and not lost. Instead of
having extra calls, and more code, there is outb_p() inb_p() outw_p()
and inw_p() which gives us a delay of about 1 microsecond. If that
is not enough delay, you can always #define REALLY_SLOW_IO before
the #include . Those macros use a port output to port 0x80
for their delay. So you need to give access to that port with ioperm().
Final Words On Permissions
In order to use ioperm() you will need to have root privileges. If you
don't want that, then you can always use setuid.
Programming the parallel port can be a lot fun. The only thing that
can actually be a pain in it is all this inverting you have to do
every now and then. For most simple projects, you only need to use the
8-bits in your data port (ie, the base port).
Finally, I must warn you that if you intend to do anything with your
parallel port, make sure it is not integrated with your motherboard.
Chances are, if you blow something up, it's going to be your
motherboard. What you should so is buy a 386, or any old machine you
can mess around with. I say 386 because it's the first of the 32-bit
processors for the IBM PC (so I can actually run Linux on it). It's a
free world, use whatever your heart desires, just don't test it on
your PII!