Main Content

使用 C API 在生成的代码和外部代码之间交换数据

一些 Simulink® Coder™ 应用程序必须与信号、状态、根级输入/输出或模型生成代码中的参数进行交互。例如,校准应用程序会监控和修改参数。信号监测或数据记录应用程序会与信号、状态和根级输入/输出数据对接。使用 Simulink Coder C API,您可以编译目标应用程序,以在生成的代码执行时记录信号、状态和根级输入/输出,监控信号、状态和根级输入/输出,并调整参数。

C API 可以通过较小的结构体共享信号、状态、根级输入/输出和参数的通用信息,从而最小化其内存占用。信号、状态、根级输入/输出和参数结构体包含结构体映射的索引,从而允许多个信号、状态、根级输入/输出或参数共享数据。

要从某个示例开始,请参阅Use C API to Access Model Signals and StatesUse C API to Access Model Parameters

生成的 C API 文件

当您将模型配置为使用 C API 时,Simulink Coder 代码生成器会生成两个附加文件 model_capi.c(或 .cpp)和 model_capi.h,其中 model 是模型的名称。代码生成器根据“配置参数”对话框中的设置,将两个 C API 文件放置在编译文件夹中。C API 源代码文件包含在模型源代码的生成代码中定义的全局模块输出信号、状态、根级输入/输出以及全局参数的信息。C API 头文件是模型源代码和生成的 C API 之间的接口头文件。您可以使用这些 C API 文件中的信息来创建您的应用程序。

注意

如果您配置代码生成器使之生成支持 C API 接口和数据记录的代码,代码生成器可以在记录到 C API 文件 model_capi.c(或 .cpp)和 model_capi.h 的模块路径中包含模块名称的文本。如果该文本包含模型的字符集编码中未表示的字符,代码生成器会用 XML 转义序列替换这些字符。例如,代码生成器用转义序列 ア 替换日语全角片假名字母 ア。有关详细信息,请参阅 国际化和代码生成

生成 C API 文件

要为您的模型生成 C API 文件,请执行以下操作:

  1. 为您的模型选择 C API 接口。有两种方法可以为您的模型选择 C API 接口,如以下各节中所述。

  2. 为您的模型生成代码。

生成代码后,您可以检查模型编译文件夹中的文件 model_capi.c(或 .cpp)和 model_capi.h

使用“配置参数”对话框选择 C API

  1. 打开您的模型,并打开“配置参数”对话框。

  2. 代码生成 > 接口窗格中,在数据交换接口子组中,选择一个或多个 C API 选项。根据您选择的选项,C API 生成的代码可支持访问信号、参数、状态和根级 I/O。

    • 如果您要为全局模块输出信号生成 C API 代码,请选择生成用于信号的 C API

    • 如果要为全局模块参数生成 C API 代码,请选择生成用于参数的 C API

    • 如果要为离散和连续状态生成 C API 代码,请选择生成用于状态的 C API

    • 如果要为根级输入和输出生成 C API 代码,请选择生成用于根级 I/O 的 C API

从命令行中选择 C API

从 MATLAB® 命令行中,您可以使用 set_param 函数来选中或清除 C API 模型配置参数。在 MATLAB 命令行中,输入以下一个或多个命令,其中 modelname 是您的模型名称。

要选中生成用于信号的 C API,请输入:

set_param('modelname','RTWCAPISignals','on')

要清除生成用于信号的 C API,请输入:

set_param('modelname','RTWCAPISignals','off')

要选中生成用于参数的 C API,请输入:

set_param('modelname','RTWCAPIParams','on')

要清除生成用于参数的 C API,请输入:

set_param('modelname','RTWCAPIParams','off')

要选中生成用于状态的 C API,请输入:

set_param('modelname','RTWCAPIStates','on')

要清除生成用于状态的 C API,请输入:

set_param('modelname','RTWCAPIStates','off')

要选中生成用于根级 I/O 的 C API,请输入:

set_param('modelname','RTWCAPIRootIO','on')

要清除生成用于根级 I/O 的 C API,请输入:

set_param('modelname','RTWCAPIRootIO','off')

