-
Notifications
You must be signed in to change notification settings - Fork 239
FAQ: TTL Triggers in Windows
Q: How to send TTL triggers via parallel port (Win 2k/XP/Vista/7)?
A: This is only one of several possible solutions used in several labs
(described first in the Psychtoolbox forum message http://tech.groups.yahoo.com/group/psychtoolbox/message/4825).
-
To access the parallel port under NT/2k/XP/Vista/7 a kernel ring 0 driver is required. We use PortTalk by Craig Peacock you can download from http://www.beyondlogic.org/porttalk/porttalk.htm. This works for 32 bit windows only (see below for 64 bit solution). Copy the
porttalk.sysdriver enclosed in the zip archive to yourwindows\system32\driversdirectory. Edit theporttalk.regfile replacing"Start"=dword:00000002by"Start"=dword:00000000(to load the driver at boot time not constantly requiring administrative privileges). As user with administrative privileges double-click the editedporttalk.regto write the contents into the Windows registry and reboot your machine. -
Copy the attached C-code (also available here lptwrite.c with additional documentation in lptwrite.m),
PortTalk_IOCTL.handpt_ioctl.c(inIoExamplesubdirectory) from the porttalk zip archive into a directory on your machine. Change your MATLAB directory into this directory and executemex -v lptwrite.cwhich should result in an additional file in your directory (eitherlptwrite.dllorlptwrite.mexw32depending on your MATLAB version). -
Find out the port address of the parallel port your cable is attached by right clicking on My Computer (for XP), select Manage then Device Manager, expand 'Ports (COM & LPT)', right click on the item that is named 'Printer Port (LPT1)' (or the like) and select properties. Select the Resources tab of the properties dialog and check the I/O Range, the first value of the 'Setting' column should be the actual port address. For LPT1 this is supposed to be 888 (hex378) on most standard setups, however, for add-on parallel port cards this is not necessarily the case. A STAR TECH 1 Port PCI Express add-on card can be called LPT1 but have a port address of 8192 (hex2000) for example. Now you can set the output bits 0-7 (pin 2-9) of your parallel port with
lptwrite(portAddress, byteValue)e.g.lptwrite(888, 255). To achieve a TTL trigger wait the time your EEG system requires (usually min 1/sampling rate) before resetting the port to zero, e.g.lptwrite(888, 255) WaitSecs(0.004) lptwrite(888, 0) -
(Vista/7 only) Under Vista, UAC prevents PortTalk from starting. Right-click MATLAB and choose "run as administrator" to allow PortTalk to start. You don't need to do this for additional MATLAB sessions until next time you reboot. You can also turn off UAC completely in the user account control panel.
Instead of PortTalk the Inpout drivers can be used for making register access possible. Inpout also works with 32bit Windows but is not compatible with lptwrite.
- If you want to write to the parallel port registers yourself, use 1. An alternative for accessing the Inpout functions is 2 (note calllib is not as fast as mex, so if performance is an issue, use the dll described there with the mex approach described here).
- Or you leave the register programming to the parPulse MEX module, see below under Alternatives.
-
Add
lptwrite(888, 0)to the beginning of your script in case your LPT port has random output bits set to high after boot as this is the case on all of our PCs (no idea why). -
You cannot prevent other processes from accessing the port. In particular, Windows periodically/unpredictably writes to the port to poll for devices, especially in the first few minutes after reboots and after you first start addressing the port. A registry key can (partially) disable this behavior (ref):
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Parport\Parameters] "DisableWarmPoll"=dword:00000001
-
A
lptreadcounterpart for reading bytes from parallel port was contributed by Erik:http://tech.groups.yahoo.com/group/psychtoolbox/message/4851
-
In case you are still using Psychtoolbox 2.54 take care to use the patched
WaitSecsversion available here:http://psychtoolbox.org/PTB-2/download/Win254Updates/WaitSecs254update1.zip
-
First check whether
pt_ioctl.chas a final newline if you experience errors while compilinglptwrite.chttp://tech.groups.yahoo.com/group/psychtoolbox/message/5130
-
To find your port address, right click 'my computer', 'properties', 'hardware', 'device manager', 'ports (com & lpt)', double click an lpt port, 'resources', 'i/o range'. The first value listed is the port's base hex address.
-
Matlab's hex2dec() is unreasonably slow and can lead to frame drops if you poll the parallel port between every flip – cache out the dec value. bin2dec() and dec2bin(.,8) are obviously useful and fast enough (tho about twice as slow as the fastest implementation). Easy mistakes: you can only write whole bytes, not individual bits, so if you want to leave certain bits unaffected during a write, read them first. Also, to keep consistent bit locations, you want to include leading zeros, so don't forget the second argument to dec2bin.
-
Matlab's data acquisition toolbox knows how to automatically read the port address from a protected area of windows memory that contains BIOS data:
get(digitalio('parallel'),'PortAddress')
Unfortunately, the data acquisition toolbox costs extra, is slower (~1ms) than lptwrite/lptread (~10us), and cannot address add-on ports (PCI/PCMCIA), which are a very inexpensive dio option (~US$15). Charitably, Mathworks support showed me the method they use. Use the WinIO driver to eg determine the first parallel port base address:
bresult = GetPhysLong((PBYTE)0x0408, &port);
An alternative is to use the DOS debug command to read the same area in memory (described here), but I can't figure out how to use an interactive DOS program from inside matlab (for instance using the "dos," "system", or "!" commands - any ideas?)
UPDATE
hwinterface32 knows how to access physical memory. The following code types out the addresses for LPT1-3. Unfortunately, like Matlab, it does not see PCI or PCMCIA add-on cards. Anyone know where to look up their addresses in physical memory? It seems that Windows must know, since it has assigned them LPT numbers.
path = 'C:\Documents and Settings\rlab\Desktop\Hwinterface32Beta01\Release\bin\'; %edit this for your location
lib='hwinterface32B01';
[notfound, warnings] = loadlibrary([path lib '.dll'],[path lib '.h']);
for i=0:2
dec2hex(calllib(lib,'ReadMemShort',hex2dec('408')+2*i))
end
unloadlibrary(lib)You'll need to make this header:
long __stdcall ReadMemShort(unsigned long addr);-
I find I have the best luck using the SPP mode setting in the BIOS, rather than ECP or EPP, especially when reading from the control register. However, note that the extended control register under ECP is supposed to be able to set to emulate SPP (I couldn't get this to work). Note that to enable bi-directional use (reading from, in addition to writing to, the data and control registers), you must both set bit 5 (counting from zero) of the control register and set the pin you want to read from must be set to the high +5V TTL setting, counting inversions (ref).
-
The electrical characteristics of ports, and even pins/registers within a port, vary (ref1 ref2). I have the best luck with totem-pole outputs (you generally need a buffer, not just a raw transistor). The raw output of totem-pole Optologic devices can be used directly.
-
Be sure to use a straight-thru cable, not a null-modem cable (which often have both db-9 and db-25 connectors, and contain crossed over lines).
-
Breakout cards with terminal blocks are very useful.
-
A good summary at wikipedia
-
You can use a PCI or PCMCIA add-on parallel port. I have run into cards that were not standards compliant and did not work with
lptread/lptwrite, but unfortunately didn't keep track of which cards failed. I have tested the following and verified that they do work:- PCI or
- PCI-X: axxon IC0534KB
- PCMCIA: quatech spp-100
- PCI adaptor for PCMCIA cards (for using laptop PCMCIA cards in a desktop): SD-PCI-PCM-G (Ricoh R5C485 chipset)
- A similar approach was described here: http://tech.groups.yahoo.com/group/psychtoolbox/message/8032
- MATLAB Data Acquisition Toolbox (see 'parallel port characteristics' under 'line and port characteristics')
- Cris Niell of Stryker lab found that matlab's public digital IO methods
putvalue/getvaluecan be accelerated from ~1ms to ~20us by caching out the value of daqgetfield(dio,'uddobject'):
```matlab
dio = digitalio('parallel')
addline(dio,7,0,'out') %pin 9
putvalue(dio,1) %~700us
putvalue(dio.Line,1) %~150us
uddobj = daqgetfield(dio,'uddobject')
putvalue(uddobj,1,1); %~20us (undocumented use demo in @dioline\putvalue.m and @digitalio\putvalue.m - args are: uddobj, vals [, lineInds])
getvalue(uddobj,1); %~20us (undocumented use demo in @dioline\getvalue.m and @digitalio\getvalue.m - args are: uddobj [, lineInds])
```
- loadlibrary (not as fast as mex) http://tech.groups.yahoo.com/group/psychtoolbox/message/9328
- parPulse: The parPulse MEX file uses the Inpout driver functions for accessing parallel port registers and offers a few goodies besides just setting port bits (port bit masking, writing to 4 control output lines, reading from 2 control input lines, non-blocking pulse output). There are MEX files for 32bit and 64bit Windows. (Installation hints and download).
/*
lptwrite.c
Compile in MATLAB with mex lptwrite.c [-O] [-g] [-v]
For description see lptwrite.m
Copyright (C) 2006 Andreas Widmann, University of Leipzig, widmann@uni-leipzig.de
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "stdio.h"
#include "windows.h"
#include "pt_ioctl.c"
#include "mex.h"
void __cdecl mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *port, *value;
int mrows, ncols, arg;
/* Check for proper number of arguments. */
if (nrhs != 2) {
mexErrMsgTxt("Two input arguments required.");
} else if (nlhs > 0) {
mexErrMsgTxt("Too many output arguments.");
}
/* The input must be noncomplex scalar double.*/
for (arg = 0; arg < 2; arg++) {
mrows = mxGetM(prhs[arg]);
ncols = mxGetN(prhs[arg]);
if (!mxIsDouble(prhs[arg]) || mxIsComplex(prhs[arg]) || !(mrows == 1 && ncols == 1)) {
mexErrMsgTxt("Input must be noncomplex scalar double.");
}
}
/* Assign pointers to each input and output. */
port = mxGetData(prhs[0]);
value = mxGetData(prhs[1]);
OpenPortTalk();
outportb(*port, *value);
ClosePortTalk();
}/*
lptread.c
Compile in MATLAB with mex lptread.c [-O] [-g] [-v]
(lcc is picky that this file ends in a blank line)
For description see lptread.m
Copyright (C) 2006 Erik Flister, UCSD, e_flister@REMOVEME.yahoo.com
Adapted from Andreas Widmann.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "stdio.h"
#include "windows.h"
#include "mex.h"
#include "pt_ioctl.c"
void __cdecl mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray*prhs[])
{
double *port;
int mrows, ncols;
double *val;
/* Check for proper number of arguments. */
if (nrhs != 1) {
mexErrMsgTxt("One input argument required.");
} else if (nlhs != 1) {
mexErrMsgTxt("One output argument required.");
}
/* The input must be noncomplex scalar double.*/
mrows = mxGetM(prhs[0]);
ncols = mxGetN(prhs[0]);
if (!mxIsDouble(prhs[0]) || mxIsComplex(prhs[0]) || !(mrows == 1 && ncols ==1)) {
mexErrMsgTxt("Input must be noncomplex scalar double.");
}
/* Assign pointers. */
port = mxGetData(prhs[0]);
plhs[0] = mxCreateScalarDouble(0);
val = mxGetPr(plhs[0]);
/* Call PortTalk. */
OpenPortTalk();
*val = inportb(*port);
ClosePortTalk();
}function value=lptread(port)
% LPTREAD read from port
%
% Description:
% IOCTL call to porttalk.sys kernel mode driver (required) by Craig Peacock
%
% Installation:
% See http://psychtoolbox.org/wikka.php?wakka=FaqTTLTrigger
% http://www.logix4u.net/parallelport1.htm is a good parallel port reference
%
% Usage:
% value = lptread(port)
%
% Arguments:
% port - double Port address (e.g., 889 = 0x1 + 0x378 for status register of LPT1
% on many machines, which corresponds to pins 10, 11, 12, 13, and 15 of a DB25
% parallel port – note pin 11 is hardware inverted!)
%
% Examples:
% val = lptread(1+hex2dec(0x378));
% dec2bin(val,8) %the second argument uses leading zeros to keep bits in consistent locations
%
% Author: Erik Flister, UCSD, 2006 (C). Adapted from Andreas Widmann.This wiki is complementary to the main website at http://psychtoolbox.org
Please feel encouraged to edit these pages and add helpful content. Take care.