============================================================================== 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!