Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Parallel Port Library


  • Please log in to reply
8 replies to this topic
Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
A simple wrapper around inpout32.dll. Designed to work with binary strings (binary in ascii charset). Tested and works on Win7 x86:

#NoEnv

InitParallelPort()

Temp1 = 00000000
InputBox, Temp1, Parallel Port, Please enter a string of binary:,, W, H, X, Y,,, %Temp1%

WriteParallelPort(Temp1)
Temp1 := ReadParallelPort()

ToolTip, % Temp1
Sleep, 1000
ExitApp

;Port address is usually 0x378, but sometimes 0x278

InitParallelPort()
{
 global InProc
 global OutProc
 DllPath := A_ScriptDir . "\inpout32.dll"
 UPtr := A_PtrSize ? "UPtr" : "UInt", AStr := A_IsUnicode ? "AStr" : "Str"
 hModule := DllCall("LoadLibrary","Str",DllPath,UPtr)
 OutProc := DllCall("GetProcAddress",UPtr,hModule,AStr,"Out32",UPtr)
 InProc := DllCall("GetProcAddress",UPtr,hModule,AStr,"Inp32",UPtr)
 Return, 0
}

ReadParallelPort(Port = 0x378) ;Port address
{ ;Returns a string of binary representing the state of each pin, by position
 global InProc
 Data := DllCall(InProc,"UInt",Port)
 While, Data != 0
  Data1 := (Data & 1) . Data1, Data >>= 1
 Return, SubStr("00000000" . Data1,-7)
}

WriteParallelPort(Data = "",Port = 0x378) ;Data to write in binary form, Port address
{
 global OutProc
 Data := SubStr("00000000" . Data,-7), Temp1 := StrLen(Data), Data1 := 0, Index := 0
 Loop, %Temp1%
  Data1 += SubStr(Data,Temp1 - Index,1) * (1 << Index), Index ++
 Return, DllCall(OutProc,"Int",Port,"Int",Data1)
}

ReadParallelPort1(Port = 0x378)
{
 global InProc
 Return, DllCall(InProc,"UInt",Port)
}

WriteParallelPort1(Data = "",Port = 0x378)
{
 global OutProc
 Return, DllCall(OutProc,"Int",Port,"Int",Data)
}

Note: requires inpout32.dll in the same directory as the script.

Edit: Now unicode and 64-bit compatible.

Timo
  • Members
  • 26 posts
  • Last active: Mar 19 2013 12:34 PM
  • Joined: 24 Mar 2012
Please tell me why is it that I can not get this running (on AHK_L)? Particularly the InitParallelPort -function is interesting, I believe it would speed up thing quite a bit.

I do have running code around the inpout32.dll but it has no Init/Release mechanism so the best that I can get (on Optiplex 745) about 200 Hz frequency in switching all output pins High/Low.

With Best Regards,
Timo

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
Are you on a 64-bit computer?

InpOut32.dll does not support 64-bit architectures, and this script is not 64-bit ready. However, you can try an unofficial 64-bit port here.

Edit: Also try an ANSI build, for good measure.
Edit: I rewrote the function to support unicode and 64-bit. You will still need the 64-bit version of the DLL if applicable though.

Timo
  • Members
  • 26 posts
  • Last active: Mar 19 2013 12:34 PM
  • Joined: 24 Mar 2012
Thank you very much Uberi!

Originally the speed was only around 200 Hz, then with just the inpout32.dll loaded it went up to around 8000 Hz and after the procedure addresses pre-fetch it went up to around 15000 Hz (on an old Dell Optiplex 745 running XPpro). This is great! I believe it was the unicode issue that I had.

Below is the code that I've put together. Both ouput and input pins can be accessed individually (or all at once). See the Example Usage section for more details.

