Specify signals, states, and parameters for inclusion in generated code.
Learn how to control these attributes of data in the generated code:
Name
Data type
Data storage class
For information about the example model, see Prepare a Control Algorithm Model for C Code Generation.
Open the example model, rtwdemo_PCG_Eval_P2
.
open_system('rtwdemo_PCG_Eval_P2')
Most programming languages require that you declare data and functions before using them. The declaration specifies:
Scope: The region of the program that has access to the data
Duration: The period during which the data exist in memory
Data type: The amount of memory allocated for the data
Initialization: A value, a pointer to memory, or NULL
The combination of scope and duration is the storage class. If you do not provide an initial value, most compilers assign a zero value or a null pointer.
Supported data types include:
double
: Double-precision floating point
single
: Single-precision floating point
int8
: Signed 8-bit integer
uint8
: Unsigned 8-bit integer
int16
: Signed 16-bit integer
uint16
: Unsigned 16-bit integer
int32
: Signed 32-bit integer
uint32
: Unsigned 32-bit integer
Fixed Point: 8-, 16-, 32-bit word lengths
With storage classes you can:
Generate exported files, with custom names, that contain global variable declarations and definitions.
Import custom header files that contain global variable declarations.
Generate const
or volatile
type qualifiers in declarations.
Represent a parameter as a macro (#define
or compiler option).
Package signals or parameters into flat structures or bit fields.
This example uses data objects to specify code generation settings for data. Alternatively, you can store the settings in the model by using dialog boxes. Both methods allow full control over the data type and storage class. You can use both methods in a single model.
This example focuses on these types of data objects:
Signal
Parameter
Bus
The code generator uses the objects from the MATLAB base workspace or a Simulink data dictionary. You can create and inspect the objects with commands at the command prompt or by using the Model Data Editor or the Model Explorer with the Code Mappings Editor.
As an example, inspect the definition of the Simulink.Signal
object pos_cmd_one
, which the model created in the base workspace:
Open the Embedded Coder app.
On the Modeling tab, click Model Data Editor.
In the Model Data Editor, inspect the Signals tab.
Click the Show/refresh additional information button.
Next to the Filter contents box, toggle the Filter using selection button.
In the model, select the pos_cmd_one
signal line. Now, the data table contains two rows: One that corresponds to the signal line in the model and one that corresponds to the signal object in the base workspace. For the row that corresponds to the signal line, the Resolve check box is selected, which means the signal line acquires settings from the signal object.
Optionally, to inspect other properties of the signal object, adjust the setting for the Change view drop-down list.
In the model, select the pos_cmd_one
signal line and pause on the ellipsis that appears above or below the signal line to open the action bar. Click the Add Signal button.
Open the Code Mappings Editor, click the Signals tab. Inspect the pos_cmd_one
signal storage class.
Return to the Model Data Editor, toggle the Filter using selection button. Now, the data table shows these signal lines and signal objects:
pos_cmd_one
pos_rqst
P_InErrMap
ThrotComm
ThrotComm
is a bus signal, which is an instantiation of the Simulink.Bus
object ThrottleCommands
. If a bus signal is nonvirtual, the signal appears as a structure in the C code. Bus objects do not appear in the Model Data Editor. However, you can inspect the object in the Model Explorer (View > Model Explorer).
As in C, you can use a bus definition (ThrottleCommands
) to create multiple instances of the structure. In a model diagram, a bus signal appears as a wide line with central dashes.
A data object has properties that you configure for simulation and for code generation, including:
DataType
(numeric data type for storage in the generated code)
StorageClass
(storage class for code generation)
Value
(parameter value)
InitialValue
(initial value for signal)
Alias
(alternative name for the data, which the code generator uses)
Dimensions
(size and number of dimensions of parameter or signal value)
Complexity
(numeric complexity)
Unit
(physical units such as cm)
Min
(minimum value)
Max
(maximum value)
Use the property Description
to specify custom documentation for data objects.
You can create data objects for named signals, states, and parameters. To associate a data object with a construct, the construct must have a name.
The Data Object Wizard tool finds constructs for which you can create data objects, and then creates the objects for you. The example model includes two signals that are not associated with data objects: fbk_1
and pos_cmd_two
.
To find the signals and create data objects for them:
1. Open the Data Object Wizard.
dataobjectwizard('rtwdemo_PCG_Eval_P2')
2. Click Find to find candidate constructs.
3. Click Select All to select all candidates.
4. Click Create to create the data objects.
The Data Object Wizard performs the equivalent of the following commands:
fbk_1 = Simulink.Signal; fbk_1.Dimensions = 1; fbk_1.DataType = 'double'; outport = get_param('rtwdemo_PCG_Eval_P2/fbk_1','PortHandles'); outport = outport.Outport; set_param(outport,'MustResolveToSignalObject','on') pos_cmd_two = Simulink.Signal; pos_cmd_two.Dimensions = 1; pos_cmd_two.DataType = 'double'; outport = get_param('rtwdemo_PCG_Eval_P2/PI_ctrl_2','PortHandles'); outport = outport.Outport; set_param(outport,'MustResolveToSignalObject','on')
Set the data type and storage class for each data object. To configure data object properties, inspect the appropriate tab in the Model Data Editor. Use the Change view drop-down list to show design properties (such as data type) and code generation settings (such as storage class).
In the Model Data Editor, inspect the Signals tab and click the Show/refresh additional information button to display information for the signal objects that the Data Object Wizard created. Then, configure these properties for the objects:
fbk_1
: Data type double
, storage class ImportedExtern
pos_cmd_two
: Data type double
, storage class ExportedGlobal
Alternatively, you can use these commands at the command prompt to configure the objects:
fbk_1.DataType = 'double'; fbk_1.StorageClass = 'ImportedExtern'; pos_cmd_two.DataType = 'double'; pos_cmd_two.StorageClass = 'ExportedGlobal';
With Embedded Coder®, you can control the file placement of definitions for parameters and constants. The example model writes the parameter definitions to the file eval_data.c
.
To change the placement of parameter and constant definitions, set the data placement options for the model configuration. In the Configuration Parameters dialog box, configure the options on the Code Generation > Code Placement pane.
In the example model, inspect the Code Placement pane in the Configuration Parameters dialog box. The model places data definitions in the file eval_data.c
and declarations in the file eval_data.h
.
You can control the default tunability of block parameters in the generated code by setting model configuration parameter Default parameter behavior.
For the example model, the default parameter behavior is set to Inlined
. By default, block parameters appear in the code as numeric literal values, not as variables stored in memory. You can use Simulink.Parameter
objects to override inlining and preserve tunability for individual parameters.
Make sure that the signal data objects (Simulink.Signal
) appear in the generated code. For individual signal lines in the model, select the option Signal name must resolve to Simulink signal object to explicitly resolve the signal name to a Simulink.Signal
object in a workspace or data dictionary. In the Model Data Editor, set Change view to Code
and select the check box in the Resolve column.
Alternatively, you can select the option for all signals in a model. At the command prompt, use the disableimplicitsignalresolution
function.
Generate code from the example model.
rtwbuild('rtwdemo_PCG_Eval_P2');
### Starting build procedure for: rtwdemo_PCG_Eval_P2 ### Successful completion of code generation for: rtwdemo_PCG_Eval_P2 Build Summary Top model targets built: Model Action Rebuild Reason ======================================================================================= rtwdemo_PCG_Eval_P2 Code generated Code generation information file does not exist. 1 of 1 models built (0 models already up to date) Build duration: 0h 0m 24.181s
Create a code generation report to more easily view the generated files. In the Simulink Editor, select model configuration parameter Open Model Report.
You can access these generated files:
rtwdemo_PCG_Eval_P2.c
: Defines the step and initialization functions. Uses the defined data objects.
eval_data.c
: Assigns initial values to the defined parameters. The model configuration specifically sets the file name.
eval_data.h
: Provides extern
declarations of parameter data. The model configuration specifically sets the file name.
ert_main.c
: Defines scheduling functions.
rtwdemo_PCG_Eval_P2.h
: Contains the type definitions of the default structures that store signal, state, and parameter data. Due to the data object settings, some data appear in eval_data.c
instead.
PCG_Eval_p2_private.h
: Declares private (local) data for the generated functions.
rtwdemo_PCG_Eval_P2_types.h
: Declares the real-time model data structure.
rtwtypes.h
: Provides mapping to the data types that Simulink® Coder™ defines (typedef
). Used for integration with external systems.
For example, view the file eval_data.c
, which allocates const
memory for global variables that correspond to the Simulink.Parameter
objects in the base workspace.
cfile = fullfile('rtwdemo_PCG_Eval_P2_ert_rtw','eval_data.c'); rtwdemodbtype(cfile,'/* Exported data definition */','* [EOF]',1,1)
/* Exported data definition */ /* Const memory section */ /* Definition for custom storage class: Const */ const real_T I_Gain = -0.03; const real_T I_InErrMap[9] = { -1.0, -0.5, -0.25, -0.05, 0.0, 0.05, 0.25, 0.5, 1.0 } ; const real_T I_OutMap[9] = { 1.0, 0.75, 0.6, 0.0, 0.0, 0.0, 0.6, 0.75, 1.0 } ; const real_T P_Gain = 0.74; const real_T P_InErrMap[7] = { -1.0, -0.25, -0.01, 0.0, 0.01, 0.25, 1.0 } ; const real_T P_OutMap[7] = { 1.0, 0.25, 0.0, 0.0, 0.0, 0.25, 1.0 } ; /* * File trailer for generated code. * * [EOF]
View the code algorithm in the model step
function in the file rtwdemo_PCG_Eval_P2.c
. The algorithm uses the data object names directly.
cfile = fullfile('rtwdemo_PCG_Eval_P2_ert_rtw','rtwdemo_PCG_Eval_P2.c'); rtwdemodbtype(cfile,'/* Model step function */',... '/* Sum: ''<S2>/Sum3'' incorporates:',1,0);
/* Model step function */ void rtwdemo_PCG_Eval_P2_step(void) { real_T Discrete_Time_Integrator1; real_T Discrete_Time_Integrator1_i; real_T rtb_IntegralGainShape; /* Sum: '<S2>/Sum2' incorporates: * Inport: '<Root>/fbk_1' * Inport: '<Root>/pos_rqst' */ rtb_IntegralGainShape = *pos_rqst - fbk_1; /* DiscreteIntegrator: '<S2>/Discrete_Time_Integrator1' */ if (rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_SYSTE != 0) { /* DiscreteIntegrator: '<S2>/Discrete_Time_Integrator1' */ Discrete_Time_Integrator1 = rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_DSTAT; } else { /* DiscreteIntegrator: '<S2>/Discrete_Time_Integrator1' incorporates: * Gain: '<S2>/Int Gain1' * Lookup_n-D: '<S2>/Integral Gain Shape' * Lookup_n-D: '<S3>/Integral Gain Shape' * Product: '<S2>/Product3' */ Discrete_Time_Integrator1 = I_Gain * look1_binlx(rtb_IntegralGainShape, (&(I_InErrMap[0])), (&(I_OutMap[0])), 8U) * rtb_IntegralGainShape * 0.001 + rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_DSTAT; } /* End of DiscreteIntegrator: '<S2>/Discrete_Time_Integrator1' */
Without storage classes and other code generation settings that the data objects specify, the generated code:
Inlines invariant signals and block parameters when you select these optimizations in the model configuration parameters
Places model input and output signals, block states, and tunable parameters in global data structures
Does not create global variables that correspond to MATLAB® variables
In contrast, the example code shows that most of the default data structures have been replaced with user-defined data objects. For example, these signals and parameters appear as global variables in the code:
pos_rqst
fbk_1
I_Gain
I_OutMap
I_InErrMap
However, the local variable rtb_IntegralGainShape
and the state variable rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_DSTAT
still use code generator data structures. Data objects do not exist for these entities.
When you end your MATLAB session, variables that you create in the base workspace do not persist. To permanently store data objects and bus objects, consider linking the model to a data dictionary.
In the example model, on the Modeling tab, under Design, click Link to Data Dictionary.
In the Model Properties dialog box, click New.
In the Create a New Data Dictionary dialog box, set File name to rtwdemo_PCG_Eval_dict
and click Save.
In the Model Properties dialog box, click Apply.
Click Migrate data.
Click Migrate in response to message about copying referenced variables.
Click OK.
Alternatively, to manually migrate the objects into a data dictionary, you can use programmatic commands:
% Create a list of the variables and objects that the target % model uses. usedVars = {Simulink.findVars('rtwdemo_PCG_Eval_P2').Name}; % Create a new data dictionary in your current folder. Link the model to % this new dictionary. dictObj = Simulink.data.dictionary.create('rtwdemo_PCG_Eval_dict.sldd'); set_param('rtwdemo_PCG_Eval_P2','DataDictionary','rtwdemo_PCG_Eval_dict.sldd') % Import only the target variables from the base workspace to the data % dictionary. importFromBaseWorkspace(dictObj,'clearWorkspaceVars',true,'varList',usedVars); saveChanges(dictObj);
The data dictionary permanently stores the objects that the model uses. To view the contents of the dictionary, click the model data badge in the lower-left corner of the model, then click the Data Dictionary link. In the Model Explorer Model Hierarchy pane, select the Design Data node.
The separation of data from the model provides these benefits:
One model, multiple data sets
Use of different data types to change the targeted hardware (for example, for floating-point and fixed-point targets)
Use of different parameter values to change the behavior of the control algorithm (for example, for reusable components with different calibration values)
Multiple models, one data set
Sharing of data between Simulink® models in a system
Sharing of data between projects (for example, transmission, engine, and wheel controllers might all use the same CAN message data set)