C API 文件的说明

关于 C API 文件

model_capi.c(或 .cpp)文件为外部应用程序提供一致的模型数据接口。根据您的配置设置,数据可以是信号、状态、根级输入/输出或者参数。在本文档中,数据项可指信号、状态、根级输入/输出或者参数。C API 使用提供数据项属性接口的结构体。该接口将每个数据项的属性打包到一个数据结构体中。如果模型包含多个数据项,接口会生成一个数据结构体数组。数据结构体的成员映射到数据属性。

为了与数据项对接,应用程序要求每个数据项具有以下属性:

  • 名称

  • 模块路径

  • 端口号(仅适用于信号和根级输入/输出)

  • 地址

  • 数据类型信息:本机数据类型、数据大小、复/实性和其他属性

  • 维度信息:行数、列数和数据方向(标量、向量、矩阵或 n 维)

  • 定点信息:斜率、偏置、定标类型、字长、指数和其他属性

  • 采样时间信息(仅适用于信号、状态和根级输入/输出):采样时间、任务标识符、帧

如下图中的示例所示,数据项 A 的属性位于数据结构体 DS_A 中。数据项 B 的属性位于数据结构体 DS_B 中。

一些属性对于每个数据项是唯一的,一些属性值则可在几个数据项之间通用。例如,名称对于每个数据项都有唯一的值。接口将唯一属性值直接放置在数据项的结构体中。数据项 A 的名称值在 DS_A 中,数据项 B 的名称值在 DS_B 中。

但是,数据类型这一属性的值可在几个数据项之间通用。由于一些数据项能够共享属性,因此 C API 具有重用特性。在本例中,接口仅在 DS_A 和 DS_B 中分别放置一个索引值。这些索引指向另一个数据结构体 DS_C,其中包含实际数据类型值。下图更详细地展示了此方案。

该图显示三个信号。signal1signal2 都具有数据类型 double。接口在结构体中只提供索引值 0,而不是在每个信号数据结构体中指定该数据类型值。"double"rtDataTypeMap 数组中的条目 0 说明,两个信号都引用该条目。此外,属性值可以在信号、状态、根级输入/输出和参数之间共享,因此状态、根级输入/输出和参数也可以引用 rtDataTypeMap 数组中的 double 条目。这种信息重用可减少生成的接口所需的内存大小。

在 C API 文件中生成的结构体数组