; The Pins of Parallel Port DB25 connector, When Looking at the Female Connector on a PC:
;
;  \--------------------------------------------------------------------/
;   \--13---12---11---10----9----8----7----6----5----4----3----2----1--/
;    \----------------------------------------------------------------/ 
;     \--25---24---23---22---21---20---19---18---17---16---15---14---/
;      \------------------------------------------------------------/
;
; Output pins: 2, 3, 4, 5, 6, 7, 8, 9
; Input pins: 10, 11, 12, 13, 15 
; Ground pins: 18, 19, 20, 21, 22, 23, 24, 25
; Control pins: 1, 14, 16, 17 
;
; Bidirectional mode is not enabled (output pins are output pins, not bi-directional).
; Control pins are not used/needed for this utility
;
; In the code:
; value 0 denotes voltage level Low 
; value 1 denotes voltage level High
;
;==============================================================================
;The parallel port is nearly always LPT1
OutputPortAddress = 0x378   ;LPT1 
InputPortAddress = 0x379    ;LPT1 
ControlPortAddress = 0x37A  ;LPT1 

;OutputPortAddress = 0x278  ;LPT2 
;InputPortAddress = 0x279   ;LPT2 
;ControlPortAddress = 0x27A ;LPT2 
;==============================================================================
;seach for the file inpout32.dll from three locations:
; sub-folder \tools under the folder where the script is
; the same folder where the script is
; \windows folder 
dllPath = %A_ScriptDir%\tools\inpout32.dll
IfNotExist, %dllPath%
	dllPath = %A_ScriptDir%\inpout32.dll
		IfNotExist, %dllPath%
			dllPath = %A_WinDir%\inpout32.dll
				IfNotExist, %dllPath%
					{
					msg := "First copy the inpout32.dll library file to one of the following folders:" "`n`n"
					msg := msg " - " A_ScriptDir "\tools\" "`n"
					msg := msg " - " A_ScriptDir "\" "`n"
					msg := msg " - " A_WinDir "\" "`n`n"
					msg := msg "Exit with no actions taken." "`n"
					msgbox % msg
					ExitApp
					}
;==============================================================================
; *** Thanks to Uberi @ AutoHotkey Forum! ***
UPtr := A_PtrSize ? "UPtr" : "UInt", AStr := A_IsUnicode ? "AStr" : "Str"
;load the module into memory
;hModule := DllCall("LoadLibrary", "Str", dllPath, "Ptr")  
hModule := DllCall("LoadLibrary","Str",DllPath,UPtr)
;and read the address of procedures
OutProc := DllCall("GetProcAddress",UPtr,hModule,AStr,"Out32",UPtr)
InpProc := DllCall("GetProcAddress",UPtr,hModule,AStr,"Inp32",UPtr)
;==============================================================================
;read in the initial state of the ouput port
OutputPortData := DllCall(InpProc,int,OutputPortAddress)
;==============================================================================


;START OF EXAMPLE USAGE

;set all eight output pins low
WriteOutputByte(0)
msgbox All output pins should be Low

;set all eight output pins high
WriteOutputByte(255)
msgbox All output pins should be High

;set the pin 3 to be low (the pins 2 to 9 can be adressed individually like this)
WritePin(3,0)
msgbox output pin3 should be Low

;set pin 3 to be high (the pins 2 to 9 can be adressed individually like this)
WritePin(3,1)
msgbox output pin3 should be High

