Access Structured Data Through a Pointer That External Code Defines
This example shows how to generate code that uses global data that some handwritten code defines. In the handwritten code, a pointer variable points to one of three structure variables that contain parameter data. A handwritten function switches the pointer between the structures. The generated code accesses the parameter data by dereferencing the pointer variable.
Explore External Code
Open the example source file rtwdemo_importstruct_user.c
. The code defines a default data structure variable ReferenceStruct
as constant (const
) data and statically initializes each field. This structure represents the reference data set.
/* Constant default data structure (reference data set) */ const DataStruct_type ReferenceStruct = { 11, /* OFFSET */ 2 /* GAIN */ };
The code defines two other structure variables, WorkingStruct1
and WorkingStruct2
, as volatile (volatile
) data.
/* Volatile data structures (working data sets) */ volatile DataStruct_type WorkingStruct1; volatile DataStruct_type WorkingStruct2;
The code defines a function that can initialize:
An inactive working structure from the active working structure.
Both working structures from the reference structure.
/* Function to initialize inactive working structures from active structure */ void InitInactiveWorkingStructs(void) { if (StructPointer == &WorkingStruct1) { /* Copy values from WorkingStruct1 to WorkingStruct2 */ memcpy((void*)&WorkingStruct2, (void*)&WorkingStruct1, sizeof(ReferenceStruct)); } else if (StructPointer == &WorkingStruct2) { /* Copy values from WorkingStruct2 to WorkingStruct1 */ memcpy((void*)&WorkingStruct1, (void*)&WorkingStruct2, sizeof(ReferenceStruct)); } else { /* Initialize both working structures from ReferenceStruct */ memcpy((void*)&WorkingStruct1, &ReferenceStruct, sizeof(ReferenceStruct)); memcpy((void*)&WorkingStruct2, &ReferenceStruct, sizeof(ReferenceStruct)); } }
The code defines StructPointer
, which is a const volatile
pointer to a structure. The code initializes the pointer to the address of ReferenceStruct
.
/* Define structure pointer. Point to reference structure by default */ const volatile DataStruct_type *StructPointer = &ReferenceStruct;
Finally, the code defines a function that can dynamically set StructPointer
to point to either ReferenceStruct
, WorkingStruct1
, or WorkingStruct2
.
/* Function to switch between structures */ void SwitchStructPointer(Dataset_T Dataset) { switch (Dataset) { case Working1: StructPointer = &WorkingStruct1; break; case Working2: StructPointer = &WorkingStruct2; break; default: StructPointer = &ReferenceStruct; } }
The example header file rtwdemo_importstruct_user.h
defines the enumeration Dataset_T
and the structure type Datastruct_type
. The file includes (#include
) the built-in Simulink® Coder™ header file rtwtypes.h
, which defines (typedef
) Simulink Coder data types such as int16_T
.
#include "rtwtypes.h" typedef enum { Reference = 0, Working1, Working2 } Dataset_T; typedef struct DataStruct_tag { int16_T OFFSET; /* OFFSET */ int16_T GAIN; /* GAIN */ } DataStruct_type;
The file also declares the global variables and the functions.
Purpose of External Code
The code is designed so that the source code of a control algorithm (whether generated or handwritten) can read data from ReferenceStruct
, WorkingStruct1
, or WorkingStruct2
by dereferencing (->
) StructPointer
. The two working structures (WorkingStruct1
and WorkingStruct2
) are located in volatile memory and are intended to be changed during runtime by an external calibration tool. The calibration engineer does not change the active working structure. Instead, the engineer changes the parameter values in the inactive working structure and then activates it by switching working structures.
If necessary for safety or in preparation for shutting down the application, the calibration tool can point StructPointer
to ReferenceStruct
instead. ReferenceStruct
stores default parameter values that do not change during execution.
Explore Example Model
Open the example model, ImportStruct
.
The model creates variables and objects in the base workspace. The Constant block and the Gain block use the ECoderDemos.Parameter
objects GAIN
and OFFSET
to set the Constant value and Gain block parameters. ECoderDemos
is an example custom package that defines two classes, Parameter
and Signal
, and some custom storage classes.
In the Embedded Coder app, select Code Interface > Individual Element Code Mappings.
In the Code Mappings Editor, inspect the Parameters tab.
The Code Mappings Editor shows rows that correspond to OFFSET
and GAIN
, which set the parameter values. In the Storage Class column, OFFSET
and GAIN
use the custom storage class StructPointer
, which the ECoderDemos
package defines.
Open the Custom Storage Class Designer and inspect the custom storage classes in the ECoderDemos
package. At the command prompt, use this command:
cscdesigner('ECoderDemos')
This example package defines multiple custom storage classes, including StructPointer
. You cannot edit the definitions. However, you can create your own packages and custom storage classes later. For an example that shows how to create a package and a custom storage class, see Create and Apply Storage Class Defined in User-Defined Package.
Under Custom storage class definitions, click StructPointer
. The settings for this custom storage class enable the generated code to interact with the pointer variable, StructPointer
, from the external code. For example, the custom storage class uses these settings:
Data scope is set to
Imported
because the example external code defines (allocates memory for)StructPointer
. With this setting, the code generator avoids generating unnecessary, duplicate definitions for the data items, such as theECoderDemos.Parameter
objects, that use the custom storage class.Data access is set to
Pointer
because in the example external code,StructPointer
is a pointer.Memory section is set to
ConstVolatile
because the example external code definesStructPointer
as constant, volatile data (const volatile
).Type is set to
FlatStructure
because in the example external code,StructPointer
points to a structure. With this setting, the generated code treats each data item (ECoderDemos.Parameter
object) as a field of a flat structure whose variable name and type name you can specify.On the Structure Attributes tab, Struct name is set to
StructPointer
. For aFlatStructure
custom storage class, Struct name specifies the name of the structure variable in the generated code. In this example,StructPointer
is the name of the variable that the external code defines.Type name is set to
DataStruct_type
, which is the name of the structure type that the example external code defines.
In the model, in the Configuration Parameters dialog box, inspect the Code Generation > Custom Code pane.
On the Additional source code tab, select Initialize code. This parameter value specifies the code to include in the generated model initialize function. In this model, the configuration parameter is set so that the generated code calls the InitInactiveWorkingStructs
function. InitInactiveWorkingStructs
initializes the working structures with the values from ReferenceStruct
. The initialization code then sets the pointer to the first working structure.
On the Code information tab, select Source files. This configuration parameter identifies the example external code file rtwdemo_importstruct_user.c
for inclusion in the build process after code generation.
Generate and Inspect Code
Generate code from the model.
In the generated file ImportStruct.c
, the model initialization function calls InitInactiveWorkingStructs
. The algorithm in the model execution (step
) function dereferences the pointer variable StructPointer
.