% lab = vellscope.pclab
% Creates a pclab object lab for interfacing with the PCSGU250. This will
% automatically bring up the PCSGU250 GUI without starting the scope or
% function generator. The settings for both are controlled by lab's
% properties. Control either using lab's methods (see README.pdf for more
% details).
% MATLAB Support Package for Velleman PCSGU250
% Version 1.0
% Copyright 2011 The MathWorks, Inc.
classdef pclab < handle
properties(Dependent)
% VoltsPerDiv_Ch1
% Volts per division for channel 1 of the scope. Value is a string.
% Acceptable values are '3V', '1V', '300mV', '100mV', '30mV', '10mV'.
VoltsPerDiv_Ch1
% VoltsPerDiv_Ch2
% Volts per division for channel 2 of the scope. Value is a string.
% Acceptable values are '3V', '1V', '300mV', '100mV', '30mV', '10mV'.
VoltsPerDiv_Ch2
% TimePerDiv
% Time per division for the scope. Value is a string.
% Acceptable values are '500ms', '200ms', '100ms', '50ms', '20ms', '10ms',
% '5ms', '2ms', '1ms', '500us', '200us', '100us', '50us', '20us', '10us',
% '5us', and '2us'.
TimePerDiv
% Trigger
% Whether the trigger is on or off. Acceptable values are 'on' and 'off'
Trigger
% TriggerEdge
% Which edge of a waveform the trigger will capture. Positive means that
% the waveform must be moving up next the level, negative means it must be
% moving down. Acceptable values are 'positive' and 'negative'.
TriggerEdge
% TriggerSource
% The trigger source channel. Acceptable values are 'ch1' and 'ch2'
TriggerSource
% TriggerLevel
% The trigger level relative to the volts per division. 0 sets the level to
% 4 divisions above the center line (ie. at the top of the scope), 127 sets
% it to the center line, and 255 sets it to 4 division below the center
% line (ie. at the bottom of the scope). Acceptable values are integers
% between 0 and 255.
TriggerLevel
% YPosition_Ch1
% The y position offset for channel 1 relative to the volts per division.
% -128 sets the center line at -8*voltsperdiv volts (ie. shifts the
% waveform one scope screen up), 0 sets the center line at 0V, and 127 sets
% the center line at 8*voltsperdiv volts (ie. shifts the waveform one scope
% screen down. Acceptable values are integers between -128 and 127.
YPosition_Ch1
% YPosition_Ch2
% The y position offset for channel 2 relative to the volts per division.
% -128 sets the center line at -8*voltsperdiv volts (ie. shifts the
% waveform one scope screen up), 0 sets the center line at 0V, and 127 sets
% the center line at 8*voltsperdiv volts (ie. shifts the waveform one scope
% screen down. Acceptable values are integers between -128 and 127.
YPosition_Ch2
% Coupling_Ch1
% The coupling type for channel 1. Acceptable values are 'AC', 'DC', and
% 'GND'.
Coupling_Ch1
% Coupling_Ch2
% The coupling type for channel 1. Acceptable values are 'AC', 'DC', and
% 'GND'.
Coupling_Ch2
end
properties(Dependent,SetAccess = private)
% DataReady
% Indicates if the scope has new data to access. Is not setable. 0
% indicates there is no data, 1 indicates there is data.
DataReady
end
properties
% FgenSource
% The type of function to use. Acceptable values are 'standard', 'library',
% and 'sweep'. Standard functions are sine, triangle, and square functions.
% Library functions are pregenerated arbitrary waveforms. Sweep functions
% are frequency sweeps across a range of interest.
FgenSource = 'standard'
% FgenFunction
% Which standard function to use. Only affects the function generator when
% FgenSource = 'standard'. Acceptable values are 'sine', 'triangle', and
% 'square'.
FgenFunction = 'sine'
% FgenFrequency
% The frequency to use for standard and library functions. Acceptable
% values are real numbers between .005 (5mHz) and 10^6 (1MHz).
FgenFrequency = 300
% FgenAmplitude
% The voltage amplitude to use for all functions. The voltage amplitude is
% the peak-to-peak voltage difference between the maximum and minimum peaks.
% Accptable values are numbers.
FgenAmplitude = 1
% FgenOffset
% The voltage offset to use for all functions. The amplitude-adjusted
% waveform has this number (in volts) added to it before being output.
% Acceptable values are numbers.
FgenOffset = 0
% FgenLibFile
% The library file to use for library mode. See README.pdf for more
% information on library files. Value is a string. Values must be filenames
% without any path information or .lib extension ('sawtooth' instead of
% 'C:\sawtooth.lib').
FgenLibFile = []
% SweepStartFreq
% The starting freqency for sweep mode in Hz. Acceptable values are real
% numbers between .005 (5mHz) and 10^6 (1MHz).
SweepStartFreq = 100
% SweepStopFreq
% The stop freqency for sweep mode in Hz. Acceptable values are real
% numbers between .005 (5mHz) and 10^6 (1MHz).
SweepStopFreq = 1000
% SweepTime
% How long the sweep takes to execute. Only affects sweep mode. The sweep
% will continuously repeat with SweepTime as a period. Acceptable values
% are real numbers in seconds.
SweepTime = 10
end
properties(Access = private)
SDK_Location%The location of the PCSGU250 SDK
LibFiles_Location%The location of the custom library files
end
properties(Constant,Hidden)
TimePerDivValues = {'500ms', '200ms', '100ms', ...
'50ms', '20ms', '10ms', ...
'5ms', '2ms', '1ms', ...
'500us', '200us', '100us', ...
'50us', '20us', '10us', ...
'5us', '2us'};
TimePerDivInSeconds = [0.5 0.2 0.1 0.05 0.02 0.01 ...
0.005 0.002 0.001 0.0005 0.0002 0.0001 ....
0.00005 0.00002 0.00001 0.000005 0.000002];
VoltsPerDivValues = {'3V', '1V', ...
'300mV' '100mV', ...
'30mV', '10mV'};
TriggerValues = {'off', 'on'};
TriggerSourceValues = {'ch1','ch2'};
TriggerEdgeValues = {'negative', 'positive'};
CouplingValues = {'AC','DC','GND'}
FgenSourceValues = {'standard', 'library', 'sweep'}
FgenFunctionValues = {'sine', 'square', 'triangle'};
end
% Other methods
methods
function obj = pclab()
obj.SDK_Location = vellscope.pclab.addSDKToWindowsPath();
if ~libisloaded('pclab')
loadlibrary(fullfile(obj.SDK_Location, 'PCSGU250'), @vellscope.pcsgu250_hdr, 'alias', 'pclab');
end
% For some reason, Start_PCSGU250 runs much faster if we change
% to the SDK directory before loading the DLL
oldDir = pwd;
cd(obj.SDK_Location);
calllib('pclab', 'Start_PCSGU250');
cd(oldDir);
obj.LibFiles_Location = fullfile(obj.SDK_Location, 'lib');
pause(.25); %pause to avoid SDK hangup
end
function delete(obj) %#ok<*MANU>
calllib('pclab', 'Stop_PCSGU250');
if libisloaded('pclab')
unloadlibrary('pclab');
end
end
function show(obj)
% obj.show
% Reveals the PCSGU250 GUI.
calllib('pclab', 'Show_PCSGU250', 1);
end
function hide(obj)
% obj.hide
% Hides the PCSGU250 GUI.
calllib('pclab', 'Show_PCSGU250', 0);
end
function display(obj)
disp(obj);
fprintf(' Help Links:\n');
fprintf(' ><a href="matlab:help vellscope.pclab">Class Help</a>\n');
fprintf(' ><a href="matlab:vellscope.pclab.showMethods">Method List</a>\n');
fprintf(' ><a href="matlab:vellscope.pclab.showProperties">Property List</a>\n\n');
end
end
% Scope methods
methods
function startScope(obj,single)
% obj.startScope()
% Starts the oscilloscope in "Run" mode. Run mode will continuously
% update as long as there is data.
%
% obj.startScope('Single')
% Starts the oscilloscope in "Single" mode. Single mode will only collect
% one screen of data and stop. This generally should be used in
% conjunction with the trigger.
if exist('single','var') && strcmp(single,'Single')
calllib('pclab', 'RunOn', 0)
calllib('pclab', 'SingleOn', 1);
else
calllib('pclab', 'SingleOn', 0);
calllib('pclab', 'RunOn', 1);
end
end
function stopScope(obj)
% obj.stopScope
% Stops the scope. A scope in Run mode will stop updating. An untriggered
% scope in Single mode will stop waiting for input.
calllib('pclab', 'RunOn', 0);
calllib('pclab', 'SingleOn', 0);
end
function out = readChannel(obj, channelNum)
% out = obj.readChannel(channelnum)
% Reads data from a scope channel (channelnum, either 1 or 2). Returns a
% 32-bit array (ie. 4096 entries long) containing the voltage in volts.
% This channel includes all data from the PCSGU250's buffer, including
% data that could not be accessed using the GUI without scrolling. The
% trigger point (the point in the waveform that triggered the scope) is
% located at roughly entry 1000, and the "default" viewing screen is from
% about 1000 to about 2625.
%
% Each data point represents one voltage sample from the PCSGU250. The
% sampling period is based on the value of TimePerDiv and is equal to
% TimePerDiv/125, with TimePerDiv in seconds. This means that the total
% amount of data collected will be roughly 40 division's worth.
validateattributes(channelNum, {'numeric'}, {'scalar', 'integer'}, 'readChannel', 'channelNum');
if channelNum~=1 && channelNum~=2
error('Channel number should be 1 or 2');
end
x = zeros(1,5000,'int32');
xp = libpointer('int32Ptr',x);
if channelNum == 1
calllib('pclab', 'ReadCh1', xp);
else
calllib('pclab', 'ReadCh2', xp);
end
% xp.Value is an int32 array
maxVoltageVolts = double(xp.Value(2))/1000;
rawGroundLevel = double(xp.Value(3));
rawData = double(xp.Value(4:4099));
maxRawValue = 255;
out = maxVoltageVolts * (rawData - rawGroundLevel)/maxRawValue;
end
function set.TimePerDiv(obj, val)
obj.setScopeEnumProperty(val, 'Time', obj.TimePerDivValues);
end
function val = get.TimePerDiv(obj)
val = obj.getScopeEnumProperty('Time', obj.TimePerDivValues);
end
function set.VoltsPerDiv_Ch1(obj,val)
obj.setScopeEnumProperty(val, 'Voltage1', obj.VoltsPerDivValues);
end
function val = get.VoltsPerDiv_Ch1(obj)
val = obj.getScopeEnumProperty('Voltage1', obj.VoltsPerDivValues);
end
function set.VoltsPerDiv_Ch2(obj,val)
obj.setScopeEnumProperty(val, 'Voltage2', obj.VoltsPerDivValues);
end
function val = get.VoltsPerDiv_Ch2(obj)
val = obj.getScopeEnumProperty('Voltage2', obj.VoltsPerDivValues);
end
function set.Trigger(obj,val)
obj.setScopeEnumProperty(val, 'TrgOn', obj.TriggerValues);
end
function val = get.Trigger(obj)
val = obj.getScopeEnumProperty('TrgOn', obj.TriggerValues);
end
function set.TriggerSource(obj,val)
obj.setScopeEnumProperty(val, 'TrgSource', obj.TriggerSourceValues);
end
function val = get.TriggerSource(obj)
val = obj.getScopeEnumProperty('TrgSource', obj.TriggerSourceValues);
end
function set.TriggerEdge(obj,val)
obj.setScopeEnumProperty(val, 'TrgEdge', obj.TriggerEdgeValues);
end
function val = get.TriggerEdge(obj)
val = obj.getScopeEnumProperty('TrgEdge', obj.TriggerEdgeValues);
end
% The triggering level value between 0 and 255
function set.TriggerLevel(obj,val)
validateattributes(val, {'numeric'}, {'scalar', 'integer', '>=', 0, '<=', 255}, 'set TriggerLevel', 'trigger value');
calllib('pclab', 'TrgLevel', val);
end
function val = get.TriggerLevel(obj)
s = getSettings(obj);
val = s.TrgLevel;
end
function set.YPosition_Ch1(obj,val)
validateattributes(val, {'numeric'}, {'scalar', 'integer', '>=', -128, '<=', 127}, 'set YPosition_Ch1', 'y position value');
calllib('pclab', 'YPosition1', val);
end
function val = get.YPosition_Ch1(obj)
s = getSettings(obj);
val = s.YPosition1;
end
function set.YPosition_Ch2(obj,val)
validateattributes(val, {'numeric'}, {'scalar', 'integer', '>=', -128, '<=', 127}, 'set YPosition_Ch2', 'y position value');
calllib('pclab', 'YPosition2', val);
end
function val = get.YPosition_Ch2(obj)
s = getSettings(obj);
val = s.YPosition2;
end
function set.Coupling_Ch1(obj,val)
obj.setScopeEnumProperty(val, 'Coupling1', obj.CouplingValues);
end
function val = get.Coupling_Ch1(obj)
val = obj.getScopeEnumProperty('Coupling1', obj.CouplingValues);
end
function set.Coupling_Ch2(obj,val)
obj.setScopeEnumProperty(val, 'Coupling2', obj.CouplingValues);
end
function val = get.Coupling_Ch2(obj)
val = obj.getScopeEnumProperty('Coupling2', obj.CouplingValues);
end
function val = get.DataReady(obj)
val = calllib('pclab','DataReady');
end
end
% Function Generator methods
methods
function set.FgenFunction(obj, func)
validatestring(func, obj.FgenFunctionValues, 'set FgenFunction', 'function');
obj.FgenFunction = func;
end
function set.FgenFrequency(obj, freq)
validateattributes(freq, {'numeric'}, {'scalar', 'real', '>=', .005, '<=', 1000000}, 'set FgenFrequency', 'frequency');
obj.FgenFrequency = freq;
end
function set.FgenAmplitude(obj, ampl)
validateattributes(ampl, {'numeric'}, {'scalar', 'real'}, 'set FgenAmplitude', 'amplitude');
obj.FgenAmplitude = ampl;
end
function set.FgenOffset(obj, offset)
validateattributes(offset, {'numeric'}, {'scalar', 'real'}, 'set FgenOffset', 'offset');
obj.FgenOffset = offset;
end
function set.FgenLibFile(obj, filename)
if isempty(filename)
obj.FgenLibFile = [];
return;
end
if ~ischar(filename)
error('Expecting the name of a .lib file, e.g., ''damp_wav''');
end
[pathstr,nameroot,ext] = fileparts(filename);
if ~isempty(pathstr)
error('filename should not include any path information (it should just be ''%s'')', nameroot);
end
if ~isempty(ext) && ~strcmpi(ext, '.lib')
warning('vellscope:LibExtWarning','Ignoring the ''%s'' extension (files are expected to be .lib)', ext);
end
fullfname = fullfile(obj.LibFiles_Location, [nameroot '.lib']);
if ~exist(fullfname, 'file')
error('''%s'' does not exist\n', filename);
end
obj.FgenLibFile = [nameroot '.lib'];
end
function set.SweepStartFreq(obj, freq)
validateattributes(freq, {'numeric'}, {'scalar', 'real', '>=', .005, '<=', obj.SweepStopFreq}, 'set StartSweepFreq', 'frequency'); %#ok<*MCSUP>
obj.SweepStartFreq = freq;
end
function set.SweepStopFreq(obj, freq)
validateattributes(freq, {'numeric'}, {'scalar', 'real', '>=', obj.SweepStartFreq, '<=', 1000000}, 'set StopSweepFreq', 'frequency');
obj.SweepStopFreq = freq;
end
function set.FgenSource(obj, source)
idx = find(strncmpi(source, obj.FgenSourceValues, length(source)));
if numel(idx)==1
obj.FgenSource = source;
else
valString = sprintf(' ''%s''\n', obj.FgenSourceValues{:});
error('vellscope:BadEnum','Valid values are:\n%s\n', valString);
end
end
function startGen(obj)
% obj.startGen
% Starts the function generator according to the class properties. Will
% choose an output mode based on FgenSource and use the appropriate Fgen
% or Sweep properties. If used when the function generator is already
% running, will apply any changed setting to the existing output.
switch obj.FgenSource
case 'standard'
idx = find(strncmpi(obj.FgenFunction, obj.FgenFunctionValues, length(obj.FgenFunction)));
calllib('pclab', 'SetGen', idx, obj.FgenFrequency, obj.FgenAmplitude, obj.FgenOffset);
case 'library'
if isempty(obj.FgenLibFile)
error('Library file not set')
else
calllib('pclab', 'SetLibWave', obj.FgenFrequency, obj.FgenAmplitude, obj.FgenOffset, obj.FgenLibFile);
end
case 'sweep'
calllib('pclab','SetSweep',obj.SweepStartFreq,obj.SweepStopFreq,obj.FgenAmplitude,obj.FgenOffset,obj.SweepTime);
end
calllib('pclab', 'StartGen');
end
function stopGen(obj)
% obj.stopGen
% Stops all function generator output.
calllib('pclab', 'StopGen');
end
end
methods (Access=private)
function val = getScopeEnumProperty(obj, propertyField, propertyEnum)
s = getSettings(obj);
fld = s.(propertyField);
val = propertyEnum{fld+1};
end
function setScopeEnumProperty(obj, val, dllFcn, propertyEnum)
idx = find(strncmpi(val, propertyEnum, length(val)));
if numel(idx)==1
calllib('pclab', dllFcn, idx-1);
else
valString = sprintf(' ''%s''\n', propertyEnum{:});
error('vellscope:BadEnum','Valid values are:\n%s\n', valString);
end
end
function settings = getSettings(obj)
x = zeros(1,11,'int32');
xp = libpointer('int32Ptr',x);
calllib('pclab', 'GetSettings', xp);
data = double(xp.Value);
settings.Voltage1 = data(1); % Volts per Div, Ch 1
settings.Voltage2 = data(2); % Volts per Div, Ch 2
settings.Time = data(3);
settings.YPosition1 = data(4);
settings.YPosition2 = data(5);
settings.Coupling1 = data(6);
settings.Coupling2 = data(7);
settings.TrgOn = data(8);
settings.TrgSource = data(9);
settings.TrgEdge = data(10);
settings.TrgLevel = data(11);
end
end
methods(Static)
function showMethods()
fprintf('\n')
fprintf('Scope Methods:\n');
fprintf(' ><a href="matlab:help vellscope.pclab.startScope">startScope</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.stopScope">stopScope</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.readChannel">readChannel(channelnum)</a>\n');
fprintf('\n');
fprintf('Function Generator Methods\n');
fprintf(' ><a href="matlab:help vellscope.pclab.startGen">startGen</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.stopGen">stopGen</a>\n');
fprintf('\n');
fprintf(' Miscellaneous\n');
fprintf(' ><a href="matlab:help vellscope.pclab.show">show</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.hide">hide</a>\n');
fprintf('\n');
end
function showProperties()
fprintf('\n')
fprintf('Scope Properties:\n');
fprintf(' ><a href="matlab:help vellscope.pclab.VoltsPerDiv_Ch1">VoltsPerDiv_Ch1</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.VoltsPerDiv_Ch2">VoltsPerDiv_Ch2</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.TimePerDiv">TimePerDiv</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.Trigger">Trigger</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.TriggerEdge">TriggerEdge</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.TriggerSource">TriggerSource</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.TriggerLevel">TriggerLevel</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.YPosition_Ch1">YPosition_Ch1</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.YPosition_Ch2">YPosition_Ch2</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.Coupling_Ch1">Coupling_Ch1</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.Coupling_Ch2">Coupling_Ch2</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.DataReady">DataReady</a>\n');
fprintf('\n');
fprintf('Function Generator Properties\n');
fprintf(' ><a href="matlab:help vellscope.pclab.FgenSource">FgenSource</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.FgenFunction">FgenFunction</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.FgenFrequency">FgenFrequency</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.FgenAmplitude">FgenAmplitude</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.FgenOffset">FgenOffset</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.FgenLibFile">FgenLibFile</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.SweepStartFreq">SweepStartFreq</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.SweepStopFreq">SweepStopFreq</a>\n');
fprintf(' ><a href="matlab:help vellscope.pclab.SweepTime">SweepTime</a>\n');
fprintf('\n');
end
function out = addSDKToWindowsPath()
if ~strcmpi(computer,'pcwin')
error('Velleman PCSGU250 library is only accessible with 32-bit MATLAB on Windows');
end
if ~ispref('vellscope', 'SDKDir')
error('You need to run vellscope.setup first');
end
SDKDir = getpref('vellscope', 'SDKDir');
currentWindowsPath = getenv('PATH');
if isempty(strfind(currentWindowsPath, SDKDir))
% not already on the path
newpath = [currentWindowsPath pathsep SDKDir pathsep];
setenv('PATH', newpath);
end
out = SDKDir;
% note: new path is not saved
end
end
end