Parallel Port Programming (PART 1) : With C

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 15

Parallel Port Programming (PART 1): with C

By Hars

Parallel port is a very commonly known port, widely used to connect the printer to the PC. If you
backside of your computer, there will be a port having 25 pins with a small symbol like this: . Th
is known as LPT port or printer port. We can program this port for device control and data transfer.
article, we will learn basics of parallel port and programming the parallel port.

 
Parallel port basics:

In computers, ports are used mainly for two reasons: Device control and communication. We c
program PC's Parallel ports for both. Parallel ports are mainly meant for connecting the printer to th
But we can program this port for many more applications beyond that.

Parallel ports are easy to program and faster compared to the serial ports. But main disadvant
it needs more number of transmission lines. Because of this reason parallel ports are not used in lo
distance communications. Let us know the basic difference between working of parallel port and se
port. In serial ports, there will be two data lines: One transmission and one receive line. To send a d
serial port, it has to be sent one bit after another with some extra bits like start bit, stop bit and parit
detect errors. But in parallel port, all the 8 bits of a byte will be sent to the port at a time and a indic
will be sent in another line. There will be some data lines, some control and some handshaking line
parallel port. If three bytes of data 01000101 10011100 10110011 is to be sent to the port, followin
figures will explain how they are sent to the serial and parallel ports respectively. We can understan
parallel port communication is faster compared to that of serial.

Parallel port basics:

In computers, ports are used mainly for two reasons: Device control and communication. We c
program PC's Parallel ports for both. Parallel ports are mainly meant for connecting the printer to th
But we can program this port for many more applications beyond that.

Parallel ports are easy to program and faster compared to the serial ports. But main disadvant
it needs more number of transmission lines. Because of this reason parallel ports are not used in lo
distance communications. Let us know the basic difference between working of parallel port and se
port. In serial ports, there will be two data lines: One transmission and one receive line. To send a d
serial port, it has to be sent one bit after another with some extra bits like start bit, stop bit and parit
detect errors. But in parallel port, all the 8 bits of a byte will be sent to the port at a time and a indic
will be sent in another line. There will be some data lines, some control and some handshaking line
parallel port. If three bytes of data 01000101 10011100 10110011 is to be sent to the port, followin
figures will explain how they are sent to the serial and parallel ports respectively. We can understan
parallel port communication is faster compared to that of serial.

For more detail on RS232 ser


 Serial port: Data transmission will be programming and connections, rea
bitwise, one after another.

article "Serial Communication us


RS232 port".  This article explains
port programming with example so
code PC to PC chat in DOS with di
cable connection.

electroSofts.com will soon
you an article on serial port program
in Windows.

figure 1.0                                                         © electroSofts.com

 Parallel Port: Data transmission is byte wise: 


Whole byte at a time.

figure 1.1                                © electroSofts.com
In the PC there will be D-25 type of female connector having 25 pins and in the printer, there will be
pin Centronics connector. Connecting cable will combine these connecter using following conventio
structure of D-25 and Centronics connecters are explained bellow.  

D25- Pin Centronics 36 Pin


Functio
Number Number
1 1 Strobe
2 to 9 2 to 9 Data Lin
10 10 Acknowledg
11 11 Busy
12 12 Out of Pa
13 13 Select
14 14 Auto fee
15 15, 32 Error
16 16, 31 Init
17 17, 36 Select I
18 to 25 18 to 30, 33 GND
 figure 1.2 - 34, 35 N/C
Table 1.0: Pin numbers and functions

 Now let us know how communication between PC and printer takes place. Computer places t
data in the data pins, then it makes the strobe low. When strobe goes low, printer understands that
is a valid data in data pins. Other pins are used to send controls to the printer and get status of the
printer, you can understand them by the names assigned to the pins.

To use the printer port for applications other than printing, We need to know how ports are
organized. There are three registers associated with LPT port: Data register, Control register and S
register. Data register will hold the data of the data pins of the port. That means, if we store a byte
to the data register, that data will be sent to the data pins of the port. Similarly control and status
registers. The following table explains how these registers are associated with ports.

