Documentation

Configure Data Interface in the Generated Code

This example shows how to specify signals and parameters for inclusion in generated code.

Time: 45 minutes

Goals

Learn how to control the following attributes of signals and parameters in the generated code:

  • Name

  • Data type

  • Data storage class

Open Example Model

Open the example model rtwdemo_PCG_Eval_P2.

open_system('rtwdemo_PCG_Eval_P2')

Background: Declaration of Data

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 is resident 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

Supported storage classes and custom storage classes allow you to:

  • 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.

Control Data in Simulink® and Stateflow®

Two methods are available for declaring data in Simulink® and Stateflow®: data objects and direct specification. This example uses the data object method. Both methods allow full control over the data type and storage class. You can mix the two methods in a single model.

You can use data objects in a variety of ways in the MATLAB® and Simulink® environment. The example focuses on three types of 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 them by entering commands in the MATLAB Command Window or by using the Model Explorer.

The following example shows the definition of Simulink® signal object pos_cmd_one, which the model created in the base workspace:

pos_cmd_one
pos_cmd_one = 

  <a href="matlab:helpPopup Simulink.Signal" style="font-weight:bold">Signal</a> with properties:

         CoderInfo: [1x1 Simulink.CoderInfo]
       Description: 'Throttle position command from the first PI controller'
          DataType: 'double'
               Min: -1
               Max: 1
              Unit: ''
        Dimensions: -1
    DimensionsMode: 'auto'
        Complexity: 'auto'
        SampleTime: -1
      InitialValue: '0'


You can open the Model Explorer and display details about a specific data object in the example model.

Open the Model Explorer.

daexplr

In the Model Hierarchy pane, select Base Workspace. In the Contents pane, inspect the definitions of these objects:

  • pos_cmd_one - Top-level output, Double, Exported Global

  • pos_rqst - Top-level input, Double, Imported Extern Pointer

  • P_InErrMap - Calibration parameter, Auto, Constant

  • ThrotComm (1) - Top-level output structure, Auto, Exported Global

  • ThrottleCommands (1) - Bus definition, Struct, None

(1) ThrottleCommands defines a Simulink.Bus object, while ThrotComm is the instantiation of the bus. If the instantiation is a nonvirtual bus, the signal will generate a structure in the C code.

As in C, you can use a bus definition (ThrottleCommands) to instantiate multiple instances of the structure. In a model diagram, a bus signal appears as a wide line with central dashes, as shown below.

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 (define a different name in the generated code)

  • 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 document your data objects. Simulink® does not use this property.

Add New 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 is a tool that finds constructs for which you can create data objects, 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.

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')

Configure Data Objects

The next step is to set the data type and storage class for each data object. To configure data object properties, in the Model Explorer Contents pane, click an object. You can edit the properties by using the corresponding columns in the Contents pane, or edit the properties in the Dialog pane.

Use the Model Explorer to configure these data object properties:

  • fbk_1: Data type double, storage class ImportedExtern

  • pos_cmd_two: Data type double, storage class ExportedGlobal

Alternatively, you can use these commands to configure the objects:

fbk_1.DataType = 'double';
fbk_1.StorageClass = 'ImportedExtern';

pos_cmd_two.DataType = 'double';
pos_cmd_two.StorageClass = 'ExportedGlobal';

Control File Placement of Parameter Data

Embedded Coder® allows you to control the files in which the parameters and constants are defined. For the example model, all parameter definitions were written to the file eval_data.c.

To change the placement of parameter and constant definitions, set the appropriate 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, view the Code Placement pane of the Configuration Parameters dialog box. The model is configured to place data definitions in the file eval_data.c and declarations in the file eval_data.h.

Declare Block Parameters Tunable or Inlined

You can control the default tunability of block parameters in the generated code. In the Configuration Parameters dialog box, on the Optimization > Signals and Parameters pane, adjust the setting for 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, and not as variables stored in memory. You can use Simulink.Parameter objects to override inlining and preserve tunability for individual parameters.

Enable Signal Data Objects in Generated Code

The next step is to ensure that the signal data objects you have created appear in the generated code. For individual signal lines in the model, set 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.

1. Right-click the signal line.

2. Select Properties. The Signal Properties dialog box appears.

