The information provided here is limited to the minimum. More information (e.g.: introductions, description of parallel port etc.) can be found on following sites:
Site | Comment |
---|---|
The EE Compendium | Data tables and many links |
Beyond Logic | Description of the parallel port and how to use it |
Name | Base Address | Alternative Base Address |
---|---|---|
LPT1 | 0x0378 | 0x03BC |
LPT2 | 0x0278 | 0x0378 |
LPT3 | 0x03BC | None |
Register | Direction | Address | Mask |
---|---|---|---|
Data Register | Input/Output | Base Address | 0xFF |
Status Register | Input | Base Address + 1 | 0xF8 |
Control Register | Output | Base Address + 2 | 0x0F |
Remarks:
Register | Bit Number | Bit Mask | Functionality Name | Inverted | Pin |
---|---|---|---|---|---|
Data Register | 0 | 0x01 | Bit 0 of data byte | No | 2 |
Data Register | 1 | 0x02 | Bit 1 of data byte | No | 3 |
Data Register | 2 | 0x04 | Bit 2 of data byte | No | 4 |
Data Register | 3 | 0x08 | Bit 3 of data byte | No | 5 |
Data Register | 4 | 0x10 | Bit 4 of data byte | No | 6 |
Data Register | 5 | 0x20 | Bit 5 of data byte | No | 7 |
Data Register | 6 | 0x40 | Bit 6 of data byte | No | 8 |
Data Register | 7 | 0x80 | Bit 7 of data byte | No | 9 |
Status Register | 3 | 0x08 | Error | No | 15 |
Status Register | 4 | 0x10 | Select | No | 13 |
Status Register | 5 | 0x20 | PaperEnd | No | 12 |
Status Register | 6 | 0x40 | Acknowledge | No | 10 |
Status Register | 7 | 0x80 | _Busy | Yes | 11 |
Control Register | 0 | 0x01 | _Strobe | Yes | 1 |
Control Register | 1 | 0x02 | _LineFeed | Yes | 14 |
Control Register | 2 | 0x04 | Initialize | No | 16 |
Control Register | 3 | 0x08 | _SelectIn | Yes | 17 |
Remarks:
To use ParaPort, its driver must first be installed by the Windows "Add/Remove Hardware Wizard".
Client applications ( executables ) do not access directly the driver, but use the Application Programming Interface ( "API" ) of the "ParaPort.dll" dynamic library. They generally follows these steps:
The ParaPortUtility application uses the underlying dll and driver to set or clear pins of the parallel port.
The central information item of ParaPort is a "cycle". This is a structure ( in the sense of C or C++ ) containing seven bytes:
A special direction bit ( 0x20 ) of the control register determines whether the data register is used as output ( bit cleared ) or input ( bit set ).
As example:
PARAPORT_CYCLE::Data
must be set to
0x20 ( in comparison with the data table ). PARAPORT_CYCLE::MaskData
must also set to 0x20.PARAPORT_CYCLE::Data
must be set to 0x00. PARAPORT_CYCLE::MaskData
must set to 0x20.Each cycle is seen as one moment in time. But since there are CPU instructions to execute on the PC and electronic reaction on parallel port hardware, the following timing diagrams can be measured on the parallel port:
All register are written or read ( the lines marked with "*" ) only, if the correspondent mask byte is not equal to 0x00.
The following figure has been measured with a logical analyser during the execution of the sample application "sampleRepeatFactor". As you can see in the source code, different pulses has been generated using each two cycles.
Each cycle lasts approximate 5 micro seconds. By setting the byte PARAPORT_CYCLE::RepeatFactor
in the structure, a cycle
can be extended ( e.g.: cycles 3, 5, 7 ).
To extend the time between pulses, the cycles 2, 4, 6 should get a value > 0 for the byte PARAPORT_CYCLE::RepeatFactor
.
Pulsed or Clocked Input / Output
During one cycle, the state of the pins can be set, cleared or read. To realize "Pulsed or Clocked I/0", two cycles are needed for each pulse, if the hardware is "edge driven". In other cases, three cycles are needed.
As example 1:
To write 0xAE on the data register with a clock (e.g.: _Strobe), two cycles are needed:
PARAPORT_CYCLE |
Cycle 1 | Cycle 2 |
---|---|---|
Control |
0x01 | 0x00 |
MaskControl |
0x21 | 0x01 |
Data |
0xAE | 0x00 |
MaskData |
0xFF | 0xFF |
Data
must be changed; if the last set value ( here 0xAE ) is wanted, the MaskData
must set to 0x00. If the default state of the data register is input, the bit 0x20 must be set in the Control
structure member.
As example 2:
To read a byte from the data register with a clock (e.g.: _Strobe), two cycles are needed:
PARAPORT_CYCLE |
Cycle 1 | Cycle 2 |
---|---|---|
Control |
0x21 | 0x00 |
MaskControl |
0x21 | 0x01 |
Data |
read value | 0x00 |
MaskData |
0xFF | 0xFF |
Data
structure member.
Data
must be changed; if the last set value
( here 0xAE ) is wanted, the MaskData
must set to 0x00. If the default state of the data register is input, the bit 0x20
must be set in the Control
structure member.
ParaPort offers to its client applications written in C or C++ an Application Programming Interface ( API ) for the exported functions of ParaPort.dll.
The C interface of ParaPort is implemented in the header file "ParaPort.h" and presents all C type definitions and function prototypes needed to access the dll.
The C++ interface of ParaPort contains C++ classes which wraps the C functions and C structures:
ParaPortCycle
in header file "ParaPortCycle.h" proposes "get" / "set" access methods to the members of the C structure PARAPORTCYCLE
.
ParaPortDll
in header file "ParaPortDll.h" does the standard work of a DLL client to load the DLL ( ::LoadLibrary( )
), determine
the addresses of the DLL exported functions ( ::GetProcAddress( )
) etc.
The usage of ParaPort can be viewed in source code of the sample program.
Description:
Before writing data to or reading data from the parallel port using ParaPortCycle
, the client application must
get a handle from ParaPort to a port by specifying its name ( e.g.: "LPT1" ). This handle will be used to call the other methods.
Method:
HANDLE openPort( const char* PortName );
Parameter:
Type | Parameter | Description |
const char* |
PortName |
Not case sensitive character array with name of port "LPT1" to "LPT3". Some computers support "LPT4" or more. Valid values are e.g.: "LPT1", "Lpt2", "lpt3", "lpT4" etc. |
Return value:
Type | Possible Values | Description |
HANDLE |
INVALID_HANDLE_VALUE |
Error occurred, call ::GetLastError() from Windows API to get information about the error |
all other values | a valid handle ( in the sense of Windows API ), which can be used as parameter of the other methods |
Description:
The following information can be extracted from a port:
Description:
This is the central functionality to write data to or read data from the parallel port.
Types:
typedef struct {
UCHAR Reserved;
UCHAR Data;
UCHAR Status;
UCHAR Control;
UCHAR MaskData;
UCHAR MaskStatus;
UCHAR MaskControl;
UCHAR RepeatFactor;
} PARAPORT_CYCLE;
Method:
BOOL executeCycle( HANDLE Handle, PARAPORT_CYCLE* ParaPortCycle, int Count );
Parameter:
Type | Parameter | Description |
HANDLE |
Handle |
handle of the port ( returned by openPort( ) ) |
PARAPORT_CYCLE* |
ParaPortCycle |
array of cycles |
int |
Count |
number of cycles in the array |
Return value:
Type | Possible Values | Description |
BOOL |
TRUE |
Method was executed successfully |
FALSE |
Error occurred, call ::GetLastError() from Windows API to get information about the error |
Description:
This functionality gives direct access to the parallel port registers. These functions have the __stdcall
declarators, so they can be called from Visual Basic applications.
Types:
PARAPORT_ADDRESS
Methods:
PARAPORT_ADDRESS getPortAddress( const char* PortName );
UCHAR input( PARAPORT_ADDRESS Address );
UCHAR output( PARAPORT_ADDRESS Address, UCHAR Byte );
Parameter:
Type | Parameter | Description |
const char* |
PortName |
Not case sensitive character array with name of port "LPT1" to "LPT3". Some computers support "LPT4" or more. Valid values are e.g.: "LPT1", "Lpt2", "lpt3", "lpT4" etc. |
PARAPORT_ADDRESS |
Address |
address of the port ( returned by getPortAddress( ) ) |
UCHAR |
Byte |
byte to be written |
Return value:
Type | Possible Values | Description |
PARAPORT_ADDRESS |
Address |
address of the port |
UCHAR |
byte to be read |
Method was executed successfully |
PARAPORT_BYTE_ON_ERROR | Error occurred, call ::GetLastError() from Windows API to get information about the error |
Description:
If a port is no more accessed, the client application must close the handle of the port.
Method:
BOOL closePort( HANDLE Handle );
Parameter:
Type | Parameter | Description |
HANDLE |
Handle |
handle of the port ( returned by openPort( ) ) |
Return value:
Type | Possible Values | Description |
BOOL |
TRUE |
Method was executed successfully |
FALSE |
Error occurred, call ::GetLastError() from Windows API to get information about the error |
Description:
All methods of ParaPort uses the error handling mechanism of Windows by using the functions ::SetLastError( )
and ::GetLastError( )
from Windows API.
ParaPort specific errors are C defines in the custom range beyond 0x20000000
. There are also some common Windows errors returned by the functions.
ParaPort specific defines:
PARAPORT_ERROR |
start of the ParaPort error range | |
PARAPORT_ERROR_INTERNAL_1 |
all functions | internal error, should never arrive |
PARAPORT_ERROR_INTERNAL_2 |
all functions | internal error, should never arrive |
PARAPORT_ERROR_INTERNAL_3 |
all functions | internal error, should never arrive |
PARAPORT_ERROR_INVALID_HANDLE |
all functions | the handle given as parameter is invalid |
PARAPORT_ERROR_INVALID_PORTNAME |
openPort( ) or getPortAddress( ) |
the name of the port is invalid |
PARAPORT_ERROR_LIBRARY_NOT_IMPLEMENTED |
all functions | this functionality is not ( yet ) implemented |
PARAPORT_ERROR_LIBRARY_NOT_LOADED |
all functions | |
PARAPORT_BYTE_ON_ERROR |
input( ) or output( ) |
the address of the port |
PARAPORT_ERROR_INVALID_CYCLE |
executeCycle( ) |
ParaPortCycle == NULL |
Windows errors:
ERROR_NOT_ENOUGH_MEMORY |
executeCycle( ) |
returned, if memory allocation fails in case of limited memory resources on the computer |
Description:
The C++ interface implements the singleton design pattern for the class ParaPortDll
.
The client application normally uses only one instance of the class, which is created by the first call of the static method getSingleton( )
.
Following calls of this method do not create any new objects, but return the address of the instance created by the first call.
If the object is no longer needed, it can be deleted by calling the method deleteSingleton( )
.
Method:
static ParaPortDll* getSingleton( );
static void deleteSingleton( );
"ParaPort.sys" is a Windows kernel driver following Microsoft's Windows Driver Model (WDM). The driver can be controlled ( start, stop, install, uninstall ) via the "Device Manager" of the computer.
ParaportUtility is a simple dialog box based windows application which can be used to set or clear pins on parallel port connectors. To be operational, the application needs the driver "ParaPort.sys" being installed and the "ParaPort.dll" must be visible to "ParaPortUtility.exe".
The Graphical User Interface consists of the following sections:
A checked box in the "Data Register", "Status Register" or "Control Register" ( e.g.: like "Pin9: D7" ) means that the correspondent pin on the parallel port connector has 5 Volt TTL. An unchecked box means 0 Volt TTL.
FAQ General
ParaPort ( in its current releases ) is not compatible with Windows NT4. The reason is historical:
There will be an "direct access" API (simple inp( )
and outp( )
in release v2.0 of ParaPort.
ATTENTION: If you get a eMail from "contact@paraport.net" with the following informations:
you MUST DELETE THE MESSAGE without executing the attached file! Executing the attached file will infect your Windows system with the virus "W32.Sobig.E@mm".
More information about the virus can be found on Symantec .
FAQ about "ParaPort.dll"
The library of ParaPort has the form of a "*.dll" file because of the following advantages:
My implementation of the Singleton is an extension of the singleton published by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. The Problem of their implementation is that they did not preview to delete of the allocated memory ( even at end of the process ). Some development utilities verifying, that all allocated memory is also freed, will signal memory leaks at the end of the process.
To read data or status register from the parallel port, the corresponding mask members of the PARAPORT_CYCLE
structure must be set: The following source code reads
the bit 2, 3 and 4 of data register ( to read the howl byte, put 0xFF into variable MaskData
) and the "PaperEnd" bit of the status register.
ParaPortCycle Cycle[ 1 ];
UCHAR MaskData = PARAPORT_MASK_DATA_D2
| PARAPORT_MASK_DATA_D3
| PARAPORT_MASK_DATA_D4;
Cycle[ 0 ].setMaskStatus( PARAPORT_MASK_STATUS_PAPEREND );
Cycle[ 0 ].setDataInput( );
Cycle[ 0 ].setMaskData( MaskData );
Dll->executeCycle( Handle, Cycle, 1 );
UCHAR Data = Cycle[ 0 ].getData( );
bool PaperEnd = Cycle[ 0 ].isStatusPaperEnd( );
A sample "read cycle" will be added in release v2.0 to show this.
Please have a look to the source code of the samples. The path of the library file is constant to simplify the source code and may be adapted to the customers target directory structure. In a "well programmed" application, the file name should be variable ( e.g.: configuration file, command line parameter ).
To use ParaPort in a MFC Application ( the ParaPortUtility application is one ), you should
loadLibrary( )
method in the InitInstance( )
method of the application's main class derived from CWinApp
,deleteSingleton( )
method in the ExitInstance( )
method of the same class,HANDLE m_Handle;
of an appropriated class ( e.g.: a class derived from CDocument
or CDialog
). openPort( )
and than can access to the parallel port with function executeCycle( )
.closePort( )
in the class destructor.A simple MFC sample application will be added in release v2.0.
The release v1.3 cannot be used by Visual Basic programs. In release v2.0, the DLL interface will be changed to support Visual Basic.
There is no extra support ( e.g.: classes ) of the .NET framework previewed. There should be some techniques integrated into C# to access a simple C dynamic link library as ParaPort.dll is.
FAQ about the driver "ParaPort.sys"
ParaPort does not support the interrupt ( bit 4 of Control Register ) to enable the IRQ via the Acknowledge line ( bit 6 of Status Register ), since it was not yet required. The only workaround is continuous polling on the parallel port.
FAQ about "ParaPortUtility"
none ( ? )