Direction
Pin No (D-Type 25) SPP Signal Register.bit
In/out
1* nStrobe In/Out Control.0
2 Data 0 In/Out Data.0
3 Data 1 In/Out Data.1
4 Data 2 In/Out Data.2
5 Data 3 In/Out Data.3
6 Data 4 In/Out Data.4
7 Data 5 In/Out Data.5
8 Data 6 In/Out Data.6
9 Data 7 In/Out Data.7
10 nAck In Status.6
11* Busy In Status.7
12 Paper-Out / Paper-End In Status.5
13 Select In Status.4
14* nAuto-Linefeed In/Out Control.1
15 nError / nFault In Status.3
16 nInitialize In/Out Control.2
17* nSelect-Printer/ nSelect- In/Out Control.3
In
18 - 25 Ground Gnd  
             Table 1.1: Pin directions and associated registers.

* Pins with * symbol in this table are hardware inverted. Than means, If a pin has a 'low' ie. 0V
Corresponding bit in the register will have value 1.

Signals with prefix 'n' are active low. That means, Normally these pins will have low value. Wh
needs to send some indication, it will become high. For example, Normally nStrobe will be high, wh
data is placed in the port, computer makes that pin low.

Normally, data, control and status registers will have following addresses. We need these add
in programming later.

Register LPT1 LPT2


Data register (Base Address + 0) 0x378 0x278
Status register (Base Address + 1) 0x379 0x279
Control register (Base Address + 2) 0x37a 0x27a
Note: All the parallel ports do not have bidirectional capability. Earlier parallel ports had only o
enabled in data pins since printers only inputs data. But latter, to make parallel port capable of
communicating with other devises, bidirectional ports are introduced.

By default, data port is output port. To enable the bidirectional property of the port, we need to
the bit 5 of control register.

 To know the details of parallel ports available in your computer, follow this procedure:

 Right click on My Computer, go to "Properties".


 Select the tab Hardware, Click Device manager.
 You will get a tree structure of devices; In that Expand "Ports(Com1 & LPT)".  
 Double Click on the ECP Printer Port(LPT1) or any other LPT port if available.
 You will get details of LPT port. Make sure that "Use this Port (enable)" is selected.
 Select tab recourses. In that you will get the address range of port.

To start programming, you will need a D-25 type Male connector. Its pin structures can be foun
the connector as follows:

Programming the printer port in DOS:


To start programming the port, we will use DOS. In DOS we have commands to access the po
directly. But, these programs will not work on the systems based on Windows XP, Windows NT or
versions. For security reason, higher versions of the windows does not allow accessing the port dir
To program the parallel port in these systems, we need to write kernel mode driver. In the part II, I
going to explain about programming the parallel port in windows XP. If you want to run the same pr
in Windows XP, For studying you can use the technique that I have posted in this forum.

When we want to find out whether particular pin of the port is high or low, we need to input the
of corresponding register as a byte. In that, we have to find out whether the corresponding bit is hig
low using bitwise operators.  We can't access the pins individually. So, you need to know basic bitw
operations.

Main bitwise operators that we need are bitwise AND '&' and bitwise OR '|'. To make a particul
in a byte high without affecting other bits, write a byte with corresponding bit 1 and all other bits 0; O
with original byte. Similarly, to make particular bit low, write a byte with corresponding bit 0 and all o
bits 1; AND it with original byte.

In Turbo C, there are following functions used for accessing the port:

 outportb( PORTID, data);


 data = inportb( PORTID);
 outport( PORTID, data);
 data = inport( PORTID);

outport() function sends a word to port, inport() reads a word from the port. outportb() sends a
port and inportb() reads a byte from the port. If you include DOS.H header, these functions will be
considured as macro, otherwise as functions. Function inport() will return a word having lower byte
data at PORTID and higher byte as data at PORTID+2. So, we can use this function to read status
control registers together. inportb() function returns byte at PORTID. outport() writes the lower byte
PORTID and higher byte to PORTID+1. So this can be used to write data and control together. out
function write the data to PORTID. outport() and outportb() returns nothing.

Let us start with inputting first. Here is an example program, copy it and run in Turbo C or Borl
without anything connected to parallel port. Then you should see data available in status register a
numbers 10, 11, 12, 13 and 15 of the parallel port. Pin 11 (active low) is 0 and all other pins are 1 m
it is OK.

