Code covered by the BSD License  

Highlights from
MATLAB Support Package for Velleman PCSGU250 Oscilloscope + Function Generator

MATLAB Support Package for Velleman PCSGU250 Oscilloscope + Function Generator

by

 

25 Jul 2011 (Updated )

MATLAB library for controlling a Velleman PCSGU250 scope and generator

vellscope.pclab
% 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

Contact us