Communications and Networks


Using the PC's Parallel Port for Bidirectional Communications

William Smith


William Smith is the engineering manager at Montana Software, a software development company specializing in custom applications for MS-DOS and Windows. You may contact him by mail at P.O. Box 663, Bozeman, MT 59771-0663.

Although most commonly used for printing, a PC's parallel port is also available for other bidirectional communications, such as high-speed data transfer between computers. In particular, the parallel port meets the growing need for a means of transferring data at high speed between portable and desktop computers.

The parallel port was originally intended to be unidirectional. As a result, apart from the monitoring of some status lines, the port's ability to receive information through the data lines is limited by its electrical design. (If care is taken electronically, the parallel port can receive data through the data lines. The data lines have a fanout of one TTL device, which limits them to being able to source 2.6 milliamps of current. They can sink up to 24 milliamps.) Newer, true IBM PS/2 computers have changed the design to make the parallel port more suitable as an input device, but I do not know the details of these changes. In general, you should not assume that your computer has a parallel port capable of receiving data in a robust fashion through the data lines. If you use the parallel port data lines for input without some current-limiting resistors, you risk destroying the port's driver ICs. A better, safer approach is to use the status lines for input and limit the use of the data lines to output.

This article presents the C code necessary to get you started on using the parallel port status lines for input.

Parallel Port Specifications

The parallel port connector on your computer has 25 pins, or lines. Eight are used for data, five for status, and four for control. The leftover lines are ground lines. Each printer port has three bytes that map into these lines. Byte 0 is the output, byte 1 is the status, and byte 2 is the control. Bits 1 and 2 in the status byte and bits 5 through 7 in the control byte are not used. Bit 0 in the status byte is not mapped to a physical line; the PC's BIOS sets this internally.

Table 1 lists the parallel port's lines. A review of how the standard operation of printing uses these lines can help to clarify their functions. The eight data lines, pins 2 through 9, carry the output information from the parallel port to the printer, with each line corresponding to one byte or character.

The status lines, pins 10 through 13 and pin 15, carry information from the printer to the computer. The printer holds the Not Error line high to tell the computer that all is well; if something is wrong, it goes low. The printer holds the Selected line high to tell the computer it is on-line, and holds the Paper Out line high to indicate that it is out of paper. The printer uses the Acknowledge line for timing, to tell the computer it has received and printed the last character, and uses the Not Busy line to tell the computer whether it is ready to accept another character.

The control lines — pins 1, 14, 16, and 17 — carry special control information from the computer to the printer. The computer uses the Strobe line for timing to tell the printer to read the data lines. (It is the reverse of the Acknowledge line.) The computer can use the Auto Feed line to control how the printer handles line-feed characters: if the computer holds this line high, the printer requires a line-feed character to advance the paper. The Initialize line will reset the printer. The computer holds this line high and drives it low to trigger a reset. The computer can use the Select Input line to switch the printer on and off line. Low is on-line and high is off-line.

Implementation

You have three ways of implementing bidirectional communication through the parallel port. The first — and most conventional — works through the operating system and requires using the data lines for input (see the cautions mentioned earlier about reading the data lines). The standard stream and file I/O functions in C will allow you to write and read from the parallel port, treating the parallel port as you would a file or device. You first open the device with fopen or open. To process input through the parallel port, you read the data lines with the fread or read family of functions. Output requires writing to the data lines with the fwrite or write family of functions. Because of the electronic limitations of the parallel port described above, dealing with input can be risky. Moreover, this method does not allow you to monitor or read the status lines.

The second and third methods both involve reading the status lines for input and writing to the data lines for output. These two methods differ in how they read the status: you can either go through the BIOS or read the parallel port directly. Fortunately, most C compilers for the PC have functions that support both methods. Under Microsoft C (MSC) the function _bios_printer can read the status lines by using a BIOS call. Once you know the port address, the MSC function inp can read the status lines directly. Under Borland C (BC) the corresponding functions are biosprint and inportb. Listing 1, PAR_COMM.C, is the MSC implementation of both the BIOS and the port method. Listing 2, PAR_COMM.H, is the associated include file for PAR_COMM.C.