3. Make sure the option Signal name must resolve to Simulink signal object is selected.

You can manually select this option for individual signal lines, or you can select the option for all such signals in a model at once by using disableimplicitsignalresolution at the command prompt:

disableimplicitsignalresolution('rtwdemo_PCG_Eval_P2')
The following signal labels will be forced to resolve to signal objects:
- error_reset  (used by: rtwdemo_PCG_Eval_P2/Define_Throt_Param/Constant4/1)
- max_diff  (used by: rtwdemo_PCG_Eval_P2/Define_Throt_Param/Constant3/1)
- fail_safe_pos  (used by: rtwdemo_PCG_Eval_P2/Define_Throt_Param/Constant/1)
 
The following states will be forced to resolve to signal objects:
... none found ...
 

View Data Objects in Generated Code

Generate code from the example model.

rtwbuild('rtwdemo_PCG_Eval_P2');
### Starting build procedure for model: rtwdemo_PCG_Eval_P2
### Successful completion of code generation for model: rtwdemo_PCG_Eval_P2

Create a code generation report to more easily view the generated files. In the Simulink Editor, select Code > C/C++ Code > Code Generation Report > Open Model Report.

The report allows you to access these generated files:

  • rtwdemo_PCG_Eval_P2.c - Provides step and initialization function. Uses the defined data objects.

  • eval_data.c - Assigns values to the defined parameters. Has the file name specifically defined.

  • eval_data.h - Provides extern definitions to the defined parameters. Has the file name specifically defined.

  • ert_main.c - Provides scheduling functions.

  • rtwdemo_PCG_Eval_P2.h - Defines data structures. Using data objects, shifted some parameters out of this file into user_data.h.

  • PCG_Eval_p2_private.h - Defines private (local) data for the generated functions. Objects now defined in eval_data were removed.

  • rtwdemo_PCG_Eval_P2_types.h - Defines the model data structure.

  • rtwtypes.h - Provides mapping to data types defined by Simulink® Coder™. 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'' */',1,0);
/* Model step function */
void rtwdemo_PCG_Eval_P2_step(void)
{
  real_T rtb_Sum3;
  real_T rtb_IntegralGainShape;
  real_T rtb_Product2;
  real_T Discrete_Time_Integrator1;

  /* Sum: '<S2>/Sum2' incorporates:
   *  Inport: '<Root>/fbk_1'
   *  Inport: '<Root>/pos_rqst'
   */
  rtb_IntegralGainShape = *pos_rqst - fbk_1;

  /* Lookup_n-D: '<S2>/Proportional  Gain Shape' */
  rtb_Sum3 = look1_binlx(rtb_IntegralGainShape, (&(P_InErrMap[0])), (&(P_OutMap
    [0])), 6U);

  /* Product: '<S2>/Product2' incorporates:
   *  Gain: '<S2>/Prop Gain1'
   */
  rtb_Product2 = P_Gain * rtb_Sum3 * rtb_IntegralGainShape;

  /* Lookup_n-D: '<S2>/Integral  Gain Shape' */
  rtb_Sum3 = look1_binlx(rtb_IntegralGainShape, (&(I_InErrMap[0])), (&(I_OutMap
    [0])), 8U);

  /* DiscreteIntegrator: '<S2>/Discrete_Time_Integrator1' incorporates:
   *  Gain: '<S2>/Int Gain1'
   *  Product: '<S2>/Product3'
   */
  Discrete_Time_Integrator1 = I_Gain * rtb_Sum3 * rtb_IntegralGainShape * 0.001
    + rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_DSTAT;

Without data objects, 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 Simulink® Coder™ 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 the Simulink® Coder™ data structures. You did not define data objects for these entities.

Store Model Data in Data Dictionary

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.

  1. In the example model, select File > Model Properties > Link to Data Dictionary.

  2. In the Model Properties dialog box, select Data Dictionary. Click New.

  3. 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 OK.

  4. Click Yes in response to the message about migrating base workspace data. Click Yes in response to the message about removing the imported items from the base workspace.

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);

The data dictionary now permanently stores the objects that the model uses. To view the contents of the dictionary, click the data dictionary badge in the lower-left corner of the model.

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)

Further Study Topics

Was this topic helpful?