C-Scene Issues 1..9 Authors
Algorithms Books Patterns Graphics Miscellaneous UNIX Web & XML Windows
Feedback FAQs Changes Submissions

Parallel Port Programming

Content

  1. Introduction
  2. The Basics
  3. The Output
  4. The Input
  5. Problems and Solutions
  6. Other Handy Macros
  7. Conclusion

by Mohammed Elzubeir
last updated 2001/10/29 (version 1.3)
also available as XML

IRC: kuru @EFnet's #c
Email: elzubeir@fakkir.net

Introduction

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 
/Strobe  PC Output (OC) 
Data_0  PC Output 
Data_1  PC Output 
Data_2  PC Output 
Data_3  PC Output 
Data_4  PC Output 
Data_5  PC Output 
Data_6  PC Output 
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 Intput 
16  Init Printer  PC Output 
17  /Select_Input  PC Output 
18  Ground  N/A 
-25  N/A  N/A 

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:

The Output

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:

   #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);
  }
  

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!].

The Input

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 places to the right, resulting in the upper five bits in the lower five bit potisions.

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.

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 weould 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().

Note In order to use ioperm() you will need to have root privileges. If you don't want that, then you can always use setuid.

Conclusion

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!

This article is Copyright © 1998 by C-Scene. All Rights Reserved.


Copyright © 1997-2000 by C-Scene. All Rights Reserved.

Part of the graphics and stylesheets used to generate this site are
Copyright © 1999-2000 by Apache Software Foundation.