You must do a little extra work before reading or writing to the port directly. You first must obtain the port address. The computer stores printer port addresses in a table in the low DOS memory area at segment 40h and offset 8h. There can be as many as four values (LPT1 through LPT4). IBM PS/2 computers support only three; typically, there is only one printer port on a computer. Each value takes up two bytes in the table and there are four locations in the table. The function get_prn_port, Listing 1, retrieves a desired port address. If a requested printer port is not installed, get_prn_port returns 0. Writing values to this table allows to you play tricks with printer ports. You can swap port addresses or assign the same port address to more than one location in the table. This can make it appear that you have more than one printer when you really do not.

The status lines are appropriate for input only. The same warnings apply to writing to the status lines as inputting from the data lines, since there is no guarantee that your computer supports writing to the status lines. The functions in Listing 1 support reading the status lines only.

It makes sense to use the data lines only for output and the status lines only for input. Communication between two parallel ports requires a special cross-over cable to support this practice. The cable maps the data lines to the status lines and vice versa. Since there are only five status lines compared to eight data lines, you cannot use all the data lines in parallel-port to parallel-port communication. I find working with four lines or bits easier than five bits. Four bits, or a nibble, is the next logical step down in size from an eight-bit byte. Consequently, I use the four highest bits for both the status and data lines. To wire up the cable, swap pins as follows: 11 with 9, 10 with 8, 12 with 7, and 13 with 6. Swapping pin 15 with 5 allows you to handle five-bit transfers. You can then use the fifth bit for status, timing, or some other protocol indicator of your choosing.

You must account for a slight complication with the status lines. The parallel-port hardware actually inverts the signals on line 11 (bit 7) and line 15 (bit 3). To further complicate things, the BIOS function also inverts some signals. The BIOS function does an additional inversion of lines 15 (bit 3) and 10 (bit 6). As a result, the status values obtained using the BIOS and reading the port are different from what you expect. They differ not only from the physical condition of the lines, but also from each other. Table 2 lists the status lines and what the BIOS and the port return for a given line level. The functions correct_bios_status and correct_port_status correct the status values so that they match with the actual line levels.

How you go about implementing the specifics of data exchange and what you do with the fifth bit will be very implementation dependent. The functions in_bios_nibble and in_port_nibble represent a possible implementation — and one that I have used for laboratory instrument communications. These functions return an unsigned int. The low four bits contain a nibble of information. These are lines 11, 10, 12, and 13 or status bits 7, 6, 5, and 4. Status bit 7 corresponds to nibble bit 4 and so on. I also use the fifth bit, line 15 or status bit 3. in_bios_nibble and in_port_nibble take this bit and use it to set the sign bit of the return value.

Since you can handle output through the data lines by conventional means, you need not create special functions for this purpose. Just use fopen, fwrites and/or fputc. You will have to determine the best method for coordinating the two processes (i.e., output through the data lines and input through the status lines). The methodology you select will very likely differ for parallel-port to parallel-port communications as opposed to communications between a parallel port and a unique piece of electronic hardware.

Conclusions

The parallel port was originally intended to serve as an output-only printing port. As the portable computer market has grown, the need for fast and simple data transfer between portable and desktop computers has led to the exploitation of the parallel port for bidirectional communications. Although the standard parallel port was not originally designed for this purpose, using the status lines creatively makes bidirectional communication possible and reliable.

As this methodology becomes better known, uses such as data acquisition and instrument control will become attractive. Using the parallel port eliminates the need to add a dedicated and proprietary controller expansion board to your computer. Since nearly all computers have a parallel port, even a notebook computer with no expansion slots becomes a candidate for a data acquisition or controller tool. In short, using the parallel port in a bidirectional manner opens the door for inexpensive, portable, and simple interfacing to electronic hardware.