Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Executing moves and traces (gather data) from Matlab
#1
Hi all,

I would like to execute moves and define traces (gather data) on a Turbo PMAC2 from Matlab. Does anyone know how to do this?

Maybe download a motion program from Matlab (and start it) and define traces (and retrieve the data) into Matlab? Maybe a direct way to use a serial-port communication to enter motion commands, just like what is possible from the terminal in Pewin32PRO2?

My ultimate goal would be to perform fully automated motor-calibrations or frequency response measurements from Matlab.

Thanks for your input.

regards,

Meindert
#2
If you would like to communicate with PMAC from a host computer, you must purchase the Pcomm Server Pro 2 Library from your nearest distributor. This will give you drivers and 400+ functions for communicating with PMAC. There are several examples inside for setting up PMAC with the host in various languages (Visual Basic, C++, C#). However, we do not as of now have any examples of how to do this with MATLAB.

That is not to say you cannot do it. If MATLAB can call C++ functions, you may be able to fully integrate the function library with MATLAB by having MATLAB call the library's functions. However, this is not something we have documented, to my knowledge. Please post something useful if you try it and figure out how to do it.
#3
Charles,

I have been able to communicate with the controller from Matlab, using the main serial port of my UMAC controller. In this post I will list the pinning for the cable and an example program to read one I-variable.
In a next post I will post the matlab code I used to read a Gather buffer.

For the cable, I took a standard 1:1 serial extension cable, cut off the male connector and soldered the wires to a 26 pin connector that goes to J7 on page 19 in 3U Turbo CPU Board - HRM.

Serial Cable
SubD
9 pin female
description Serial Port 26 pin (J7) description
1nc
2Rx5Send Data
3Tx3Receive Data
4DTR14Data Set Ready
5GND13GND
6DSR11Data Terminal Ready
7 RTS 7Clear To Send
8CTS9 Request To Send
9nc

The following matlab code connects to serial port COM1, sends an "I10" string and retrieves the answer from the controller.


Code:
% ReadIVarExample;
%
% This script shows an example on how to read an I-variable using the main
% serial Delta Tau controler port. See GatherData.m for more details.
%
% Assumptions:
% I1    = 1             Serial Port Mode
% I3    = 2             I/O Handshake Control
% I54   = 12 (default)  Baudrate = 38400
%
% If this program errors and ends without properly closing the serial
% port, it might be impossible to re-connect to the port the next time. The
% following line of code will release the port and clear the port-object.
% newobjs  = instrfind,fclose(newobjs);clear newobjs;

%
% file           : ReadIVarExample.m
% last changes   : February 3rd, 2011
% author         : M.L.Norg
% used functions : -
% version        : 1.0
% notes          :

% Connect to serial port object and set parameters.
s                       = serial('COM1');    
s.BaudRate              = 38400;
s.InputBufferSize       = 4096;
s.FlowControl           = 'none';
s.Terminator            = 'CR';
s.Timeout               = 1;
s.ReadAsyncMode         = 'continuous';
s.DataTerminalReady     = 'off';
%  Open port
fopen(s);

IVariableString         = 'I10';                        % ServoInterruptTimeI10
fprintf(s,IVariableString);                             % Send string
pause(0.1);                                             % Give controller some time to respond.
ReturnString            = fgetl(s);                     % Read till next <LF>
IVariableValue          = str2num(ReturnString);        % Convert to number
ServoSampleTime         = IVariableValue / 8388608e3;   % Convert to seconds
ServoFrequency          = 1/ ServoSampleTime;           % Convert to frequency
disp([IVariableString,' = ',num2str(IVariableValue),'. ServoFrequency = ',num2str(ServoFrequency),' [Hz]']);

% Every request is followed by a Handshake string (<ACK> or <BELL>).
% If this string stays in serial buffer, an fgetl will not read anything
% (because there are no <LF> characters.
% Read handshake string:
if s.BytesAvailable,
    AckStr = fread(s,s.BytesAvailable);
    if (single(AckStr(1))==6)
        HandShakeString     = 'ACK';                    % In case of success
    elseif (single(ReturnValueStr(1))==7)
        HandShakeString     = 'BELL';                   % In case of error
    else
        HandShakeString     = AckStr;                   % otherwise
    end
end
disp(['HandShake String = ',HandShakeString]);

% Properly clean up serial object
fclose(s);delete(s);clear s;

If anyone is going to experiment with this code, let me know if there are any problems. I'd be happy to assist.

regards,

Meindert Norg
#4
This code reads the contents of a Gather buffer on my UMAC controller through an RS232 COM port. Before running this code, make sure there is measurement data available in the buffer.

Again, let me know if there are any problems with running this code.

Enjoy. Meindert Norg

Updates:
  • V1.0 Original version
  • V1.1 2011-02-08
    .zip   GatherData_v1_1.zip (Size: 3.48 KB / Downloads: 13)
    • Added help info on I54.
    • Added Time to file.
  • V2.0 2011-02-10
    .zip   GatherData_v2_0.zip (Size: 4.85 KB / Downloads: 8)
    • Added 48 INT signals conversion.
    • Tested I5051 Data Gathering Selection Mask 2.
    • Split "Read+Conversion" loop into "Read" loop and "Conversion" loop.
    • Used start- and stop-buffer location without the use of pre-defined M-variables
  • V2.1 2011-02-10
    .zip   GatherData_v2_1.zip (Size: 4.88 KB / Downloads: 9)
    • Fixed SignalType pre-definition.
    • Fixed notification on 'incorrect 12HexWord' length.
  • v2.2 2011-02-11
    • Checked for empty GATHER buffer.
  • v2.3 2011-02-24
    .zip   DT_GatherData_Serial_v2_3.zip (Size: 5.08 KB / Downloads: 31)
    • Changed name from GatherData.m to DT_GatherData_Serial.m and used DT_GatherData.m to implement ActiveX gathering function, which is more than 12 times faster and more robust.

Code:
function [Data,Time] = DT_GatherData_Serial(port_name)
% DT_GatherData_Serial  - Reads GATHER data from a Delta Tau controller
% through a serial port.
%
% [Data,Time] = DT_GatherData_Serial('COM1') connects to the Delta Tau
% controller through COM1. It returns a vector Data containing as many
% columns as parameters that were traced, and the Time vector starting at
% t=0;
%
% Assumptions:
% I1    = 1
%       When CS handshaking is not used (I1 is 1 or 3), Turbo PMAC
%       disregards the state of the CS input and always sends the character
%       immediately. This mode permits Turbo PMAC to “output” messages,
%       values, and acknowledgments over the serial port even when there is
%       nothing connected, which can be valuable in stand-alone and
%       PLC-based applications where there are SENDS and CMDS statements in
%       the program. If these strings cannot be sent out the serial port,
%       they can back up, stopping program execution.
% I3    = 2
%       Turbo PMAC acknowledges receipt of a:
%       - valid <CR>-terminated command with an <ACK>;
%       - invalid command with a <BELL> character.
%       Messages are sent as DATA <CR> [ DATA <CR> ... ] <ACK>. (The final
%       <ACK> is the acknowledgment of the host command; it does not get
%       sent with a message initiated from a PMAC program [SEND or CMD]).
%       This is probably the best setting for fast communications with a
%       host program without terminal display.
% I54   = 12
%       I54 controls the baud rate for communications on the main serial
%       port. Turbo PMAC uses I54 only at power-up/reset to set up the
%       frequency of the clocking circuit for the serial port. To change
%       the baud rate, it is necessary to change the value of I54, store
%       this value to non-volatile flash memory with the SAVE command, and
%       reset the card. At this time, Turbo PMAC will establish the new
%       baud rate.
%
% If this program errors and ends without properly closing the serial
% port, it might be impossible to re-connect to the port the next time. The
% following line of code will release the port and clear the port-object.
% newobjs  = instrfind;fclose(newobjs);clear newobjs;

% The following definitions are used in this file:
% Signal        - A series of measurements from one source, e.g. motor 1
%                 actual position, or motor 2 filter output.
% 12HexWord     - Data is sent through the serial port in 48 bits chunks. 1
%                 48 bits chunk is 12 Hexadecimal characters. This is one
%                 12HexWord
% 6HexWord      - Each 12HExWord consists of 2 6HexWords.

%
% file           : DT_GatherData_Serial.m
% last changes   : February 8, 2011
% author         : M.L.Norg
% used functions : -
% version        : 2.0
% notes          : V1.0 Original version
%                  v1.1 Added help info on I54.
%                       Added Time to file.
%                  v2.0 2011-02-10
%                       Added 48 INT signals conversion.
%                       Tested I5051 Data Gathering Selection Mask 2.
%                       Split "Read+Conversion" loop into "Read" loop
%                       and "Conversion" loop.
%                       Used start- and stop-buffer location without the
%                       use of pre-defined M-variables.
%                  v2.1 2011-02-10
%                       Fixed SignalType pre-definition.
%                       Fixed notification on 'incorrect 12HexWord' length.
%                       Fixed Time calculation
%                  v2.2 2011-02-11
%                       Checked for empty GATHER buffer.
%                  v2.3 2011-02-24
%                       Changed name from GatherData.m to
%                       DT_GatherData_Serial.m and used DT_GatherData.m to
%                       implement ActiveX gathering function, which is more
%                       than 12 times faster and more robust.

% Connect and configure RS232 port
% BaudRate      I54
%   9600         8
%  14400         9
%  19200        10
%  38400        12  % Average of 1150 samples/sec.
%  57600        13  % Average of 1786 samples/sec, but with increased
                    % number of missed characters
%  76800        14  % Not available in Matlab
% 115200        15
s                               = serial(port_name);    
s.BaudRate                      = 38400;
% Buffersize has no impact on duration of fopen(s) command, but an
% increased bufer-size will help prevent loss of data during debugging.
s.InputBufferSize               = 1000000 * 12;      
s.FlowControl                   = 'none';
s.Terminator                    = 'CR';
s.Timeout                       = 1;
s.ReadAsyncMode                 = 'continuous';
s.DataTerminalReady             = 'off';

%  Open port. Takes about 3.2 seconds
fopen(s);


% Keep track of how many 6HEX words are to be expected. A 24 bit integer
% counts for 1, a 48 bit integer counts for 2.

NrOf6HexWordsPerSample          = 0;

% Get I5050 String.
% Example is given for measuring signals in address I5003 and I5004.
% See page 150 of Turbo PMAC/PMAC2 Software Reference.
% I5050str = 'C'
I5050str                        = RequestIVariable(s,5050);

% Convert into Binary.
% I5050Bin = '1100'
I5050Bin                        = dec2bin(hex2dec(I5050str(2:end)));

% Find indexes that are one, starting to count from LSB (right).
% Example: '1100' -> '0011'.
% Find '1' indexes: 3 and 4.
I5001MaskIsOne                  = strfind(I5050Bin(end:-1:1),'1');

NumberOfSignalsI5050            = length(I5001MaskIsOne);

% Predefine SignalType with 50 spaces
SignalType                      = char(ones(50,1)*32);

% Add found indexes to 5000 to find the I-variable where the address is
% stored of the signal that needs to be traced:
% Example: 5000 + 3 = I5003, 5000 + 4 = I5004
% Also find out how what type the signals are. Get 2nd character.
%   0: Y-register only (24 bits)
%   4: X-register only (24 bits)
%   8: X/Y double register (48 bits), Executive program interprets as
%      integer
%   C: X/Y double register (48 bits), Executive program interprets as
%      floating-point


for NrSignal = 1 : length(I5001MaskIsOne)
    SignalDefinition            = RequestIVariable(s,5000+I5001MaskIsOne(NrSignal));
    SignalType(NrSignal)        = SignalDefinition(2);
end

% Repeat for second half of gather definition.
I5051str                        = RequestIVariable(s,5051);
I5051Bin                        = dec2bin(hex2dec(I5051str(2:end)));
I5025MaskIsOne                  = strfind(I5051Bin(end:-1:1),'1');
NumberOfSignalsI5051            = length(I5025MaskIsOne);
for NrSignal = 1 : length(I5025MaskIsOne)
    SignalDefinition            = RequestIVariable(s,5024+I5025MaskIsOne(NrSignal));
    SignalType(NrSignal+NumberOfSignalsI5050) = SignalDefinition(2);
end

NumberOfSignals                 = NumberOfSignalsI5050 + NumberOfSignalsI5051;

% Find out how what type the signals are, and how many 6HEX words we can
% expect.
for NrSignal = 1 : NumberOfSignals
    switch SignalType(NrSignal)
        case {'0','4'}
            NrOf6HexWordsPerSample      = NrOf6HexWordsPerSample + 1;
        case {'8','C'}
            NrOf6HexWordsPerSample      = NrOf6HexWordsPerSample + 2;
        otherwise
            error(['Unexpected signaltype: ''',SignalType(NrSignal),'''.']);
    end
end


% See page 421 of Turbo PMAC - User Manual:
% If the data gathered for a sample leaves the last grouping with only six
% characters, this last grouping is filled out with the contents of the
% servo cycle counter register X:$000000.
%
% Check if NrOf6HexWordsPerSample is odd. If so, add one to make it even.
if rem(NrOf6HexWordsPerSample,2),
    NrOf6HexWordsPerSample       = NrOf6HexWordsPerSample + 1;
    % Use 'T' as indication for Timestamps
    NumberOfSignals              = NumberOfSignals + 1;
    SignalType(NumberOfSignals)  = 'T';
end

% Get I10 - Servo Interrupt Time
% This parameter tells Turbo PMAC how much time there is between servo
% interrupts (which is controlled by hardware circuitry), so that the
% interpolation software knows how much time to increment each servo
% interrupt.
I10str                          = RequestIVariable(s,10);
ServoInterruptTimeI10           = str2double(I10str);
ServoSampleTime                 = ServoInterruptTimeI10 / 8388608e3;

% Get I5049 - Gathering Downsampling Factor
I5049str                        = RequestIVariable(s,5049);
GatherDownSampling              = str2double(I5049str);
GatherSampleTime                = ServoSampleTime * GatherDownSampling;

% Find out how large buffer is:
% pg 478 of PMAC Software Reference Manual:
%   X:$003120 Data gather buffer start address
%   Y:$003120 Data gather buffer storage address
% Get Gather Start Address
GatherStartAddressStr           = RequestVariable(s,'RX:$003120');
GatherStartAddress              = str2double(GatherStartAddressStr);

% Get Gather End Address
GatherEndAddressStr             = RequestVariable(s,'RY:$003120');
GatherEndAddress                = str2double(GatherEndAddressStr);

NumberOf12HexWords              = (GatherEndAddress - GatherStartAddress);

% Check if GATHER buffer is empty.
if ~NumberOf12HexWords
    Data                        = [];
    Time                        = [];
    disp('GATHER Buffer is empty');
    % clean up serial object
    fclose(s);delete(s);clear s;
else
    % Predefine DataStr with X-es (char(88)). If an corrupt Word12Hex is found,
    % 12Xes will be used to indicate the corruption. This will eventually be
    % converted into NaNs.
    DataStr                         = char(88*ones(NumberOf12HexWords,12));

    % Ask for data from buffer in 48 bits (12 HEX) size chunks.
    fprintf(s,'LIST GATHER')
    tstart                          = clock;    % Calculate how long Xfer takes.
    LineHex                         = [];

    % Read data
    for Nr12HexWord = 1 : NumberOf12HexWords
        if isempty(LineHex)
            LineHex                 = fgetl(s);
        end

        % Find spaces in LineHex
        iSpaces                     = findstr(LineHex,' ');

        % If no spaces are found, only one Word12Hex is left in LineHex
        if isempty(iSpaces)
            Word12Hex               = LineHex;
            LineHex                 = [];
        else
            Word12Hex               = LineHex(1:iSpaces(1)-1);
            LineHex                 = LineHex(iSpaces(1)+1:end);
        end

        if (length(Word12Hex)==12)
            DataStr(Nr12HexWord,:)  = Word12Hex;
        else
            disp(['Warning: "',Word12Hex,'" of incorrect length. Length is ',...
                   num2str(length(Word12Hex)),' and should be 12.']);
        end
    end

    disp(['Read ',num2str(NumberOf12HexWords),' 42 bit words in ',...
           num2str(etime(clock,tstart)),' seconds. ',...
           num2str((NumberOf12HexWords*42)/etime(clock,tstart)),' [bps].' ]);
    % clean up serial object
    fclose(s);delete(s);clear s;

    NumberOfSamples                 = NumberOf12HexWords / (NrOf6HexWordsPerSample/2);

    % Change from Round Robin style into regular style:
    % Lets say we measured 1 24 bit (A), 1 42 bit(HL), plus time (T):
    % DataStr is now filled like:
    %   LA
    %   TH
    %   LA
    %   TH
    %
    % Change order into:
    %   AL
    %   HT
    %   AL
    %   HT
    %
    % And reshape into:
    %   ALHT
    %   ALHT
    %
    % When grabbing the parts for a 48 int, (LH) first grab High part H and
    % then Low part L.

    DataStrChangedOrder             = [DataStr(:,7:12) DataStr(:,1:6)];
    DataStrReshaped                 = reshape(DataStrChangedOrder',6*NrOf6HexWordsPerSample,NumberOfSamples)';

    % Predefine Data as matrix with NaNs.
    Data                            = NaN*ones(NumberOfSamples,NumberOfSignals);

    % page 246 Turbo PMAC User Manual:
    % An M-variable may take one of the following types, as specified by the address prefix in the definition:
    % X: 1 to 24 bits fixed-point in X-memory
    % Y: 1 to 24 bits fixed-point in Y-memory
    % D: 48 bits fixed-point across both X- and Y-memory
    % L: 48 bits floating-point across both X- and Y-memory
    % DP: 32 bits fixed-point (low 16 bits of X and Y) (for use in dual-ported RAM)
    % F: 32 bits floating-point (low 16 bits of X and Y) (for use in dual-ported RAM)
    % TWD: Multiplexed BCD decoding from Thumbwheel port
    % TWB: Multiplexed binary decoding from Thumbwheel port
    % TWS: Multiplexed serial I/O decoding from Thumbwheel port
    % TWR: Multiplexed serial resolver decoding from Thumbwheel port
    % *: No address definition;

    for NrSample = 1 : NumberOfSamples,
        iDataStart              = 1;
        for NrSignal = 1 : NumberOfSignals
            switch SignalType(NrSignal)
                case {'0','4','T'},
                    SampleValueStr              = DataStrReshaped(NrSample,iDataStart+(0:5));
                    % Check for X contents. This means data was corrupted.
                    if isempty(strfind(SampleValueStr,'X')),
                        SampleValue             = hex2dec(SampleValueStr);
                        % Convert from 24 unsigned int to signed int
                        SampleValue             = SampleValue - 2^24 * (SampleValue > 2^23);
                        Data(NrSample,NrSignal) = SampleValue;
                    else
                        % Fill with NaN if data was corrupted.
                        Data(NrSample,NrSignal) = NaN;
                    end
                    iDataStart                  = iDataStart+6;
                case '8',
                    SampleValueStr              = DataStrReshaped(NrSample,iDataStart+[6 7 8 9 10 11 0 1 2 3 4 5]);
                    % Check for X contents. This means data was corrupted.
                    if isempty(strfind(SampleValueStr,'X')),
                        SampleValue             = hex2dec(SampleValueStr);
                        % Convert from 48 unsigned int to signed int
                        SampleValue             = SampleValue - 2^48 * (SampleValue > 2^47);
                        Data(NrSample,NrSignal) = SampleValue;
                        % Fill with NaN if data was corrupted.
                    else
                        Data(NrSample,NrSignal) = NaN;
                    end
                    iDataStart                  = iDataStart+12;
                case 'C',
                    error('Not implemented yet');
                otherwise
                    error(['Unexpected signaltype: ''',SignalType(NrSignal),'''.']);
            end
        end
    end

    Time = (0:NumberOfSamples-1) * GatherSampleTime;
end

function [ReturnString,HandshakeString] = RequestIVariable(s,IvarNumber)
    % This function sends an I variable value and processes the response.
    IVarString                  = ['I',num2str(IvarNumber)];
    fprintf(s,IVarString);
    pause(0.1);
    ReturnString                = fgetl(s);
    HandshakeString             = ReadHandshake(s);

function [ReturnString,HandshakeString] = RequestVariable(s,VarString)
    % This function sends a string and expects a return string.
    fprintf(s,VarString);
    ReturnString                = fgetl(s);
    HandshakeString             = ReadHandshake(s);


function HandShakeString = ReadHandshake(s)
    % This function is called to read the handshake string <ACK> or <BELL>
    HandShakeString             = '';
    if s.BytesAvailable,
        clear ReturnValueStr;
        ReturnValueStr = fread(s,s.BytesAvailable);
        if (single(ReturnValueStr(1))==6)
            HandShakeString     = 'ACK';
        elseif (single(ReturnValueStr(1))==7)
            HandShakeString     = 'BELL';
        else
            HandShakeString     = ReturnValueStr;
        end
    end
#5
Hi Meindert,

Thanks so much for posting this information. It is very useful!

I can suggest a method that may be more reliable for you to get data out of the gather buffer than using the "LIST GAT" command. It is called "Indirect Addressing". Basically, you point an M-Variable (let's just call it GatPtr) to the first memory address in the gather buffer, point another M-Variable (let's just call it pGatPtr) to the address definition of the first M-Variable, and then you can step through the buffer by incrementing pGatPtr and looking at the GatPtr returns. From p. 505 of the Turbo SRM pdf (printed page 487):

X:$003120 Data gather buffer start address - lists the Y address (24 bits) containing the first gathered point
Y:$003120 Data gather buffer storage address - lists the last Y address (24 bits) containing the last gathered point

Code:
#define GatStartAddress            M5000
#define GatEndAddress            M5001

GatStartAddress->X:$003120,0,24    ; Start of gather buffer
GatEndAddress->Y:$003120,0,24    ; End of gather buffer

Then you can assign an M-Variable to whatever value GatStartAddress returns, another M-Variable to what GatEndAddress returns, and then the difference between the two values will be the total number of points gathered. Then, you can determine the number of points per source by dividing the total number of points by the number of sources gathered. The gather buffer will be filled in round-robin style; e.g., for two sources:

First source first point in first gather buffer memory location, second source first point in second gather buffer memory location, first source second point in third memory location, second source second point in fourth memory location, etc. As such, if you want to get every point successively from the first source, you will have to point to the first location with the first M-Variable (GatPtr), get the value out of it, then increment pGatPtr by the number of sources (which is 2 in this example).

Please see page printed p. 247 of the Turbo PMAC User Manual for more details on Indirect Addressing, or refer to the attached slide. You can also refer to this forum post:

http://forums.deltatau.com/showthread.ph...t=manually

It discusses reading the gather buffer manually, but does not actually cover indirect addressing. I realize this may be a difficult task, so please let me know if you need any help figuring this out and I would be happy to help.


Attached Files
.pdf   Indirect Addressing Slide.pdf (Size: 80.88 KB / Downloads: 42)
#6
Charles.

Thanks for your suggestion.
I implemented this method, but have the following issues / worries:
  1. The original method, using LIST GATHER does not require any pre-defined variables, as this Indirect Addressing method does. This is nice, because it can always be used after a gather is executed.
  2. Using LIST GATHER method at baudrate 38400 bps I measured a transfer rate of 1100 samples per second. Using Indirect Addressing, that dropped to 63 samples per second. Here are the main differences:
    • LIST GATHER: 6 characters per sample.
      • Send one request for data, and result is streaming to PC without any further request: 6 characters per sample.
    • Indirect Addressing: approx. 18 characters per sample. To get one 12 HEX word (2 samples) one needs to:
      • Send one change of address: 'M4005=xxxxx': 11 characters per 2 samples.
      • Request contents of memory M4004 is pointing to: 'M4004': 5 characters
      • Read result in signed int format: up to 16 characters per 2 samples
      • Every write also returns the 'ACK' value.
    • Although this is only 3 times more data, the reading and writing (instead of reading a stream) results in a 17 x slower transfer of data.
Anyway, here is the code I used to test this method.

Thanks.

Meindert

Code:
function [DATA,Time] = GatherDataIndirectAddressing(port_name)
%
% GatherDataIndirectAddressing  - Reads GATHER data from a Delta Tau
%       controller through a serial port using Indirect Memory Addressing.
%
% [DATA,Time] = GatherDataIndirectAddressing(COM1') connects to the Delta Tau
% controller through COM1.
% It returns a vector DATA containing as many columns as parameters that
% were traced, and the Time vector containing timestamps.
%
% Assumptions:
% I1    = 1
%       When CS handshaking is not used (I1 is 1 or 3), Turbo PMAC
%       disregards the state of the CS input and always sends the character
%       immediately. This mode permits Turbo PMAC to “output” messages,
%       values, and acknowledgments over the serial port even when there is
%       nothing connected, which can be valuable in stand-alone and
%       PLC-based applications where there are SENDS and CMDS statements in
%       the program. If these strings cannot be sent out the serial port,
%       they can back up, stopping program execution.
% I3    = 2
%       Turbo PMAC acknowledges receipt of a:
%       - valid <CR>-terminated command with an <ACK>;
%       - invalid command with a <BELL> character.
%       Messages are sent as DATA <CR> [ DATA <CR> ... ] <ACK>. (The final
%       <ACK> is the acknowledgment of the host command; it does not get
%       sent with a message initiated from a PMAC program [SEND or CMD]).
%       This is probably the best setting for fast communications with a
%       host program without terminal display.
% I54   = 12
%       I54 controls the baud rate for communications on the main serial
%       port. Turbo PMAC uses I54 only at power-up/reset to set up the
%       frequency of the clocking circuit for the serial port. To change
%       the baud rate, it is necessary to change the value of I54, store
%       this value to non-volatile flash memory with the SAVE command, and
%       reset the card. At this time, Turbo PMAC will establish the new
%       baud rate.
%
% The following M-variable definitions are required:
% Note that one can choose other M-variables, but that the definition of
% M4005 has to change to point to the 'new' M4004 variable.
% Also, where these variables are used, matlab-code has to change to access
% the proper variables.
%
% M4002->X:$003120,0,24        
%       Start of gather buffer.
% M4003->Y:$003120,0,24        
%       End of gather buffer.
% M4004->D:$117E6
%       Possible start of Gather buffer. Exact value is not important
%       because it will later be changed by this Matlab function by means
%       of M4005
%       M-variable address definitions are in fixed locations in Turbo PMAC
%       memory, starting at $004000 (for M0) and ending at $005FFF (for
%       M8191). The X-register at each of these addresses holds the code
%       that determines the format of the M-variable; the Y-register holds
%       the address of the register being pointed to. By changing the
%       contents of this Y-register, you can change the address of the
%       register that this Mvariable points to.
% M4005->Y:$004FA5,0,16
%       Memory location of M4004:
%           4000_HEX + 4005_DEC =
%           4000_HEX + FA5_HEX = 4FA5
%
% When this program errors and ends without properly closing the serial
% port, it might be impossible to re-connect to the port the next time. The
% following line of code will release the port and clear the port-object.
% newobjs  = instrfind;fclose(newobjs);clear newobjs;

%
% file           : GatherDataIndirectAddressing.m
% last changes   : February 8nd, 2011
% author         : M.L.Norg
% used functions : -
% version        : 1.0
% notes          : Changed GatherData.m to use IndirectAddressing as
%                  suggested by CharlesP in:
%                  http://forums.deltatau.com/showthread.php?tid=354
%

% Connect and configure RS232 port
% BaudRate      I54
%   9600         8
%  14400         9
%  19200        10
%  38400        12
%  57600        13
%  76800        14  % Not available in Matlab
% 115200        15
s                               = serial(port_name);    
s.BaudRate                      = 38400;
s.InputBufferSize               = 4096;
s.FlowControl                   = 'none';
s.Terminator                    = 'CR';
s.Timeout                       = 1;
s.ReadAsyncMode                 = 'continuous';
s.DataTerminalReady             = 'off';

%  Open port
fopen(s);

% How many signals are gathered?
I5050str                        = RequestVariable(s,'I5050');
NumberOfSignalsI5050            = length((strfind(dec2bin(hex2dec(I5050str(2:end))),'1')));
I5051str                        = RequestVariable(s,'I5051');
NumberOfSignalsI5051            = length((strfind(dec2bin(hex2dec(I5051str(2:end))),'1')));
NumberOfSignals                 = NumberOfSignalsI5050 + NumberOfSignalsI5051;

% Get I10 - Servo Interrupt Time
% This parameter tells Turbo PMAC how much time there is between servo
% interrupts (which is controlled by hardware circuitry), so that the
% interpolation software knows how much time to increment each servo
% interrupt.
I10str                          = RequestVariable(s,'I10');
ServoInterruptTimeI10           = str2double(I10str);
ServoSampleTime                 = ServoInterruptTimeI10 / 8388608e3;

% Get I5049 - Gathering Downsampling Factor
I5049str                        = RequestVariable(s,'I5049');
GatherDownSampling              = str2double(I5049str);
GatherSampleTime                = ServoSampleTime * GatherDownSampling;

% In case of an odd number of signals, the time-clock (sample number) is
% added to the back of the measurement.
% Calculate the number of 8Hex words per sample.
% NumberOfSignals:              1   2   3   4   5   6   7   8   9   10
% NumberOf16HexWords:           1   1   2   2   3   3   4   4   5   5
% NumberOf6HexWordsPerSample:   2   2   4   4   6   6   8   8   10  10
NumberOf6HexWordsPerSample      = ceil(NumberOfSignals/2)*2;

% Get M4002 - Get Gather Start Address
M4002str                        = RequestVariable(s,'M4002');
GatherStartAddress              = str2double(M4002str);
% Get M4003 - Get Gather End Address
M4003str                        = RequestVariable(s,'M4003');
GatherEndAddress                = str2double(M4003str);

NumberOfSamples                 = ceil((GatherEndAddress - GatherStartAddress) / (NumberOf6HexWordsPerSample/2));
DATA                            = zeros(NumberOfSamples,NumberOf6HexWordsPerSample);
% clear DATA
tstart                          = clock;    % Calculate how long Xfer takes.
Counter6HexWords                = 0;        % Reset sample counter
for Address2Read = GatherStartAddress : GatherEndAddress-1
    % Change value of M4005, by which the location M4004 points to is
    % changed.
    HandShStr                   = SendString(s,['M4005=',num2str(Address2Read)]);
    % Request value of memory where M4004 points to:
    DataStr                     = RequestVariable(s,'M4004');
    % Convert to Decimal
    DataNum                     = str2double(DataStr);
    % If value is smaller than zero, add 2^48, making it an unsigned int.
    if DataNum < 0,
        DataNum                 = DataNum + 2^48;
    end
    % Convert to 48 bit Hexadecimal word, so it can be broken down in its
    % upper 24 bits and lower 24 bits
    Word12Hex                   = dec2hex(DataNum,12);

    % Example of Word12Hex
    % 05E7E50026A0
    % \          /
    %  -----+----
    %       |    
    %       V    
    %   Word12Hex
    %
    % 05E7E50026A0
    % \    /\    /
    %  -+--  --+-
    %   |      |  
    %   |      V  
    %   |   Word6Hex  
    %   V
    % Word6Hex
    %
    % Keep count of 6Hex words (6Hex word = 1 sample of 1 signal)
    Counter6HexWords        = Counter6HexWords + 1;

    % Calculate which Row needs to be filled
    RowNr                   = ceil(Counter6HexWords/NumberOf6HexWordsPerSample);

    % Calculate which Column needs to be filled
    % NOTE: first evaluate the RIGHT part of Word12Hex.
    ColumnNr                = rem(Counter6HexWords-1,NumberOf6HexWordsPerSample)+1;
    DATA(RowNr,ColumnNr)    = hex2dec(Word12Hex(7:12));

    % Keep count of 8Hex words (8Hex word = 1 sample of 1 signal)
    Counter6HexWords        = Counter6HexWords + 1;

    % Calculate which Column needs to be filled
    % NOTE: Now evaluate the LEFT part of Word12Hex.
    ColumnNr                = rem(Counter6HexWords-1,NumberOf6HexWordsPerSample)+1;
    DATA(RowNr,ColumnNr)    = hex2dec(Word12Hex(1:6));
end

disp(['Read ',num2str(Counter6HexWords),' 42 bit words (= ',...
       num2str(2*Counter6HexWords),' samples) in ',...
       num2str(etime(clock,tstart)),' seconds. ',...
       num2str((2*Counter6HexWords)/etime(clock,tstart)),' [samples/sec].' ]);
  
% convert from unsigned int into signed integer:
% Check which numbers are larger than 2^23 and subtract 2^24
iFlip          = DATA>2^23;
DATA(iFlip)    = DATA(iFlip) - 2^24;

Time = [1:size(DATA,1)]' * GatherSampleTime;

% clean up serial object
fclose(s);delete(s);clear s;


function [HandshakeString] = SendString(s,VarString)
    % This function sends a string without expecting a response other than
    % an ACK.
    fprintf(s,VarString);
    HandshakeString             = ReadHandshake(s);

function [ReturnString,HandshakeString] = RequestVariable(s,VarString)
    % This function sends a string and expects a return string.
    fprintf(s,VarString);
    ReturnString                = fgetl(s);
    HandshakeString             = ReadHandshake(s);

function HandShakeString = ReadHandshake(s)
    % This function is called to read the handshake string <ACK> or <BELL>.
    HandShakeString             = '';
    % Make sure something is available to be read, but error when wait is
    % longer than 5 seconds.
    tStartWait                  = clock;
    while ~s.BytesAvailable;
        if etime(clock,tStartWait) > 5
            % clean up serial object
            fclose(s);delete(s);clear s;
            error('Waited longer than 5 seconds for Handshake value.');
        end
    end
    if s.BytesAvailable,
        clear ReturnValueStr;
        ReturnValueStr = fread(s,s.BytesAvailable);
        if (single(ReturnValueStr(1))==6)
            HandShakeString     = 'ACK';
        elseif (single(ReturnValueStr(1))==7)
            HandShakeString     = 'BELL';
        else
            HandShakeString     = ReturnValueStr;
            warning(['Read handshake string: ''',HandShakeString,''' while expecting ''ACK'' or ''BELL''.']);
        end
    end
#7
Thanks for your post, Meindert, and for the detailed analysis and comparison of the two methods. This is very valuable reference material! We appreciate your hard work and contribution to the forum.
#8
Hi,

I also found a way to connect to the ActiveX COM-interface from Matlab. This is a short Matlab-code example to request the value of I130 from the controller:
Code:
% First connect to the Delta Tau COM server, which is implemented in
% C:\WINDOWS\system32\PcommServer.exe. On my system, this executable is
% started automatically when I call actxserver:
hPcommServer            = actxserver('PcommServer.PMacDevice');

% Create a handle to the custom interface:
% This handle "hPmacDevice" will be the actual interfaces that is used to
% make calls to the Delta Tau controller.
hPmacDevice             = hPcommServer.invoke('IPmacDevice');

% Open the interface to device 0.
% From "PEWIN32PRO2", menu -> Terminal -> Select PMAC
% opens a window with PMAC Devices. My UMAC is listed as number 00.
results1                = hPmacDevice.Open(0);

% Send a string and display its response, using GetResponseEx method:
GetResponseExString     = hPmacDevice.GetResponseEx(0,'I130',1)

% Clean up:
hPmacDevice.Close(0)
hPmacDevice.delete
clear hPcommServer;

I attached the 'published' result of Matlab code with more explanation.
I've not been able to successfully use gather related methods. I might be making the wrong calls (the PcommServer.pdf manual is somewhat unclear on what means what, and the examples don't get me any further either), or Matlab just does not like the Delta Tau COM implementation for those specific methods. I will update this post if I find out more.

regards,

Meindert


Attached Files
.pdf   ActiveXExample.pdf (Size: 73.33 KB / Downloads: 55)


Forum Jump:


Users browsing this thread: 1 Guest(s)