接口像处理数据类型一样,将其他公共属性(如地址、维度、定点定标和采样时间)映射到单独的结构体中,并在数据项的结构体中提供相应索引。有关结构体定义的完整列表,请参考文件 matlabroot/rtw/c/src/rtw_capi.h。该文件还说明了结构体中的每个成员。model_capi.c(或 .cpp)文件中生成的结构体数组具有在 rtw_capi.h 文件中定义的结构体类型。以下是在 model_capi.c(或 .cpp)中生成的结构体数组的简要说明:

  • rtBlockSignals 是一个结构体数组,包含关于模型中全局模块输出信号的信息。数组中的每个元素都具有 struct rtwCAPI_Signals 类型。该结构体的成员提供信号名称、模块路径、模块端口号、地址以及指向数据类型、维度、定点和采样时间这些结构体数组的索引。

  • rtBlockParameters 是结构体数组,包含关于模型中的可调模块参数的信息,按模块名称和参数名称列出。数组中的每个元素都具有 struct rtwCAPI_BlockParameters 类型。该结构体的成员提供参数名称、模块路径、地址以及指向数据类型、维度和定点这些结构体数组的索引。

  • rtBlockStates 是一个结构体数组,包含关于模型中离散和连续状态的信息。数组中的每个元素都具有 struct rtwCAPI_States 类型。该结构体的成员提供状态名称、模块路径、类型(连续或离散)以及指向地址、数据类型、维度、定点和采样时间这些结构体数组的索引。

  • rtRootInputs 是一个结构体数组,包含关于模型中根级输入的信息。数组中的每个元素都具有 struct rtwCAPI_Signals 类型。该结构体的成员提供根级输入名称、模块路径、模块端口号、地址以及指向数据类型、维度、定点和采样时间这些结构体数组的索引。

  • rtRootOutputs 是一个结构体数组,包含关于模型中根级输出的信息。数组中的每个元素都具有 struct rtwCAPI_Signals 类型。该结构体的成员提供根级输出名称、模块路径、模块端口号、地址以及指向数据类型、维度、定点和采样时间这些结构体数组的索引。

  • rtModelParameters 是一个结构体数组,其中包含由模型中的一个或多个模块或 Stateflow® 图引用为模块参数的工作区变量的信息。数组中的每个元素都具有数据类型 rtwCAPI_ModelParameters。该结构体的成员提供变量名称、地址以及指向数据类型、维度和定点这些结构体数组的索引。

  • rtDataAddrMap 是一个数组,包含出现在 rtBlockSignalsrtBlockParametersrtBlockStatesrtModelParameters 数组中的信号、状态、根级输入/输出和参数的基址。rtDataAddrMap 数组的每个元素均为指向 void (void*) 的指针。

  • rtDataTypeMap 是一个结构体数组,包含关于模型中各种数据类型的信息。该数组的每个元素都具有 struct rtwCAPI_DataTypeMap 类型。该结构体的成员提供数据类型的名称、数据类型的大小以及关于数据是否为复数的信息。

  • rtDimensionMap 是一个结构体数组,包含关于模型中各种数据维度的信息。该数组的每个元素都具有 struct rtwCAPI_DimensionMap 类型。该结构体的成员提供关于数据中的维数、数据的方向(标量、向量还是矩阵)以及数据实际维度的信息。

  • rtFixPtMap 是一个结构体数组,包含关于信号、状态、根级输入/输出和参数的定点信息。该数组的每个元素都具有 struct rtwCAPI_FixPtMap 类型。该结构体的成员提供关于数据定标、偏置、指数以及定点数据是否有符号的信息。如果模型没有定点数据(信号、状态、根级输入/输出或参数),Simulink Coder 软件会将 NULL 或零值赋给 rtFixPtMap 数组的元素。

  • rtSampleTimeMap 是一个结构体数组,包含关于模型中全局信号、状态和根级输入/输出的采样信息。(该数组不包含关于参数的信息。)该数组的每个元素都具有 struct rtwCAPI_SampleTimeMap 类型。该结构体的成员提供关于采样周期、偏移以及数据基于帧还是基于采样的信息。

生成示例 C API 文件

子主题 C API 信号C API 状态C API 根级输入和输出C API 参数讨论使用示例模型 CAPIPModel 生成的 C API 结构体。要从示例模型生成代码,请执行以下操作:

  1. 打开模型 CAPIModel

    openExample("CAPIModel")

  2. 如果您要为 CAPIModel 中的根级输入/输出生成 C API 结构体,请选择模型配置参数生成用于根级 I/O 的 C API

    此参数的设置必须在顶层模型和引用模型之间匹配。如果修改了参数设置,请将顶层模型和引用模型保存到同一可写工作文件夹中。

  3. 为模型生成代码。

下一个子主题中的 C API 代码示例是用 C 语言作为目标语言生成的。

该模型有三个全局模块输出信号,这些信号将出现在 C API 生成代码中:

  • top_sig1,它是顶层模型中 Gain1 模块输出的测试点

  • sig2_eg,它出现在顶层模型中,并在基础工作区中定义为具有存储类 ExportedGlobalSimulink.Signal 对象

  • bot_sig1,它出现在引用模型 CAPIModelRef 中,并定义为具有存储类 Model defaultSimulink.Signal 对象

该模型还具有两个离散状态,它们将出现在 C API 生成代码中:

  • top_state,它是为顶层模型中的 Delay1 模块定义的

  • bot_state,它是为引用模型中的 Discrete Filter 模块定义的

该模型具有根级输入/输出,如果您选择模型配置参数生成用于根级 I/O 的 C API,这些输入/输出将出现在 C API 生成代码中:

  • 四个根级输入,In1In4

  • 六个根级输出,Out1Out6

