Using public class methods and libraries in parfeval()

18 views (last 30 days)
Hello everyone,
I've been googling the entire day, but nobody seems to have quite the same problem as myself at the moment.
I've programmed a matlab-class to communicate to a USB-SPI Interface Chip via the provided drivers. No Problem here. I can execute everything and read and write Data properly and quickly.
My Problem starts, as soon as I start to put this Code into a parallel pool. Since it's some very active Code (i.e. polling of IO-Pins and such) I need it to run separately from everything else.
My Code:
Firstly, I open the (properly working outside of a parallel pool) Class:
% Open ACCELEROMETER Class
ACC = ADXL372;
% Configure both Channels, as per documentation
[P1,C1]=ACC.ADXL_Config(1);
[P2,C2]=ACC.ADXL_Config(2);
My constructor looks as follows:
function ADXL = ADXL372()
if ~libisloaded( ADXL.Libname )
loadlibrary( ADXL.Libname, 'libMPSSE_spi_matlabFriendly.h');
end
if ~libisloaded( ADXL.Libname2 )
loadlibrary( ADXL.Libname2);
end
clc
% Get the number of SPI channels available.
calllib(ADXL.Libname,'SPI_GetNumChannels',ADXL.pNumchannels);
disp(['Channels Found: ', num2str(get(ADXL.pNumchannels,'value'))]);
% Connect to SPI channel 0. Valid numbers are 0:(Numchannels-1).
E = calllib(ADXL.Libname,'SPI_OpenChannel',0,ADXL.pChannelHandle);
disp(['Channel Opened with Errorcode: ', num2str(E)]);
% Set GPIO for Receiving Interrupt
E=calllib(ADXL.Libname,'FT_WriteGPIO',ADXL.pChannelHandle,0,0);
disp(['GPIO Set with Errorcode: ',num2str(E)]);
% Set Latency Timer to 1ms
E=calllib(ADXL.Libname2,'FT_SetLatencyTimer',ADXL.pChannelHandle,1);
disp(['LatencyTimer Set with Errorcode: ',num2str(E)]);
% Define the channel configuration struct, and initialize the channel.
ChConfig.ClockRate = uint32(10e6); % Clock speed, Hz
ChConfig.LatencyTimer = uint32(1); % Users guide section 3.4, suggested value is 2-255 for all devices
ChConfig.configOptions = uint32(0b100000); % Bit 1 is CPOL, bit 0 is CPHA. Higher order bits configure the chip select.
ChConfig.Pin = uint32(0x00000000);
E = calllib(ADXL.Libname,'SPI_InitChannel',ADXL.pChannelHandle,ChConfig);
disp(['SPI configured with Errorcode: ',num2str(E)]);
end
I've found solutions to make my main file know, that the function exists. Now at least I'm not getting the "Unknown Function ..." Error.
F = parfeval(gcp(),@(varargin) ACC.ReadFIFO(varargin{:}),4,Dauer,10);
with ReadFIFO being:
function [MessungA,MessungB,TVectorA,TVectorB] = ReadFIFO(ADXL,Time,Means)
% Some code that's irrelevant to this question
end
The Problem:
I need to run ReadFIFO in a separate pool for the rest of my program to work properly. First I got errors, that the function was unkown. Now I'm getting Errors that say "Library was not found", as if the pool doesn't receive the loaded libraries.
I can provide more code, but it essentially all looks like this example:
calllib(ADXL.Libname,'SPI_ChangeCS',ADXL.pChannelHandle, uint32(0b100000));

Accepted Answer

