Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
VisualStimulation/pp.m
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
323 lines (289 sloc)
10.4 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function input = pp(pins,output,slowChecks,port,addr) | |
% [input] = pp(pins,[output],[slowChecks],[port],[address]) | |
% | |
% read/write TTL levels on parallel port pins under linux, win32 (using | |
% porttalk), or matlab32 on win64 (using io32) | |
% | |
% Arguments: | |
% pins integer vector of hardware pin nums | |
% output logical vector same size as pins indicating TTL levels to | |
% write -- omit or use [] to merely read | |
% slowChecks optional logical scalar (default true) | |
% indicates whether to run extensive and slow input | |
% validation, checking all relevant OS information | |
% regarding the ports (that i could find) | |
% if set to false when writing to incorrect addr, you could damage your OS | |
% port optional integer vector indicating parallel port indices | |
% defaults to first port found in /proc/sys/dev/parport/ | |
% (set to [] for windows) | |
% address optional integer vector indicating hardware memory address | |
% corresponding to ports. | |
% eg, the first value in /proc/sys/dev/parport/parport0/base-addr | |
% (must be scalar on windows -- hex2dec the first I/O Range address on the | |
% Resources tab of the LPT entry in the device manager) | |
% | |
% Returns: logical matrix of values read (rows correspond to pins, columns | |
% to ports) -- use no return value to merely write | |
% | |
% if multiple ports/addrs are supplied, the same output/pins are used for each. | |
% | |
% if you want pp to be as fast as possible, supply both the port | |
% and addr, and set slowChecks to false. | |
% | |
% the parallel port's pins are divided into three registers, and some pins | |
% are logically inverted. pp takes care of these details for you. | |
% | |
% you cannot write to the status register (pins 10, 11-13, 15). | |
% values read from the data register (pins 2-9) are likely the values | |
% you wrote, not input from external equipment. use the status or control | |
% register (pins 1, 14, 16-17) to read input. | |
% | |
% pp works for built-in as well as add-on (PCI/PCMCIA) parallel ports | |
% if their drivers are installed. on fedora 15, i found both to be | |
% installed by default. | |
% | |
% pp requires the corresponding compiled mex file (ppMex, lptread/lptwrite, io32). | |
% you may need to recompile it for your arch -- see instructions in ppMex.c, etc. | |
% for fastest operation, you could call ppMex directly, but it does no input | |
% validation, and depends on formatting provided by getPinInfo as performed | |
% in this file. | |
% | |
% ppMex is currently configured to use iopl/inb/outb, which require root | |
% (ie, you need to start matlab with sudo). we may someday switch to ppdev, | |
% which is not supposed to require root, but the current prototype ppdev code | |
% seems to need it to read /dev/parport* anyway. | |
% | |
% Examples: | |
% vals = pp(uint8([1 2 10 3])) % read some pins (slow) | |
% vals = pp(uint8([1 2 10 3]),[],false,uint8(0),uint64(888)) % read some pins (fast) | |
% | |
% pp(uint8([16 4 8 1]),[true false true true]) % write to some pins (slow) | |
% pp(uint8([16 4 8 1]),[true false true true],true, uint8(0),uint64(888)) % write to some pins (slow -- validates port address for safety) | |
% pp(uint8([16 4 8 1]),[true false t``rue true],false,uint8(0),uint64(888)) % write to some pins (fast -- dangerous if addr is incorrect) | |
% | |
% vals = pp(uint8([16 4 8 1]),[true false true true]) % write some pins, then immediately read from them, hopefully verifying what you wrote | |
% | |
% Copyright (C) 2011 Erik Flister, University of Oregon, erik.flister <at> gmail | |
if ~exist('slowChecks','var') || isempty(slowChecks) | |
slowChecks=true; | |
end | |
if ~exist('output','var') | |
output = logical([]); | |
end | |
if isunix && ~ismac %IsLinux probably more appropriate, but requires psychtoolbox | |
useSscanf=true; %maybe textscan is faster? | |
pportDir='/proc/sys/dev/parport/'; | |
if slowChecks | |
[s dmesg]=unix('dmesg | grep parport'); | |
if s~=0 | |
error('couldn''t dmesg') | |
end | |
[s dev]=unix('ls -al /dev | grep parport'); | |
if s~=0 | |
error('couldn''t dev') | |
end | |
[s drv]=unix('grep ppdev /proc/devices'); | |
if s~=0 | |
error('couldn''t proc') | |
end | |
[s lsm]=unix('lsmod | grep ppdev'); | |
if s~=0 | |
error('couldn''t lsmod ppdev') | |
end | |
[s lsmp]=unix('lsmod | grep parport'); | |
if s~=0 | |
error('couldn''t lsmod parport') | |
end | |
[s iop]=unix('cat /proc/ioports | grep parport'); | |
if s~=0 | |
error('couldn''t ioports') | |
end | |
end | |
if slowChecks || ~exist('port','var') || isempty(port) | |
ports=uint8([]); | |
d=dir(pportDir); | |
for i=1:length(d) | |
if d(i).isdir | |
if useSscanf | |
[A, count, errmsg] = sscanf(d(i).name, 'parport%u'); | |
if count==1 && isempty(errmsg) && ~isempty(A) | |
ports(end+1)=uint8(A); | |
end | |
else | |
C = textscan(d(i).name, 'parport%u8'); | |
if ~isempty(C{1}) | |
ports(end+1)=C{1}; | |
end | |
end | |
end | |
end | |
if ~exist('port','var') || isempty(port) | |
port=ports(1); | |
end | |
if ~isvector(port) || ~isinteger(port) || ~all(port>=0) || ~all(ismember(port,ports)) || length(unique(port))<length(port) | |
error('port must be integer vector of valid port IDs with no duplicates') | |
end | |
ports=intersect(port,ports); | |
if length(ports)<length(port) | |
error('couldn''t find all ports') | |
end | |
else | |
ports=port; | |
end | |
if ~exist('addr','var') || isempty(addr) | |
addr=zeros(size(port),'uint64'); | |
end | |
if slowChecks | |
if ~isvector(addr) || ~isinteger(addr) || ~all(addr>=0) || ~all(size(addr)==size(port)) | |
error('addr must be integer vector >=0 same size as port') | |
end | |
end | |
for i=1:length(ports) | |
name = ['parport' num2str(ports(i))]; | |
base = ['cat ' pportDir name filesep]; | |
if slowChecks | |
[s modes]=unix([base 'modes']); | |
if s~=0 | |
error('couldn''t cat modes') | |
end | |
if isempty(strfind(modes,'SPP')) | |
error('SPP not supported') | |
end | |
if isempty(strfind(dev,name)) || isempty(strfind(dmesg,name)) | |
error('pport in pportDir but not dev or dmesg') | |
end | |
end | |
if addr(i)==0 || slowChecks | |
[s a]=unix([base 'base-addr']); | |
if s~=0 | |
error('couldn''t cat base-addr') | |
end | |
if useSscanf | |
[a, count, errmsg] = sscanf(a, '%u %*u'); | |
% the second number is always 0 for me (ubuntu 11/fedora 15), | |
% but kkd reported a 1912 on parport0 for his machine (ubuntu 12.04). | |
% according to http://www.kernel.org/doc/Documentation/parport.txt | |
% this is just another address for the port? what's it for? | |
if count==1 && isempty(errmsg) && ~isempty(a) | |
a=uint64(a); | |
else | |
a | |
count | |
errmsg | |
[s a]=unix([base 'base-addr']) | |
error('bad addr') | |
end | |
else | |
a = textscan(a, '%u64 %*u64'); | |
if ~isempty(a{1}) | |
a=a{1}; | |
else | |
error('bad addr') | |
end | |
end | |
if addr(i)==0 | |
addr(i)=a; | |
else | |
if addr(i)~=a | |
error('port and address don''t match') | |
end | |
end | |
end | |
end | |
else | |
error('only linux and windows supported') | |
end | |
if isempty(pins) | |
input = []; | |
return | |
end | |
if slowChecks | |
if ~isvector(pins) || ~isinteger(pins) || ~all(pins>0) || ~all(pins<=17) || length(unique(pins))<length(pins) | |
error('pins must be integer vector 1<=pins<=17 with no duplicates') | |
end | |
if ~(isempty(output) || (islogical(output) && all(size(output)==size(pins)))) | |
error('output must be empty or logical vector same size as pins') | |
end | |
end | |
bitSpecs=getPinInfo(pins); | |
inds = logical(bitSpecs(:,3)); | |
function x = inv (x) | |
x(inds) = ~x(inds); | |
end | |
if ~isempty(output) | |
output = inv(output); | |
end | |
if ispc | |
if nargout>0 | |
input = nan(length(pins),1); | |
end | |
[offsets, ~, vInds] = unique(bitSpecs(:,2)); | |
for i = 1:length(offsets) | |
a = addr + double(offsets(i)); | |
these = vInds == i; | |
cInds = 8 - bitSpecs(these,1); | |
if isnan(ioObj) | |
codeStr = lptread(a); | |
else | |
codeStr = double(io32(ioObj,a)); | |
end | |
codeStr = fastDec2Bin(codeStr); | |
if isempty(output) | |
if slowChecks && ~all(isnan(input(these))) | |
error('whoops') | |
end | |
input(these) = codeStr(cInds)=='1'; | |
elseif nargout == 0 | |
if offsets(i)==1 | |
error('cannot write to status register') | |
end | |
codeStr(cInds) = char('0'*ones(1,sum(these)) + output(these)*('1' - '0')); | |
codeStr = fastBin2Dec(codeStr); | |
if isnan(ioObj) | |
lptwrite(a,codeStr); | |
else | |
io32(ioObj,a,codeStr); | |
end | |
else | |
error('doesn''t yet support write validation') | |
end | |
end | |
if nargout>0 | |
if slowChecks && any(isnan(input)) | |
error('whoops') | |
end | |
input = inv(input); | |
end | |
else | |
% w=warning('off', 'MATLAB:concatenation:integerInteraction'); | |
input = inv(ppMex([uint64(ports(:)) uint64(addr(:))],[bitSpecs(:,1:2) uint8(output(:))])); | |
% warning(w.state, 'MATLAB:concatenation:integerInteraction'); | |
end | |
end | |
function out=getPinInfo(pinNum) | |
pportSpec=uint8([... %[bitNum,regOffset,inv] | |
0 2 1; %1 | |
0 0 0; %2 | |
1 0 0; %3 | |
2 0 0; %4 | |
3 0 0; %5 | |
4 0 0; %6 | |
5 0 0; %7 | |
6 0 0; %8 | |
7 0 0; %9 | |
6 1 0; %10 | |
7 1 1; %11 | |
5 1 0; %12 | |
4 1 0; %13 | |
1 2 1; %14 | |
3 1 0; %15 | |
2 2 0; %16 | |
3 2 1; %17 | |
]); | |
try | |
out=pportSpec(pinNum,:); | |
catch e | |
e | |
error('pin num must be integer 1-17') | |
end | |
end |