此外,该模型有五个全局模块参数,这些参数将出现在 C API 生成代码中:

  • Kp(顶层模型 Gain1 模块和引用模型 Gain2 模块共享)

  • Ki(引用模型 Gain3 模块)

  • p1(查找表 lu1d

  • p2(查找表 lu2d

  • p3(查找表 lu3d

C API 信号

rtwCAPI_Signals 结构体捕获信号信息,包括信号名称、地址、模块路径、输出端口号、数据类型信息、维度信息、定点信息和采样时间信息。

以下是 CAPIModel_capi.c 中的代码节,它提供关于 CAPIModel 中顶层模型的 C API 信号的信息:

/* Block output signal information */
static const rtwCAPI_Signals rtBlockSignals[] = {
  /* addrMapIndex, sysNum, blockPath,
   * signalName, portNumber, dataTypeIndex, dimIndex, fxpIndex, sTimeIndex
   */
  { 0, 0, "CAPIModel/Gain1",
    "top_sig1", 0, 0, 0, 0, 0 },

  { 1, 0, "CAPIModel/lu2d",
    "sig2_eg", 0, 0, 1, 0, 0 },

  {
    0, 0, (NULL), (NULL), 0, 0, 0, 0, 0
  }
};

注意

为了更好地理解代码,请阅读文件中的注释。例如,请注意前面代码中从第三行开始的注释。此注释按顺序列出 rtwCAPI_Signals 结构体的成员。这将告诉您信号中每个成员的赋值出现的顺序。在此示例中,注释告诉您,signalName 是该结构体的第四个成员。以下几行代码描述了第一个信号:

  { 0, 0, "CAPIModel/Gain1",
    "top_sig1", 0, 0, 0, 0, 0 },

从这些代码中,您可以推断出第一个信号的名称是 top_sig1

除最后一个数组元素外,每个数组元素描述模块信号的一个输出端口。最后一个数组元素是哨兵值,所有元素都设置为空值。例如,检查由以下代码描述的第二个信号:

  { 1, 0, "CAPIModel/lu2d",
    "sig2_eg", 0, 0, 1, 0, 0 },

该信号名为 sig2_eg,是 CAPIModel/lu2d 模块第一个端口的输出信号。(此端口是第一个端口,因为第二行显示的从零开始的 portNumber 索引赋值为 0。)

该信号的地址由 addrMapIndex 给出,在此示例中,它显示在第一行,值为 1。该值提供指向 rtDataAddrMap 数组的索引,该数组可在 CAPIModel_capi.c 的后续部分中找到:

/* Declare Data Addresses statically */
static void* rtDataAddrMap[] = {
  &CAPIModel_B.top_sig1,            /* 0: Signal */
  &sig2_eg[0],                         /* 1: Signal */
  &CAPIModel_DWork.top_state,       /* 2: Discrete State */
  &rtP_Ki,                             /* 3: Model Parameter */
  &rtP_Kp,                             /* 4: Model Parameter */
  &rtP_p1[0],                          /* 5: Model Parameter */
  &rtP_p2[0],                          /* 6: Model Parameter */
  &rtP_p3[0],                          /* 7: Model Parameter */
};

索引 1 指向 rtDataAddrMap 数组中的第二个元素。从 rtDataAddrMap 数组中,您可以推断出该信号的地址是 &sig2_eg[0]

这种间接指定可用于同一模型中的多个代码实例。对于多个实例,信号信息保持不变,但地址除外。在本例中,模型是单个实例。因此,代码静态声明 rtDataAddrMap。如果您选择生成可重用代码,将生成初始化函数,该函数会动态地初始化每个实例的地址。有关生成可重用代码的详细信息,请参阅为模型入口函数配置生成的 C 函数接口Configure Code Reuse Support

dataTypeIndex 提供指向 rtDataTypeMap 数组的索引(该数组可在 CAPIModel_capi.c 的后续部分中找到),指示信号的数据类型:

/* Data Type Map - use dataTypeMapIndex to access this structure */
static const rtwCAPI_DataTypeMap rtDataTypeMap[] = {
  /* cName, mwName, numElements, elemMapIndex, dataSize, slDataId, *
   * isComplex, isPointer */
  { "double", "real_T", 0, 0, sizeof(real_T), SS_DOUBLE, 0, 0 }
};

由于 sig2_eg 的索引是 0,因此该索引指向数组中的第一个结构体元素。您可以推断信号的数据类型是 doubleisComplex 的值是 0,表明信号不是复信号。此处没有直接在 rtwCAPI_Signals 结构体中提供数据类型信息,而是采用了间接指定。间接指定允许具有相同数据类型的多个信号指向同一个映射结构体,从而为每个信号节省内存。

dimIndex(维度索引)提供指向 rtDimensionMap 数组的索引(该数组可在 CAPIModel_capi.c 的后续部分中找到),指示信号的维度。由于 sig2_eg 的索引是 1,因此该索引指向 rtDimensionMap 数组中的第二个元素:

/* Dimension Map - use dimensionMapIndex to access elements of ths structure*/
static const rtwCAPI_DimensionMap rtDimensionMap[] = {
  /* dataOrientation, dimArrayIndex, numDims, vardimsIndex */
  { rtwCAPI_SCALAR, 0, 2, 0 },

  { rtwCAPI_VECTOR, 2, 2, 0 },
...
};

从这个结构体中,您可以推断这是维度为 2 的非标量信号。dimArrayIndex 值 2 提供指向 rtDimensionArray 的索引,该数组可在 CAPIModel_capi.c 的后续部分中找到:

/* Dimension Array- use dimArrayIndex to access elements of this array */
static const uint_T rtDimensionArray[] = {
  1,                                   /* 0 */
  1,                                   /* 1 */
  2,                                   /* 2 */
...
};

fxpIndex(定点索引)提供指向 rtFixPtMap 数组的索引(该数组可在 CAPIModel_capi.c 的后续部分中找到),指示关于信号的定点信息。您的代码可以使用定标信息来计算信号的真实值,所用的公式为 V=SQ+B,其中 V 是“真实”(即基数为 10)的值,S 是用户指定的斜率,Q 是“量化的定点值”或“存储的整数”,而 B 是用户指定的偏置。有关详细信息,请参阅定标 (Fixed-Point Designer)

由于 sig2_eg 的该索引是 0,因此信号没有定点信息。定点映射索引为零意味着信号没有定点信息。

sTimeIndex(采样时间索引)提供 rtSampleTimeMap 数组的索引(该数组可在 CAPIModel_capi.c 的后续部分中找到),指示信号的任务信息。如果记录多速率信号或条件执行信号,采样信息会很有帮助。

注意

model_capi.c(或 .cpp)包括 rtw_capi.h。引用 rtBlockSignals 数组的源文件还必须包括 rtw_capi.h

C API 状态

rtwCAPI_States 结构体捕获状态信息,包括状态名称、地址、模块路径、类型(连续或离散)、数据类型信息、维度信息、定点信息和采样时间信息。

以下是 CAPIModel_capi.c 中的代码节,它提供关于 CAPIModel 中顶层模型的 C API 状态的信息:

/* Block states information */
static const rtwCAPI_States rtBlockStates[] = {
  /* addrMapIndex, contStateStartIndex, blockPath,
   * stateName, pathAlias, dWorkIndex, dataTypeIndex, dimIndex,
   * fixPtIdx, sTimeIndex, isContinuous
   */
  { 2, -1, "CAPIModel/Delay1",
    "top_state", "", 0, 0, 0, 0, 0, 0 },

  {
    0, -1, (NULL), (NULL), (NULL), 0, 0, 0, 0, 0, 0
  }
};

除了最后一个数组元素,每个数组元素都描述模型中的一个状态。最后一个数组元素是哨兵值,所有元素都设置为空值。在此示例中,顶层模型的 C API 代码显示一个状态:

  { 2, -1, "CAPIModel/Delay1",
    "top_state", "", 0, 0, 0, 0, 0, 0 },

该状态名为 top_state,是为 CAPIModel/Delay1 模块定义的。isContinuous 的值为零,表明该状态是离散的,而不是连续的。其他字段对应于 C API 信号中具有类似命名的等效信号项,如下所示:

  • 信号地址由 addrMapIndex 给出,在此示例中,该地址为 2。这是指向 rtDataAddrMap 数组的索引,该数组可在 CAPIModel_capi.c 的后续部分中找到。由于该索引从 0 开始,因此 2 对应于 rtDataAddrMap 中的第三个元素,即 &CAPIModel_DWork.top_state

  • dataTypeIndex 提供指向 rtDataTypeMap 数组的索引(该数组可在 CAPIModel_capi.c 的后续部分中找到),指示参数的数据类型。值 0 对应于双精度非复参数。

  • dimIndex(维度索引)提供指向 rtDimensionMap 数组的索引,该数组可在 CAPIModel_capi.c 的后续部分中找到。值 0 对应于第一个条目,即 { rtwCAPI_SCALAR, 0, 2, 0 }

  • fixPtIndex(定点索引)提供指向 rtFixPtMap 数组的索引(该数组可在 CAPIModel_capi.c 的后续部分中找到),指示关于参数的定点信息。与对应的信号属性一样,定点映射索引为零意味着参数没有定点信息。

C API 根级输入和输出

rtwCAPI_Signals 结构体捕获根级输入/输出信息,包括输入/输出名称、地址、模块路径、端口号、数据类型信息、维度信息、定点信息和采样时间信息。(此结构体也用于模块输出信号,如前面在 C API 信号中所述。)

以下是 CAPIModel_capi.c 中的代码节,它提供关于 CAPIModel 中顶层模型的 C API 根级输入/输出的信息:

/* Root Inputs information */
static const rtwCAPI_Signals rtRootInputs[] = {
  /* addrMapIndex, sysNum, blockPath,
   * signalName, portNumber, dataTypeIndex, dimIndex, fxpIndex, sTimeIndex
   */
  { 3, 0, "CAPIModel/In1",
    "", 1, 0, 0, 0, 0 },

  { 4, 0, "CAPIModel/In2",
    "", 2, 0, 0, 0, 0 },

  { 5, 0, "CAPIModel/In3",
    "", 3, 0, 0, 0, 0 },

  { 6, 0, "CAPIModel/In4",
    "", 4, 0, 0, 0, 0 },

  {
    0, 0, (NULL), (NULL), 0, 0, 0, 0, 0
  }
};

/* Root Outputs information */
static const rtwCAPI_Signals rtRootOutputs[] = {
  /* addrMapIndex, sysNum, blockPath,
   * signalName, portNumber, dataTypeIndex, dimIndex, fxpIndex, sTimeIndex
   */
  { 7, 0, "CAPIModel/Out1",
    "", 1, 0, 0, 0, 0 },

  { 8, 0, "CAPIModel/Out2",
    "", 2, 0, 0, 0, 0 },

  { 9, 0, "CAPIModel/Out3",
    "", 3, 0, 0, 0, 0 },

  { 10, 0, "CAPIModel/Out4",
    "", 4, 0, 0, 0, 0 },

  { 11, 0, "CAPIModel/Out5",
    "sig2_eg", 5, 0, 1, 0, 0 },

  { 12, 0, "CAPIModel/Out6",
    "", 6, 0, 1, 0, 0 },

  {
    0, 0, (NULL), (NULL), 0, 0, 0, 0, 0
  }
};

注意

生成 C++ 代码时,代码生成器不提供关于 C API 根级输入/输出的信息。

有关解释 rtwCAPI_Signals 结构体中的值的信息,请参阅前一节 C API 信号

C API 参数

rtwCAPI_BlockParametersrtwCAPI_ModelParameters 结构体捕获参数信息,包括参数名称、模块路径(对于模块参数)、地址、数据类型信息、维度信息和定点信息。

rtModelParameters 数组包含工作区变量的条目,这些变量被引用为可调的 Simulink 模块参数或具有 Stateflow 状态机作用域的数据。例如,可调参数包括使用 Auto 以外的存储类的 Simulink.Parameter 对象。如果没有此种数据,Simulink Coder 软件仅为该数组元素赋予 NULL 或零值。

您为模型配置参数默认参数行为选择的设置将决定以何种方式将信息生成为 model_capi.c(或 .cpp)中的 rtBlockParameters 数组。

  • 如果您将默认参数行为设置为可调,则模型中每个模块的每个可修改参数在 rtBlockParameters 数组中都有一个对应的条目。但是,如果使用 MATLAB 变量或可调参数来指定模块参数,则模块参数不会出现在 rtBlockParameters 中。取而代之,变量或可调参数会出现在 rtModelParameters 中。

  • 如果您将默认参数行为设置为内联,则 rtBlockParameters 数组为空。Simulink Coder 软件仅为其元素赋予 NULL 或零值。

每个数组的最后一个成员是哨兵值,所有元素都设置为空值。

这是在 CAPIModel_capi.c 中默认生成的 rtBlockParameters 数组:

/* Individual block tuning is not valid when inline parameters is *
 * selected. An empty map is produced to provide a consistent     *
 * interface independent  of inlining parameters.                 *
 */
static const rtwCAPI_BlockParameters rtBlockParameters[] = {
  /* addrMapIndex, blockPath,
   * paramName, dataTypeIndex, dimIndex, fixPtIdx
   */
  {
    0, (NULL), (NULL), 0, 0, 0
  }
};

在此示例中,只生成最后一个作为哨兵值的数组元素,结构体 rtwCAPI_BlockParameters 的所有成员都设置为 NULL 和零值。这是因为对于 CAPIModel 示例模型,默认情况下默认参数行为设置为内联。如果将默认参数行为设置为可调,则将在 rtwCAPI_BlockParameters 结构体中生成模型参数。但是,MATLAB 变量和可调参数出现在 rtwCAPI_ModelParameters 结构体中。

这是在 CAPIModel_capi.c 中默认生成的 rtModelParameters 数组:

/* Tunable variable parameters */
static const rtwCAPI_ModelParameters rtModelParameters[] = {
  /* addrMapIndex, varName, dataTypeIndex, dimIndex, fixPtIndex */
  { 2, TARGET_STRING("Ki"), 0, 0, 0 },

  { 3, TARGET_STRING("Kp"), 0, 0, 0 },

  { 4, TARGET_STRING("p1"), 0, 2, 0 },

  { 5, TARGET_STRING("p2"), 0, 3, 0 },

  { 6, TARGET_STRING("p3"), 0, 4, 0 },

  { 0, (NULL), 0, 0, 0 }
};

在此示例中,rtModelParameters 数组包含的各条目对应于可调 Simulink 模块参数引用的每个变量。

例如,第四个参数的 varName(变量名称)是 p2。其他字段对应于 C API 信号中具有类似命名的等效信号项,如下所示:

  • 第四个参数的地址由 addrMapIndex 给出,在此示例中,它是 5。这是指向 rtDataAddrMap 数组的索引,该数组可在 CAPIModel_capi.c 的后续部分中找到。由于该索引从 0 开始,因此 5 对应于 rtDataAddrMap 中的第六个元素,即 rtP_p2

  • dataTypeIndex 提供指向 rtDataTypeMap 数组的索引(该数组可在 CAPIModel_capi.c 的后续部分中找到),指示参数的数据类型。值 0 对应于双精度非复参数。

  • dimIndex(维度索引)提供指向 rtDimensionMap 数组的索引,该数组可在 CAPIModel_capi.c 的后续部分中找到。值 3 对应于第四个条目,即 { rtwCAPI_MATRIX_COL_MAJOR, 6, 2, 0 }

  • fixPtIndex(定点索引)提供指向 rtFixPtMap 数组的索引(该数组可在 CAPIModel_capi.c 的后续部分中找到),指示关于参数的定点信息。与对应的信号属性一样,定点映射索引为零意味着参数没有定点信息。

有关生成代码中可调参数存储的详细信息,请参阅生成的代码如何存储内部信号、状态和参数数据

将 C API 数据结构体映射到实时模型

实时模型数据结构体封装了模型数据和全面描述模型的关联信息。当您选择 C API 功能并生成代码时,Simulink Coder 代码生成器会向 model.h 中生成的实时模型数据结构体添加另一个成员:

/*
 * DataMapInfo:
 * The following substructure contains information regarding
 * structures generated in the model's C API.
 */
struct {
  rtwCAPI_ModelMappingInfo mmi;
} DataMapInfo;

该成员定义 struct rtwCAPI_ModelMappingInfo 类型的 mmi(表示模型映射信息)。该结构体位于 matlabroot/rtw/c/src/rtw_modelmap.h 中。mmi 子结构体定义模型和 C API 文件之间的接口。更具体地说,mmi 的成员将实时模型数据结构体映射到 model_capi.c(或 .cpp)中的结构体。

mmi 成员的值初始化为数组即完成映射,如将模型映射到各个 C API 结构体数组中所示。每个成员都指向生成的 C API 文件中的一个结构体数组。例如,使用 rtw_modelmap.h 文件中的以下代码,会将 rtBlockSignals 结构体数组的地址分配给 model.c(或 .cpp)中 mmi 子结构体的第一个成员:

/* signals */
struct {
    rtwCAPI_Signals const *signals;     /* Signals Array */
    uint_T                numSignals;   /* Num Signals   */
    rtwCAPI_Signals const *rootInputs;  /* Root Inputs array */
    uint_T               numRootInputs; /* Num Root Inputs  */
    rtwCAPI_Signals const *rootOutputs; /* Root Outputs array */
    uint_T               numRootOutputs;/* Num Root Outputs  */
} Signals;

model.c(或 .cpp)中的模型初始化函数通过调用 C API 初始化函数来执行初始化。例如,示例模型 CAPIModel 的模型初始化函数中包含以下生成代码:

/* Initialize DataMapInfo substructure containing ModelMap for C API */
CAPIModel_InitializeDataMapInfo(CAPIModel_M);

将模型映射到各个 C API 结构体数组

注意

该图将数组按其结构体在 rtw_modelmap.h 中的出现顺序列出,这与它们在 model_capi.c 中生成的顺序略有不同。

生成用于与目标系统交换数据的 C API 数据定义文件

此示例说明如何使用基于目标的 C API 与表示信号、状态、参数和根级 I/O 的生成代码对接。

打开示例模型

打开示例模型 CAPIModel

open_system('CAPIModel');

C API 可用于与生成的代码中的应用程序数据进行交互,而不必停止程序执行或重新编译生成的代码。要使用 C API 接口,对于顶层模型及其引用模型,请执行以下操作:

1.在开发计算机和目标计算机之间设置客户端/服务器协议(如 TCP/IP 或双端口内存连接)。

2.选择至少一个 C API 模型配置参数:信号参数状态根级 I/O

3.用可寻址的存储类配置您要使用 C API 访问的数据元素。

顶层模型和引用模型的 C API 配置设置必须匹配。

代码生成器将 C API 接口放在文件 model_capi.c 中。根据您的配置设置,数据可以表示使用可寻址存储类配置的信号、状态、参数和根级 I/O。该文件包括提供数据属性接口的结构体。

C API 的限制

C API 功能具有以下限制。

  • C API 不支持 CodeFormat TLC 变量的下列值:

    • S-Function

    • Accelerator_S-Function(用于加速仿真)

  • 对于基于 ERT 的目标,C API 要求启用对浮点代码的支持。

  • 不支持局部模块输出信号。

  • 不支持局部 Stateflow 参数。

  • 不支持以下自定义存储类对象:

    • 没有 csc_registration 文件的对象

    • 已分组的自定义存储类

    • 使用宏定义的对象

    • BitField 对象

    • FileScope 对象

  • 使用 C API 时,自定义的数据放置被禁用。该接口在 model.hmodel_private.h 中查找全局数据声明。当自定义数据放置将声明放置于任何其他文件中,将导致代码无法编译。

注意

仅当您使用 ERT 系统目标文件并清除模型配置参数忽略自定义存储类时,自定义存储类对象才能在代码生成中工作。

相关主题