Shared memory between TcCOM instances
In some applications it may be advantageous for TcCOM instances to share a memory area, so that certain structures/variables are defined once in an object and all other objects reference this memory location.
To achieve this, you can follow two different paths in TwinCAT:
- Linking TcCOM instances in TwinCAT with data pointers
- Use of global variables (Exported Global/Imported External)
In the remainder of this chapter, both paths will be described.
While with data pointers you are more flexible in linking the data, when using global variables you have the advantage that the structure/variable exists only once in memory. For data pointers, a local copy of the data is held in each TcCOM instance.
Notice | |
Data transmission not threadsafe Use data pointers and global variables with caution. The data exchange is not threadsafe. Therefore, we strongly recommend to run all involved TcCOM instances in the same task context. |
Linking TcCOM instances in TwinCAT with data pointers
The following describes how you can specifically use a shared memory area between TcCOM instances via data pointer.
As described in section Configuration of data access to data of a TcCOM object, you can make DataAreas accessible via DataPointer. Input Destination DataArea, Output Source DataArea and Standard DataArea are accessible via DataPointer. In principle, you can create any variable group, model parameter, DWork, BlockIO, etc. as a standard DataArea and thus make it accessible as Data Pointer.
Objective
In the following, we will show you how to make a specific subrange of variables accessible via DataPointer and not the entire variable group range at once. The basic concept is based on the use of data store memory blocks in Simulink®.
Model with DataStore
A Data Store Memory block is created in a model "DataStoreObject". In this sample with only one variable of type double. This variable is read with a Data Store Read, multiplied by an input and set to an output.
At TC TcCom Interface, the option DataStore: Data Access is changed from None to Standard DataArea and the Simulink® model is compiled into a TcCOM. If the object is instantiated in TwinCAT, the following representation is obtained:
The DataStore DataArea is now displayed in the process image and contains a variable of type LREAL, which can be accessed via DataPointer.
Model with DataPointer
In a second Simulink® model, a double value is generated by a clock and this is set to the TC Module Output.
In the configuration of the TC Module Output, the connection type is set to DataPointer and the data type is set to double.
If this model is compiled into a TcCOM object, the following representation is obtained in TwinCAT:
The name of the TC Module Output "PtrToMyDataStore" of type LREAL is now displayed under Data Pointer. This can now be linked by double-clicking on the DataStoreObject_DW_MyDataStore.
Both TcCOM instances should be called in the same task because the communication is not threadsafe.
If different tasks are used, the user must ensure that data is consistent and is read or written at appropriate times. |
The behavior of the sample here is shown in the graphic below.
If the input In1 of the DataStore model is manually set to 1 and the output Out1 is observed via the TwinCAT Scope, an increasing straight line is seen. This means that the increasing value from the clock is written to the DataStore of the DataStoreObject-TcCOM via pointer.
Read access via TC Module Input Read access to a DataArea can be realized via DataPointer using the TC Module Input. |
Local data copy in each module The Simulink CoderTM generates the C/C++ code in such a way that local variables are created for the TC Module Input and TC Module Output and thus each module instance contains a local copy of the data. Accordingly, the memory requirements of the project increase with each instance. |
Use of global variables (Exported Global/Imported External)
The following describes how to create a global variable in TcCOM using the Storage Classes in Simulink® and reference it in other TcCOM.
Objective
A Simulink® parameter GlobalParams is created, which contains a structure of 3 elements. The goal is to define this structure via a TcCOM object and to instantiate further TcCOM objects that access this structure via shared memory area, so that changes in the defining TcCOM arrive directly in all connected TcCOM instances.
Modeling in Simulink®
Two Simulink® models are created:
- "DataDefiner" model
- "DataUser" model
Furthermore, a Simulink® bus is defined and created as a Simulink® parameter named GlobalParam
. The DataDefiner model determines the content of GlobalParam at the end, while the DataUser models only read the content.
Note that the Storage Class definition of GlobalParam is set differently for the DataDefiner and DataUser below. For the DataDefiner, the Storage Class is set to Exported Global, while for the DataDefiner, the Storage Class is set to Imported Extern.
The following script bundles the two described Simulink® models into one driver, cf. Bundling of several models in one TwinCAT driver.
thisDir = fileparts(mfilename("fullpath"));
% model names
mdlNames = ["DataDefiner","DataUser"];
codeDirectories = fullfile(thisDir,strcat(mdlNames,'_tcgrt'));
isParamDefiner = [true,false];
% instantiate project configuration
projCfg = TwinCAT.ModuleGenerator.ProjectExportConfig('FullPath',fullfile(thisDir,'TcCppProj','DataSharingModules.vcxproj'));
regenerate = false;
for i=1:length(mdlNames)
% generate code for the models (only if the code directory doesn't exist) -> remove the directory to rebuild specific models
if regenerate || ~isfolder(codeDirectories(i))
% load the model and apply basic settings
mdl = load_system(mdlNames(i));
set_param(mdl,'SystemTargetFile','TwinCatGrt.tlc');
set_param(mdl,'CodeInterfacePackaging','C++ class');
set_param(mdl,'TcCom_TcComWrapperFb','off');
set_param(mdl,'TcProject_Generate','off');
set_param(mdl,'SolverType','Fixed-step');
% adapt the storage class of the shared parameter structure
if isParamDefiner(i)
GlobalParam.CoderInfo.StorageClass = "ExportedGlobal";
else
GlobalParam.CoderInfo.StorageClass = "ImportedExtern";
end
% generate code and close the model
slbuild(mdl);
close_system(mdl,0);
end
% add the class export configuration to the "global" project export configuration
mdlProjCfg = TwinCAT.ModuleGenerator.ProjectExportConfig.Load(codeDirectories(i));
clsCfg = mdlProjCfg.ClassExportCfg{1};
projCfg.AddClassExportConfig(clsCfg)
end
% generate and build the project
TwinCAT.ModuleGenerator.ProjectExporter(projCfg);
Configuration in TwinCAT XAE
- Create one instance of the DataDefiner in the TwinCAT XAE (more are not allowed!). On the other hand, you can create as many DataUser instances as you like.
- 1. Set Parameter (Init) > Parameter GlobalParam_sharing for the DataDefiner to "Define".
- 2. Set Parameter (Init) > Parameter GlobalParam_sharing for all DataUsers to "Inherit".
- 3. Create a task.
- 4. Assign this task to all created instances.
- 5. Activate the project.
- Change the values of GlobalParam in the DataDefiner, e.g. via the Block Diagram, and observe the direct effect on the DataUser instances.