FAQ - TTL triggers via parallel port (Win 2k/XP/Vista/7)


Note: there are several other solutions to send out digital pulses which work on OS X and Linux as well as Windows; these are listed at FaqTTLTriggerOSXLinux -- note that some use USB, which is slower than a parallel port.

Note: the solution discussed below works for win32 only and is register-based. This solution http://code.google.com/p/ratrix/source/browse/classes/util/parallelPort/pp.m?name=trunk works for win32, matlab32 on win64, or any matlab on linux (some minor work will be required to add support for matlab64 on win), and is a more natural pin-based API.

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 http://tech.groups.yahoo.com/group/psychtoolbox/message/4825).
  1. 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.sys driver enclosed in the zip archive to your windows\system32\drivers directory. Edit the porttalk.reg file replacing "Start"=dword:00000002 by "Start"=dword:00000000 (to load the driver at boot time not constantly requiring administrative privileges). As user with administrative privileges double-click the edited porttalk.reg to write the contents into the Windows registry and reboot your machine.
  2. Copy the attached C-code (also available here lptwrite.c with additional documentation in lptwrite.m), PortTalk_IOCTL.h and pt_ioctl.c (in IoExample subdirectory) from the porttalk zip archive into a directory on your machine. Change your MATLAB directory into this directory and execute "mex -v lptwrite.c" which should result in an additional file in your directory (either lptwrite.dll or lptwrite.mexw32 depending on your MATLAB version).
  3. 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)

  1. (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.

For 64 bit windows, use http://apps.usd.edu/coglab/psyc770/IO64.html. Note that http://code.google.com/p/ratrix/source/browse/classes/util/parallelPort/pp.m?name=trunk works for matlab32 on win64 (using http://apps.usd.edu/coglab/psyc770/IO32on64.html), but some minor work will be required to add support for matlab64 on win (already works on linux for either).

An alternative is http://tech.groups.yahoo.com/group/psychtoolbox/message/9328 (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)

Additional information

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Parport\Parameters]
"DisableWarmPoll"=dword:00000001
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:
hwinterface32B01.h
long __stdcall ReadMemShort(unsigned long addr);


Alternatives

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])

lptwrite.c


lptwrite.c
/*
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


lptread.c
/*
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();
}


lptread.m


lptread.m
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.
Valid XHTML :: Valid CSS: :: Powered by WikkaWiki