Main Content

配置生成的代码中的数据接口

指定要包含在生成的代码中的信号、状态和参数。

了解如何在生成的代码中控制数据的下列属性:

  • 名称

  • 数据类型

  • 数据存储类

有关示例模型的信息,请参阅为 C 代码生成准备控制算法模型

打开示例模型

打开示例模型 rtwdemo_PCG_Eval_P2

open_system('rtwdemo_PCG_Eval_P2')

数据声明

大多数编程语言要求您在使用数据和函数之前先声明它们。在声明中指定:

  • 范围:程序可以访问的数据范围

  • 持续时间:数据在内存中存在的期间

  • 数据类型:为数据分配的内存量

  • 初始化:值、指向内存的指针或 NULL

范围和持续时间的组合即为存储类。如果不提供初始值,大多数编译器会赋予一个零值或一个空指针。

支持的数据类型包括:

  • double:双精度浮点

  • single:单精度浮点

  • int8:有符号 8 位整数

  • uint8:无符号 8 位整数

  • int16:有符号 16 位整数

  • uint16:无符号 16 位整数

  • int32:有符号 32 位整数

  • uint32:无符号 32 位整数

  • 定点:8 位、16 位、32 位字长

使用存储类,您可以:

  • 生成具有自定义名称的导出文件,其中包含全局变量声明和定义。

  • 导入自定义头文件,其中包含全局变量声明。

  • 在声明中生成 constvolatile 类型限定符。

  • 将参数表示为宏(#define 或编译器选项)。

  • 将信号或参数打包到平面结构体或位域中。

控制 Simulink 和 Stateflow 中的数据

此示例使用数据对象来指定数据的代码生成设置。您也可以使用对话框存储模型中的设置。这两种方法都可以完全控制数据类型和存储类。您可以在一个模型中同时使用这两种方法。

此示例重点介绍以下类型的数据对象:

  • 信号

  • 参数

  • 总线

代码生成器使用来自 MATLAB® 基础工作区或 Simulink® 数据字典中的对象。您可以在命令提示符下使用命令来创建和检查对象,也可以结合使用模型数据编辑器或模型资源管理器和代码映射编辑器来创建和检查对象。

例如,检查模型在基础工作区中创建的 Simulink.Signal 对象 pos_cmd_one 的定义:

  1. 打开 Embedded Coder

  2. 建模选项卡上,点击模型数据编辑器

  3. 在模型数据编辑器中,检查信号选项卡。

  4. 点击显示/刷新其他信息按钮。

  5. 过滤内容框旁边,切换基于所选项进行过滤按钮。

  6. 在模型中,选择 pos_cmd_one 信号线。现在,数据表包含两行:一行对应于模型中的信号线,另一行对应于基础工作区中的信号对象。对于与信号线对应的行,解析复选框处于选中状态,这意味着该信号线从信号对象中获取设置。

  7. (可选)要检查信号对象的其他属性,请调整更改视图下拉列表的设置。

  8. 在模型中,选择 pos_cmd_one 信号线,并悬停在信号线上方或下方出现的省略号上以打开操作栏。点击“添加信号”按钮。

  9. 打开代码映射编辑器,点击信号选项卡。检查 pos_cmd_one 信号存储类。

返回模型数据编辑器,切换基于所选项进行过滤。现在,数据表显示下列信号线和信号对象:

  • pos_cmd_one

  • pos_rqst

  • P_InErrMap

  • ThrotComm

ThrotComm 是总线信号,它是 Simulink.Bus 对象 ThrottleCommands 的实例化。如果总线信号是非虚拟信号,则该信号在 C 代码中显示为结构体。总线对象不会出现在模型数据编辑器中。但是,您可以在模型资源管理器 (视图 > 模型资源管理器) 中检查对象。

与在 C 中一样,您可以使用总线定义 (ThrottleCommands) 创建结构体的多个实例。在模型图中,总线信号显示为具有中心短横线的宽线。

数据对象具有您可以针对仿真和代码生成进行配置的属性,包括:

  • DataType(生成的代码中的存储的数值数据类型)

  • StorageClass(代码生成的存储类)

  • Value(参数值)

  • InitialValue(信号的初始值)

  • Alias(代码生成器使用的数据的替代名称)

  • Dimensions(参数或信号值的维度大小和维度数)

  • Complexity(数值复/实性)

  • Unit(物理单位,如 cm)

  • Min(最小值)

  • Max(最大值)

使用属性 Description 为数据对象指定自定义文档。

添加新数据对象

您可以为指定的信号、状态和参数创建数据对象。要将数据对象与构造相关联,构造必须有名称。

借助数据对象向导工具找到您可为其创建数据对象的构造,然后为您创建这些对象。本示例模型包含两个未与数据对象关联的信号:fbk_1pos_cmd_two

要找到信号并为它们创建数据对象,请执行下列操作:

1.打开数据对象向导。

dataobjectwizard('rtwdemo_PCG_Eval_P2')

2.点击查找以查找候选构造。

3.点击全选选中所有候选项。

4.点击创建以创建数据对象。

数据对象向导执行与以下命令等效的内容:

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

配置数据对象

设置每个数据对象的数据类型和存储类。要配置数据对象属性,请检查模型数据编辑器中的相应选项卡。使用更改视图下拉列表来显示设计属性(如数据类型)和代码生成设置(如存储类)。

在模型数据编辑器中,检查信号选项卡,然后点击显示/刷新其他信息按钮以显示数据对象向导创建的信号对象的信息。然后,为对象配置下列属性:

  • fbk_1:数据类型 double,存储类 ImportedExtern

  • pos_cmd_two:数据类型 double,存储类 ExportedGlobal

您也可以在命令提示符下使用下列命令来配置对象:

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

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

控制参数数据的文件放置

使用 Embedded Coder®,您可以控制参数和常量定义的文件放置。本示例模型将参数定义写入文件 eval_data.c 中。

要更改参数和常量定义的放置位置,请设置模型配置的数据位置选项。在“配置参数”对话框中,配置代码生成 > 代码布局窗格中的选项。

在本示例模型中,检查“配置参数”对话框中的代码布局窗格。该模型将数据定义放在文件 eval_data.c 中,将声明放在文件 eval_data.h 中。

声明模块参数为可调参数或内联参数

您可以通过设置模型配置参数默认参数行为来控制生成代码中模块参数的默认可调性。

对于本示例模型,默认参数行为设置为 Inlined。默认情况下,模块参数作为数值字面值显示在代码中,而不是作为变量存储在内存中。您可以使用 Simulink.Parameter 对象来覆盖内联并保留各个参数的可调性。

在生成的代码中启用信号数据对象

确保信号数据对象 (Simulink.Signal) 显示在生成的代码中。对于模型中的各个信号线,选择选项状态名称必须解析为 Simulink 信号对象,以将信号名称显式解析为工作区或数据字典中的 Simulink.Signal 对象。在模型数据编辑器中,将更改视图设置为 Code,然后选中解析列中的复选框。

您也可以为模型中的所有信号选择该选项。在命令提示符下,使用 disableimplicitsignalresolution 函数。

查看生成的代码中的数据对象

从示例模型中生成代码

slbuild('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 36.354s

创建代码生成报告以更轻松地查看生成的文件。在 Simulink 编辑器中,选择模型配置参数打开模型报告

您可以访问下列生成的文件:

  • rtwdemo_PCG_Eval_P2.c:定义单步函数和初始化函数。使用定义的数据对象。

  • eval_data.c:将初始值赋给定义的参数。模型配置特意设置了文件名。

  • eval_data.h:提供参数数据的 extern 声明。模型配置特意设置了文件名。

  • ert_main.c:定义调度函数。

  • rtwdemo_PCG_Eval_P2.h:包含存储信号、状态和参数数据的默认结构体的类型定义。由于数据对象设置,某些数据显示在 eval_data.c 中。

  • PCG_Eval_p2_private.h:为生成的函数声明私有(局部)数据。

  • rtwdemo_PCG_Eval_P2_types.h:声明实时模型数据结构体。

  • rtwtypes.h:提供到 Simulink® Coder™ 定义的数据类型 (typedef) 的映射。用于与外部系统集成。

例如,查看文件 eval_data.c,它为与基础工作区中的 Simulink.Parameter 对象对应的全局变量分配 const 内存。

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]

在文件 rtwdemo_PCG_Eval_P2.c 中查看模型 step 函数中的代码算法。该算法直接使用数据对象名称。

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' */

如果数据对象没有指定存储类和其他代码生成设置,则生成的代码:

  • 当您在模型配置参数中选择这些优化时,会内联不变信号和模块参数

  • 将模型输入和输出信号、模块状态和可调参数放在全局数据结构体中

  • 不创建与 MATLAB 变量对应的全局变量

而本示例代码显示,大多数默认数据结构体已被替换为用户定义的数据对象。例如,下列信号和参数在代码中显示为全局变量:

  • pos_rqst

  • fbk_1

  • I_Gain

  • I_OutMap

  • I_InErrMap

但是,局部变量 rtb_IntegralGainShape 和状态变量 rtwdemo_PCG_Eval_P2_DWork.Discrete_Time_Integrator1_DSTAT 仍然使用代码生成器数据结构体。对于这些实体,不存在数据对象。

在数据字典中存储模型数据

当您结束 MATLAB 会话时,您在基础工作区中创建的变量不会持久存在。要永久存储数据对象和总线对象,请考虑将模型链接到数据字典。

  1. 在示例模型中,在建模选项卡上,在设计下,点击链接到数据字典

  2. 在“模型数学”对话框中,点击新建

  3. 在“创建新数据字典”对话框中,将文件名设置为 rtwdemo_PCG_Eval_dict 并点击保存

  4. 在“模型数学”对话框中,点击应用

  5. 点击迁移数据

  6. 在出现关于复制引用变量的消息时,点击迁移

  7. 点击确定

或者,要手动将对象迁移到数据字典中,可以使用以下编程命令:

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

数据字典可永久存储模型使用的对象。要查看字典的内容,请点击模型左下角的模型数据标记,然后点击数据字典链接。在模型资源管理器的模型层次结构窗格中,选择设计数据节点。

数据与模型分离具有以下好处:

一个模型,多个数据集

  • 使用不同的数据类型来更改目标硬件(例如,对于浮点和定点目标使用不同的数据类型)

  • 使用不同的参数值来更改控制算法的行为(例如,对于具有不同标定值的可重用组件使用不同的参数值)

多个模型,一个数据集

  • 在系统中的各 Simulink® 模型之间共享数据

  • 在各工程之间共享数据(例如,变速器、发动机和车轮控制器都可以使用相同的 CAN 消息数据集)

相关主题