;read in the state of all the five input pins
gosub ReadInputPins
;and then show their state
msg = 
(
These inputs are normally all high unless connected to ground.`n 
Pin 10 State = %Pin10State%
Pin 11 State = %Pin11State% 
Pin 12 State = %Pin12State% 
Pin 13 State = %Pin13State%
Pin 15 State = %Pin15State%
)
msgbox, % msg

;read in the state of an individual pin (here pin 11) and show it
PinToRead = 11
msg := ReadInputPin(PinToRead)
msgbox, % "Pin " PinToRead " state is: " msg

;test a High/low loop 3000 rounds and show the result
CycleCount = 3000
msg := TestOutputFrequency(CycleCount)
msgbox, % "Frequency as tested by " CycleCount " cycles is: " msg

;END OF EXAMPLE USAGE


goto ByeBye
;------------------------------------------------------------------------------
WriteOutputByte(OutputPortData)
{
	Global OutputPortAddress
	Global OutProc
	DllCall(OutProc,int,OutputPortAddress,int,OutputPortData)
}
;------------------------------------------------------------------------------
WritePin(PinNumber,PinValue)
{
	Global OutputPortData
	Global OutputPortAddress
	Global OutputPortData
	Global InpProc
	Global OutProc

	ormask := 2 ** (PinNumber - 2)
	andmask := 255 - ormask
	OutputPortData := DllCall(InpProc,int,OutputPortAddress)
		if(PinValue=0)
		{
			OutputPortData := OutputPortData & andmask
		}
		else
		{
			OutputPortData := OutputPortData | ormask
		}
	DllCall(OutProc,int,OutputPortAddress,int,OutputPortData)
}
;------------------------------------------------------------------------------
TestOutputFrequency(loopcount)
{
	Global OutputPortData
	Global OutputPortAddress
	Global OutProc

	thistime = %A_TickCount%
	loop, %loopcount%
	{
		OutputPortData = 0
		DllCall(OutProc,int,OutputPortAddress,int,OutputPortData)
		OutputPortData = 255
		DllCall(OutProc,int,OutputPortAddress,int,OutputPortData)
	}

	thistime := (A_TickCount - thistime) / 1000 ;secons
	frequency := loopcount / thistime /1000 ;in kHx
	return % frequency " kHz"
}
;------------------------------------------------------------------------------
ReadInputPins:
	InputData := DllCall(InpProc,int,InputPortAddress)
	Pin10State := 0 < (InputData & 64)
	Pin11State := 0 = (InputData & 128) ; no error, this one is inverted by hardware
	Pin12State := 0 < (InputData & 32)
	Pin13State := 0 < (InputData & 16)
	Pin15State := 0 < (InputData & 8)
return
;------------------------------------------------------------------------------
ReadInputPin(PinNumber)
{
	Global InputPortAddress
	Global InpProc
	InputData := DllCall(InpProc,int,InputPortAddress)
	
	if(PinNumber = 10)
	 Return 0 < (InputData & 64)
	  else if (PinNumber = 11)
	   Return 0 = (InputData & 128) ; no error, this one is inverted by hardware
        else if (PinNumber = 12)
         Return 0 < (InputData & 32)
          else if (PinNumber = 13)
           Return 0 < (InputData & 16)
            else if (PinNumber = 15)
		     Return 0 < (InputData & 8)
			  else
				{
				Msgbox, Only pins 10, 11, 12, 13, and 15 are input pins.
				Return -1
                }
}
;------------------------------------------------------------------------------
ByeBye:
	;unload the module
	DllCall("FreeLibrary", "Ptr", hModule)
exitApp
;------------------------------------------------------------------------------
With Best Regards,
Timo

Timo
  • Members
  • 26 posts
  • Last active: Mar 19 2013 12:34 PM
  • Joined: 24 Mar 2012
And now when I wrapped the speed test function like:

BatchLinesStore =A_BatchLines
SetBatchLines, -1
speed test here...
SetBatchLines = BatchLinesStore

I get frequency at about 38 kHz. Still nearly two fold improvement!

With Best Regards,
Timo

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010

And now when I wrapped the speed test function like:

BatchLinesStore =A_BatchLines
SetBatchLines, -1
speed test here...
SetBatchLines = BatchLinesStore

I get frequency at about 38 kHz. Still nearly two fold improvement!

With Best Regards,
Timo


Just a heads up, but that code neither saves nor restores the SetBatchLines option. I believe you mean something along these lines:

BatchLines := A_BatchLines
SetBatchLines, -1
;stuff
SetBatchLines, %BatchLines%


Timo
  • Members
  • 26 posts
  • Last active: Mar 19 2013 12:34 PM
  • Joined: 24 Mar 2012
Thank you again Uberi!

all the time I'm messing around with the %% ... and with the "" and with the %. To me all languages have something that does not easily penetrate into my head but I'm very surprised how easy the Autohotkey is to learn. And also how powerful it is.

With Best Regards,
Timo

heopas
  • Members
  • 44 posts
  • Last active: Oct 26 2012 09:56 PM
  • Joined: 14 Apr 2010
Thanks very much for this library!!!
I want to ask if it is possible to make a example with Control Ports.

Thanks in advance.

Uberi
  • Moderators
  • 1119 posts
  • Last active: May 02 2015 06:05 PM
  • Joined: 23 Aug 2010
Using the control port is simple - just add 2 to the base port. So 0x378 would become 0x37A. You can also access the status port by adding 1 to the base port.