Edric Ellis
Edric Ellis on 7 Jun 2022
I suspect the problem here is that when you transfer instances of your ADXL372 class to the workers, they are not getting set up correctly. The data transfer mechanism from client to worker when you invoke parfeval is equivalent to saving your objects to disk, and then loading them on the worker (it all happens in memory though). The problem is almost certainly that your loadlibrary calls are not being invoked on the workers.
By default, MATLAB classes do not call the constructor during load (or when they're received by a worker for a parfeval call). See this page in the doc for more details. I think you have two options here:
Here's an example of what I mean for the 2nd point:
% This causes the ADXL372 constructor to be invoked on every worker
adxlConst = parallel.pool.Constant(@ADXL372)
% Pass the Constant to the workers, extract the ".Value" and use that
F = parfeval(gcp(),@(c, varargin) ReadFIFO(c.Value, varargin{:}),4, adxlConst, Dauer, 10);
  5 Comments
Edric Ellis
Edric Ellis on 8 Jun 2022
The statement
app.adxlConst = parallel.pool.Constant(@ADXL372);
builds an instance of ADXL372 on each worker. I thought that SPISetup needed to be called by each process to set up state there - but it sounds like that is not the case?
If you want to update a UI on completion of a Future, you might want to use afterEach - this is designed for that sort of situation.
Martin Quintus
Martin Quintus on 8 Jun 2022
Edited: Martin Quintus on 8 Jun 2022
EDIT:
NEVERMIND, I'm dumb.
there was a STOP()-function still in my code, that was being executed, as soon as the accelerometers started. It's even in this comment right between the two StatusLED-function calls.
It works now!
Original Message:
I only need one instance of ADXL372.
Sorry if my information isn't sufficient enough. I've never used either classes or the parallel computing toolbox before but need both for my master's thesis.
In essence:
ADXL372 is a class I programmed, whose sole purpose is setting up and communicating with two ADXL372 accelerometers via a single FTDI USB-SPI interface.
The class's properties consist of all possible configuration bits in mnemonic form, so that later re-configuration is easier.
The class's methods include constructor (which WAS used to load the libraries AND set the SPI Chip up. These two functions have since been moved to separate methods) and readFIFO which, well, reads the FIFO.
readFIFO is polling a Pin on the FTDI-Interface to determine whether data is available and is the sole reason that this whole thing needs to run in parallel. I need to be able to read the FIFO and simultaneously talk to two maxon motors and a read/write serialport (the latter of which I haven't even touched yet).
I've tried afterEach, but it locks everything (i.e. GUI consisting of two gauges and both motors) up completely. Not even the motors move after Starting. This is the Start function:
% Button pushed function: STARTButton
function STARTButtonPushed(app, event)
app.StatusLamp.Color = 'red';
if app.AxialSwitch.Value == "On"
app.Motor1.SetOperationMode( OperationModes.ProfileVelocityMode );
app.Motor1.WaitUntilDone(1000);
app.Motor1.MotionInVelocity( app.GeschwindigkeitUminEditField_2.Value, app.BeschleunigungUminsEditField_2.Value );
end
% ^^^^^ works fine on its own and in combination with Motor2
if app.TransversalSwitch.Value == "On"
app.Motor2.SetOperationMode( OperationModes.ProfileVelocityMode );
app.Motor2.WaitUntilDone(1000);
app.Motor2.MotionInVelocity( app.GeschwindigkeitUminEditField.Value, app.BeschleunigungUminsEditField.Value );
end
% ^^^^^ works fine on its own and in combination with Motor1
if app.ACCSwitch.Value == "On"
j = parfeval(gcp(),@(c, varargin) ReadFIFO(c.Value, varargin{:}),4, app.adxlConst, app.MessungsdauersEditField.Value, app.MovingAveragesEditField.Value);
afterEach(j,@(A,B,tA,tB) app.DisplayACCData(A,B,tA,tB),0);
app.StatusLamp.Color = 'yellow';
STOP(app);
app.StatusLamp.Color = 'green';
end
% ^^^^^ locks GUI and Motors 1&2 up as soon as statement is true and releases
% them as soon as readFIFO is done.
end

Sign in to comment.

More Answers (0)

Categories

Find more on Asynchronous Parallel Programming in Help Center and File Exchange

Products


Release

R2020b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!