Use Simulink.Parameter Type of Variant Control Variables for Code Generation in Variant Blocks
If you intend to generate code for a model containing variant blocks, specify variant control variables as
objects. The Simulink.Parameter
Simulink.Parameter
objects allow you to specify other attributes, such as data type and storage class, and control the appearance and placement of variant control variables in generated code.
You can define a variant control variable of type Simulink.Parameter
only in the base workspace or in a data dictionary. Defining Simulink.Parameter
type of variant control variables in a mask or model workspace is not supported. For more information on storage locations for variant control variables, see Storage Locations for Variant Control Variables (Operands) in Variant Blocks.
Simulink.Parameter
objects within structures and that have data types other than
objects are not supported.Simulink.Bus
Prerequisites
To learn more about how to use Variant Subsystems in Simulink®, Implement Variations in Separate Hierarchy Using Variant Subsystems example.
Explore the Model
1. Open the slexVariantSubsystems
model.
model = "slexVariantSubsystems"
open_system(model);
model = "slexVariantSubsystems"
2.In the MATLAB® Editor, define a Simulink.Parameter
object, V
and set its value to 1
. The parameter object V
uses the custom storage class ExportedGlobal
to define V
in an external file. You can further control the appearance and placement of V
and prevent optimizations from eliminating storage for V
using the storage class property. For more information, see Storage Classes for Different Variant Activation Times.
V = Simulink.Parameter; V.Value = 1; V.DataType = "int32"; V.CoderInfo.StorageClass = "ExportedGlobal";
Note: Variant control variables defined as Simulink.Parameter
objects can have any of the storage classes listed in Storage Classes for Different Variant Activation Times. You can also convert a scalar variant control variable into a Simulink.Parameter
object. For more information, see Convert Variant Control Variables into Simulink.Parameter Objects.
3. Specify the Simulink.Parameter
object as the variant control variable in the Block Parameters dialog box of the Controller
block. Also, change the Variant activation time to code compile
.
linearBlkPath = "slexVariantSubsystems/Controller/Linear Controller"; nonLinearBlkPath = "slexVariantSubsystems/Controller/Nonlinear Controller"; set_param(linearBlkPath,"VariantControl","V == 1"); set_param(nonLinearBlkPath,"VariantControl","V == 2"); set_param(model+"/Controller","VariantActivationTime","startup");
Ensure that the Linear Controller
and NonLinear Controller
blocks are atomic.
set_param(linearBlkPath,"TreatAsAtomicUnit","on"); set_param(nonLinearBlkPath,"TreatAsAtomicUnit","on");
4. During simulation, the Linear Controller
block becomes active. Double-click the Controller
block to see the active choice.
sim(model);
Similarly, if you change the value of V
to 2
, the Nonlinear Controller
block becomes active during simulation.
V.Value = 2; sim(model);
Read Value of Variant Control Variable from Custom Header File
1. Specify the system target file as grt.tlc
.
set_param(model,"SystemTargetFile","grt.tlc");
2. In the Apps tab of the toolstrip, navigate to Simulink Coder.
3. In the C code tab, select Build > Generate code. Alternatively, enter this command in the Command Window.
slbuild(model);
### Starting build procedure for: slexVariantSubsystems ### Successful completion of build procedure for: slexVariantSubsystems Build Summary Top model targets: Model Build Reason Status Build Duration ======================================================================================================================== slexVariantSubsystems Information cache folder or artifacts were missing. Code generated and compiled. 0h 0m 10.864s 1 of 1 models built (0 models already up to date) Build duration: 0h 0m 11.573s
As the storage class for variable V
is configured as ExportedGlobal
, the code generator creates global definitions and declarations for V
in the generated code. You can then access the value of V
by using #include
in your external code. This value of V
determines which of the specified variants must be activated in the embedded executable. However, the value of V
specified in the Simulink.Parameter
object is still used during simulation, while the value specified in the external code is used for compiling the generated code and activating the desired variant in the embedded executable.
The generated code contains both Linear and Nonlinear choices in if
and else if
conditions because of the startup
activation time.
cfile=fullfile(pwd, 'slexVariantSubsystems_grt_rtw', 'slexVariantSubsystems.c'); coder.example.extractLines(cfile, '/* Model step', '/* Model initialize', 1, 0);
/* Model step function */ void slexVariantSubsystems_step(void) { real_T denAccum; real_T rtb_VariantMergeForOutportOut1; real_T rtb_sine1; real_T rtb_sine3; real_T sine2; /* Sin: '<Root>/sine1' */ if (slexVariantSubsystems_DW.systemEnable != 0) { rtb_sine1 = slexVariantSubsystems_M->Timing.taskTime0; slexVariantSubsystems_DW.lastSin = sin(1.0471975511965976 * rtb_sine1); slexVariantSubsystems_DW.lastCos = cos(1.0471975511965976 * rtb_sine1); slexVariantSubsystems_DW.systemEnable = 0; } rtb_sine1 = ((slexVariantSubsystems_DW.lastSin * 0.99994516936551214 + slexVariantSubsystems_DW.lastCos * -0.010471784116245792) * 0.99994516936551214 + (slexVariantSubsystems_DW.lastCos * 0.99994516936551214 - slexVariantSubsystems_DW.lastSin * -0.010471784116245792) * 0.010471784116245792) * 0.1; /* End of Sin: '<Root>/sine1' */ /* Sin: '<Root>/sine2' */ if (slexVariantSubsystems_DW.systemEnable_e != 0) { sine2 = slexVariantSubsystems_M->Timing.taskTime0; slexVariantSubsystems_DW.lastSin_g = sin(sine2); slexVariantSubsystems_DW.lastCos_n = cos(sine2); slexVariantSubsystems_DW.systemEnable_e = 0; } /* Sin: '<Root>/sine2' */ sine2 = ((slexVariantSubsystems_DW.lastSin_g * 0.99995000041666526 + slexVariantSubsystems_DW.lastCos_n * -0.0099998333341666645) * 0.99995000041666526 + (slexVariantSubsystems_DW.lastCos_n * 0.99995000041666526 - slexVariantSubsystems_DW.lastSin_g * -0.0099998333341666645) * 0.0099998333341666645) * 2.0; /* Sin: '<Root>/sine3' */ if (slexVariantSubsystems_DW.systemEnable_b != 0) { rtb_sine3 = slexVariantSubsystems_M->Timing.taskTime0; slexVariantSubsystems_DW.lastSin_i = sin(0.52359877559829882 * rtb_sine3); slexVariantSubsystems_DW.lastCos_i = cos(0.52359877559829882 * rtb_sine3); slexVariantSubsystems_DW.systemEnable_b = 0; } rtb_sine3 = ((slexVariantSubsystems_DW.lastSin_i * 0.99998629224742674 + slexVariantSubsystems_DW.lastCos_i * -0.00523596383141958) * 0.99998629224742674 + (slexVariantSubsystems_DW.lastCos_i * 0.99998629224742674 - slexVariantSubsystems_DW.lastSin_i * -0.00523596383141958) * 0.00523596383141958) * 0.3; /* End of Sin: '<Root>/sine3' */ /* Outputs for Atomic SubSystem: '<Root>/Controller' */ if (V == 1) { /* Outputs for Atomic SubSystem: '<S1>/Linear Controller' */ /* Update for DiscreteTransferFcn: '<S2>/Discrete Transfer Fcn' */ denAccum = (sine2 - 0.09 * slexVariantSubsystems_DW.DiscreteTransferFcn_states[0]) - 0.5 * slexVariantSubsystems_DW.DiscreteTransferFcn_states[1]; /* Sum: '<S2>/Add' incorporates: * DiscreteTransferFcn: '<S2>/Discrete Transfer Fcn' */ rtb_VariantMergeForOutportOut1 = ((0.7 * slexVariantSubsystems_DW.DiscreteTransferFcn_states[1] + slexVariantSubsystems_DW.DiscreteTransferFcn_states[0]) + rtb_sine1) + rtb_sine3; /* Update for DiscreteTransferFcn: '<S2>/Discrete Transfer Fcn' */ slexVariantSubsystems_DW.DiscreteTransferFcn_states[1] = slexVariantSubsystems_DW.DiscreteTransferFcn_states[0]; slexVariantSubsystems_DW.DiscreteTransferFcn_states[0] = denAccum; /* End of Outputs for SubSystem: '<S1>/Linear Controller' */ /* End of Outputs for SubSystem: '<Root>/Controller' */ } else if (V == 2) { /* Outputs for Atomic SubSystem: '<S1>/Nonlinear Controller' */ /* Sum: '<S3>/Add' incorporates: * Lookup_n-D: '<S3>/1-D Lookup Table' * Sin: '<Root>/sine2' */ rtb_VariantMergeForOutportOut1 = (rtb_sine1 + look1_binlxpw(sine2, slexVariantSubsystems_ConstP.uDLookupTable_bp01Data, slexVariantSubsystems_ConstP.uDLookupTable_tableData, 10U)) + rtb_sine3; /* End of Outputs for SubSystem: '<S1>/Nonlinear Controller' */ } /* Outport: '<Root>/Out1' */ slexVariantSubsystems_Y.Out1 = rtb_VariantMergeForOutportOut1; /* Update for Sin: '<Root>/sine1' */ rtb_VariantMergeForOutportOut1 = slexVariantSubsystems_DW.lastSin; slexVariantSubsystems_DW.lastSin = slexVariantSubsystems_DW.lastSin * 0.99994516936551214 + slexVariantSubsystems_DW.lastCos * 0.010471784116245792; slexVariantSubsystems_DW.lastCos = slexVariantSubsystems_DW.lastCos * 0.99994516936551214 - rtb_VariantMergeForOutportOut1 * 0.010471784116245792; /* Update for Sin: '<Root>/sine2' */ rtb_VariantMergeForOutportOut1 = slexVariantSubsystems_DW.lastSin_g; slexVariantSubsystems_DW.lastSin_g = slexVariantSubsystems_DW.lastSin_g * 0.99995000041666526 + slexVariantSubsystems_DW.lastCos_n * 0.0099998333341666645; slexVariantSubsystems_DW.lastCos_n = slexVariantSubsystems_DW.lastCos_n * 0.99995000041666526 - rtb_VariantMergeForOutportOut1 * 0.0099998333341666645; /* Update for Sin: '<Root>/sine3' */ rtb_VariantMergeForOutportOut1 = slexVariantSubsystems_DW.lastSin_i; slexVariantSubsystems_DW.lastSin_i = slexVariantSubsystems_DW.lastSin_i * 0.99998629224742674 + slexVariantSubsystems_DW.lastCos_i * 0.00523596383141958; slexVariantSubsystems_DW.lastCos_i = slexVariantSubsystems_DW.lastCos_i * 0.99998629224742674 - rtb_VariantMergeForOutportOut1 * 0.00523596383141958; /* Matfile logging */ rt_UpdateTXYLogVars(slexVariantSubsystems_M->rtwLogInfo, (&slexVariantSubsystems_M->Timing.taskTime0)); /* signal main to stop simulation */ { /* Sample time: [0.01s, 0.0s] */ if ((rtmGetTFinal(slexVariantSubsystems_M)!=-1) && !((rtmGetTFinal(slexVariantSubsystems_M)- slexVariantSubsystems_M->Timing.taskTime0) > slexVariantSubsystems_M->Timing.taskTime0 * (DBL_EPSILON))) { rtmSetErrorStatus(slexVariantSubsystems_M, "Simulation finished"); } } /* Update absolute time for base rate */ /* The "clockTick0" counts the number of times the code of this task has * been executed. The absolute time is the multiplication of "clockTick0" * and "Timing.stepSize0". Size of "clockTick0" ensures timer will not * overflow during the application lifespan selected. * Timer of this task consists of two 32 bit unsigned integers. * The two integers represent the low bits Timing.clockTick0 and the high bits * Timing.clockTickH0. When the low bit overflows to 0, the high bits increment. */ if (!(++slexVariantSubsystems_M->Timing.clockTick0)) { ++slexVariantSubsystems_M->Timing.clockTickH0; } slexVariantSubsystems_M->Timing.taskTime0 = slexVariantSubsystems_M->Timing.clockTick0 * slexVariantSubsystems_M->Timing.stepSize0 + slexVariantSubsystems_M->Timing.clockTickH0 * slexVariantSubsystems_M->Timing.stepSize0 * 4294967296.0; }
If you are using Embedded Coder® with an ERT-based system target file, for example, ert.tlc
, and you want to generate a code that guards the variant choices in preprocessor conditionals #if
and #elif
, specify the Variant activation time parameter of the variant blocks to code compile
. These preprocessor conditionals enable you to conditionally compile variant choices as described in Compile Code Conditionally for Variations of Component Represented Using Variant Block.