/*     file:  ex1.c


       by HarshaPerla for electroSofts.com.
       Displays contents of status register of parallel port.
       Tested with TurboC 3.0 and Borland C 3.1 for DOS.
*/

#include"stdio.h"
#include"conio.h"
#include"dos.h"

#define PORT 0x378


void main()
{
    int data;
    clrscr();
    while(!kbhit())
    {
        data=inportb(PORT+1);
        gotoxy(3,10);
        printf("Data available in status register: %3d (decimal), %3X
(hex)\n", data, data);
        printf("\n Pin 15: %d",(data & 0x08)/0x08);
        printf("\n Pin 13: %d",(data & 0x10)/0x10);
        printf("\n Pin 12: %d",(data & 0x20)/0x20);
        printf("\n Pin 11: %d",(data & 0x80)/0x80);
        printf("\n Pin 10: %d",(data & 0x40)/0x40);
        delay(10);
    }
}
To understand bitwise operations: you want to find data in pin 15, value of (data & 0x08) will be 0x0
3 of register is high, 0therwise.   

     bit no. 7654 3210


     bit no. 7654 3210
      data : XXXX 0XXX 
      data : XXXX 1XXX 
   & with : 0000 1000   (0x08 )
   & with : 0000 1000   (0x08 )
           -> 0000 0000   (0x00 -> bit 3 is low)
           -> 0000 1000   (0x08 -> bit 3 is high )
We will use the same logic throughout the article.

Now, take a D-25 male with cables connected to each pins. Short all the pins from 18 to 25, ca
ground. Now you can run above program and see the change by shorting pins 10, 11, 12, 13 and 1
ground. I prefer using switches between each input pins and ground. Be careful, do not try to groun
output pins.  

To find out the availability of ports in a computer programmatically, we will use the memory loc
where the address of port is stored.

0x408 0x409 0x40a 0x40b 0x40c 0x40d


LPT3
LPT1low LPT1high LPT2low LPT2high LPT3low
high
byte byte byte byte byte
byte
If you run the the following code in Turbo C or Borland C, You will get the addresses of available po

/*PortAdd.c
  To find availability and addresses of the lpt ports in the computer.
*/

#include <stdio.h>
#include <dos.h>
void main()
{
    unsigned int far *ptraddr; /* Pointer to location of Port Addresses
*/
    unsigned int address;        /* Address of Port */
    int a;
    ptraddr=(unsigned int far *)0x00000408;
    clrscr();

    for (a = 0; a < 3; a++)


    {
        address = *ptraddr;
        if (address == 0)
            printf("No port found for LPT%d \n",a+1);
        else
            printf("Address assigned to LPT%d is 0x%X
\n",a+1,address);
        ptraddr++;
    }
    getch();
}
         Next we will go to check output pins. To check the output, we will use LED's. I have driven LE
directly from the port. But it is preferred to connect a buffer to prevent excessive draw of current fro
port. Connect an LED in series with a resister of  1KW or 2.2KW between any of the data pins(2 to
ground. With that, if you run the program given below, you should see the LED blinking with app. 1
frequency.

#include"conio.h"
#include"dos.h"

#define PORT 0x378

void main()
{
    while(!kbhit())
    {
        outportb(PORT, ~inportb(PORT) );
        delay(1000);
    }
}
 We can easily program the parallel port in DOS. But as we know, DOS
programs have their own limitations. So, if you want to move from DOS to
Windows, go through this article. This is an introduction to program the
parallel port in VC++. You need not have much knowledge about VC++.
This article is designed for one who know basics of parallel port and
beginners of VC++. If you don't know anything about parallel port, read my
first article "Parallel port programming with C (Part 1)". There you get basic
information about parallel port and programming the port in Turbo C or
Borland C.

         Now, you are knowing the pins and registers of parallel port. You
know how to access them in DOS. If you want to run your program in
Windows 95 or 98, you are having access to the port in the similar way.
You need to know how to use dialog boxes and windows materials with it.
But your program should also run in Windows XP, NT or higher versions,
then there is another issue. Higher versions of Windows do not allow to
access the hardware directly for security reasons. But still, there are ways, I
will explain later. First we will start programming which will work only in
lover versions of Windows.

Direct Access:

        If you want to program the port in VB, there is no direct access to the
port. Still you can access the port using DLL files created with VC++. You
can use the next method Access using inpout32.

        If you are familiar with Visual C++, then create a dialog based
application named ParallelPort and skip this section, go to adding controls.

Creating the application:

 Start Visual C++, Select File->New.


 In the tab 'Projects', Select "MFC AppWizard (exe)", give project
name as "ParallelPort" and click OK.
 In the next window, select radio button "Dialog based" and click next
leaving all other options default.
 Click Finish, then OK to get a window with two buttons and one
sentence "TODO: Place Dialog controls here.", select and delete that
sentence. Click to select the button "Cancel" and delete it.
 Right click on button labeled OK, select Properties from the drop
down menu. Change the value of Caption to "&EXIT" from "OK".
 Resize the dialog box to get a window as shown below. If you run the
application by clicking this icon:  , it should give 0 errors and 0
warnings, and you will get the following window.
figure(2.1)

Adding controls: 

      Now, you should see a tool bar as shown here, it is called Control toolbar. If not, select
it from view menu->toolbars. Icon marked here with red color is the Check Box. If you click
the check box icon and draw in the window, You will have the check box placed in the
window. You need to place 17 such check boxes in the window. You can use copy-past to
make your work easy. After that, Group then using 3 group boxes. Group box icon is there
above the check box in the figure. After doing this much, your design should look like
figure(2.3). So, re arrange your dialog components to look like that.  Again run the
application and make sure that there is no error.
figure(2

Figure(2.3)

      Next, right click on the Group Box labeled Static and go to properties.
Change the captions to Data, Status and Control respectively. Right click
on the first Check box Check 1, Change the caption to "Pin 2" and ID to
IDC_Pin2. Similarly change the captions of check boxes in data group to
Pin 2 to Pin 9; status port Pin 10, Pin 11, Pin 12, Pin 13 and Pin 15; Control
Port Pin 1, Pin 14, Pin 16 and Pin 17. Change the ID's
correspondingly(IDC_Pin2, IDC_Pin3...).

      Window designing is over. Next part is coding. We have placed some
controls in the dialog box. To get the values of these controls, we need to
have variables associated with then. To do that, right click and select
"ClassWizard" from drop down menu. Select tab "Member Variables". You
will get a list of Control IDs. Select each IDs separately and click Add
Variable. Type variable name as m_pin1. m_pin10, m_pin11... and retain
Category Value and Variable type BOOL. Refer following figure.

Figure(2.4)    

       In the Workspace, Select ClassView tab, under ParallelPort classes,


right click on CParallelPortDlg, click Add Member Function. Give function
type as void and function name as UpdatePins(). It will take you to the new
function created. Edit the code as follows.

void CParallelPortDlg::UpdatePins()
{
     int reg;
     reg=_inp(STATUS);

     if((reg & 0x40)==0) m_pin10=0;     else m_pin10=1;


     if((reg & 0x80)==0) m_pin11=0;     else m_pin11=1;
     if((reg & 0x20)==0) m_pin12=0;     else m_pin12=1;     
     if((reg & 0x10)==0) m_pin13=0;     else m_pin13=1; 
     if((reg & 0x08)==0) m_pin15=0;     else m_pin15=1;
     //////////
     reg=_inp(DATA);
     
     if((reg & 0x01)==0) m_pin2=0;     else m_pin2=1;
     if((reg & 0x02)==0) m_pin3=0;     else m_pin3=1;     
     if((reg & 0x04)==0) m_pin4=0;     else m_pin4=1; 
     if((reg & 0x08)==0) m_pin5=0;     else m_pin5=1;
     if((reg & 0x10)==0) m_pin6=0;     else m_pin6=1; 
     if((reg & 0x20)==0) m_pin7=0;     else m_pin7=2;
     if((reg & 0x40)==0) m_pin8=0;     else m_pin8=1;
     if((reg & 0x80)==0) m_pin9=0;     else m_pin9=1;
     ////// 
     reg = _inp(CONTROL);

     if((reg & 0x01)==0) m_pin1=0;      else m_pin1=1;


     if((reg & 0x02)==0) m_pin14=0;     else m_pin14=1;
     if((reg & 0x04)==0) m_pin16=0;     else m_pin16=1;
     if((reg & 0x08)==0) m_pin17=0;     else m_pin17=1;

     UpdateData(FALSE);
}

            Now scroll to the top of the page and add these lines before class
declarations.

#define DATA 0x378
#define STATUS 0x379
#define CONTROL 0x37a

      The function CParallelPortDlg::UpdatePins() is used to display values of


all pins initially. Here, we have used _inp() function to get the values of
registers associated with the ports. _inp(PORT) will return the data present
in PORT. Depending on the status of the pins, we are making Check boxes
checked or unchecked. When we change the value of member variable and
call the function UpdateData(FALSE), the values in the member variables
will be updated in the corresponding controls in the window. Similarly if you
call UpdateData(TRUE), Values which are there in the corresponding
controls will sit in the member variables. Here, The values from the
variables should be updated in the window. So, UpdateWindow(FALSE). If
you have read my first article, you will understand all other things done
here.

      To make run this code when the dialog is initialized, we need to call it.
So, go to function OnInitdialog() in the file CParallelPortDlg. (In the class
view tab of the workspace, under ParallelPort Classes, expand
CParallelPortDlg, you will get the function name, double click it.) Add the
following code to it. This code will call the function UpdatePins() and set a
timer to scan the port pins. You can change the second parameter to
change the frequency at which ports are needed to be scanned. I have
used 200 milli seconds. _outp(CONTROL, 0xDF) will reset the control
register bit 5 low so that data pins will act as output._outp(PORT, DATA)
sends the byte DATA to the address PORT.
BOOL CParallelPortDlg::OnInitDialog()
{
    //App.Wiz generated code 
    // TODO: Add extra initialization here
    SetTimer(1,200,NULL);
    _outp(CONTROL, _inp(CONTROL) & 0xDF);
    UpdatePins();
    return TRUE; // return TRUE unless you set the focus to a control
}
 

            Next part is Updating the pin contents for each timer tics. For that,
we need to handle the windows message WM_TIMER. Now since we have
set the timer for 200 ms, for every 200 ms, Windows returns WM_TIMER
message. To write a handler, write click on the CParallelPortDlg in the
class view tab, select "Add Windows Message Handler...". In "New
Windows messages/events", select WM_TIMER and click Add and Edit. It
will take you to the newly created function CParallelPortDlg::OnTimer(UINT
nIDEvent). Add the following code to it.

void CParallelPortDlg::OnTimer(UINT nIDEvent) 
{
    // TODO: Add your message handler code here and/or call default
    int status_reg;
    status_reg=_inp(STATUS);
  
    if((status_reg & 0x40)==0) m_pin10=0;    else m_pin10=2; 
    if((status_reg & 0x80)==0) m_pin11=0;    else m_pin11=1;
    if((status_reg & 0x20)==0) m_pin12=0;    else m_pin12=1;
    if((status_reg & 0x10)==0) m_pin13=0;    else m_pin13=1;
    if((status_reg & 0x08)==0) m_pin15=0;    else m_pin15=1;

    UpdateData(FALSE);
    CDialog::OnTimer(nIDEvent);
}

         Here, we have refreshed only input pins. Output pins have to be
changed when user clicks on the check boxes. To find any change of value
in check boxes, we can use BN_CLICKED message handler. But for all the
check boxes we have to repeat the process. It is easy to use
ON_COMMAND_RANGE. For that, scroll up to the position in the file
ParallelPortDlg.cpp where you find
BEGIN_MESSAGE_MAP(CParallelPortDlg, CDialog). (Do not confuse
between CParallelPortDlg and CAboutDlg.)  Add the following code to it.
BEGIN_MESSAGE_MAP(CParallelPortDlg, CDialog)
     //{{AFX_MSG_MAP(CParallelPortDlg)
     ON_WM_SYSCOMMAND()
     ON_WM_PAINT()
     ON_WM_QUERYDRAGICON()
     ON_WM_TIMER()
     //}}AFX_MSG_MAP
     //Code added by me from here.
     ON_COMMAND_RANGE(IDC_Pin2, IDC_Pin9, ChangePin)           
     ON_COMMAND(IDC_Pin14, ChangeControl)
     ON_COMMAND(IDC_Pin16, ChangeControl)
     ON_COMMAND(IDC_Pin17, ChangeControl)
     ON_COMMAND(IDC_Pin1, ChangeControl)
     //Code added by me till here
END_MESSAGE_MAP()

         Above code will call the function ChangePin() when buttons in the
range IDC_Pin2 to IDC_Pin9 are changed and ChangeControl() when
chack buttons with ID IDC_Pin14, IDC_Pin16, IDC_Pin17 or IDC_Pin1 are
changed. Now we need those two functions. Add two new functions to
CParllelPortDlg: void 'ChangePin()' and 'void ChangeControl()' using the
method explained earlier. Write codes to them as follows:

void CParallelPortDlg::ChangePin(int pin)
{

int data_register, new_register;

UpdateData(TRUE);
data_register=_inp( DATA );
new_register=0;
if( m_pin2==TRUE ) new_register |= 0x01;
if( m_pin3==TRUE ) new_register |= 0x02;
if( m_pin4==TRUE ) new_register |= 0x04;   
if( m_pin5==TRUE ) new_register |= 0x08;
if( m_pin6==TRUE ) new_register |= 0x10;
if( m_pin7==TRUE ) new_register |= 0x20;
if( m_pin8==TRUE ) new_register |= 0x40;
if( m_pin9==TRUE ) new_register |= 0x80;

_outp(DATA, new_register);

void CParallelPortDlg::ChangeControl()
{
int control_register, new_register;

UpdateData(TRUE);

control_register = _inp( CONTROL );


new_register = control_register;

if( m_pin1== 0 ) new_register &= 0xFE;


    else new_register |= 0x01; 
if( m_pin14==0 ) new_register &= 0xFD;
    else new_register |= 0x02; 
if( m_pin16==0 ) new_register &= 0xFB;
    else new_register |= 0x04; 
if( m_pin17==0 ) new_register &= 0xF7;
    else new_register |= 0x08; 

_outp(CONTROL, new_register);

      If everything is OK, you should get the following window when you run
the program. To test the program, run this program without connecting
anything to the port. Change some of the pins and close the window. If you
run the program again you should get the values which was there before
closing the window in the output pins.

          You can always use this program to test the parallel port. Now, make
a circuit connecting all the input pins to switches, all the output pins to
LEDs with 2.2K or 10K resisters. If you press switch, corresponding pin
value should change in the screen, If you change state of any output pin,
corresponding LED should glow.
      Every thing is ok. But as you know, this program will run only in win9x.
If your program is needed to run in windows xp and higher versions, you
need to write a kernel mode device driver. Do not worry if you are not up to
that level. There are DLL files available freely for such drivers. You can use
those files and call them from your program.

Access using inpout32

If you do not want to use driver and test the above program in Windows XP
as it is, use my post in the ElectroSofts Forum

        InpOut32 is a DLL file which can send a data to parallel port and
which can return the data in the parallel port. You can download this file
with source code for free from http://logix4u.net. You can use this file in
any  windows programming language like Visual basic, C#, C++ etc. If you
know how to use DLL files, download the file from http://logix4u.net and use
Inp32() and Out32() functions instead of _inp() and _outp().

      To know how to use DLL file in VC++, let us now convert our previous
project to XP enabled program.

 Add these two lines in the file ParallelPortDlg.cpp after pre processor
directives.

short _stdcall Inp32(short portaddr);


void _stdcall Out32(short portaddr, short datum);

 Where ever _inp() comes, change them to Inp32() and where ever
outp() comes, change them to Out32().
 Copy DLL file inpout32.dll and lib file inpout32.lib got by compiling the
source code available at logix4u.net to the project folder.
 From project menu, select settings, go to tab link, in object/ library
modules write inpout32.lib
 Now your program should run without any errors.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy