Main Content

本页的翻译已过时。点击此处可查看最新英文版本。

生成要导出到外部代码库的组件源代码

如果您有 Embedded Coder® 软件,您可以从建模组件中生成函数源代码,以便在外部代码库中使用。生成的代码不包括相关调度代码(例如,单步函数)。Simulink® 环境之外的控制逻辑会调用生成的函数代码。

建模选项

您可以为下列建模组件生成要导出的函数代码:

  • 导出函数模型(包含函数模块的模型,这些函数模块只包含函数调用子系统、函数调用模型模块或其他导出函数模型,如导出函数模型概述中所述)

  • 导出函数子系统(包含函数调用子系统的虚拟子系统)

要导出代码生成器为这些建模组件生成的代码,建模组件必须满足特定的要求 (Embedded Coder)

对于在以前的版本中设计的模型,代码生成器可以从触发子系统中导出函数。对导出函数子系统的相关要求也适用于从触发子系统导出函数,但以下情况除外:

  • 将要从中导出函数的触发子系统封装在一个顶层虚拟子系统中。

  • 触发子系统不必满足针对包含函数调用子系统的虚拟子系统的要求和限制。

  • 导出使用绝对时间或执行时长的函数 (Embedded Coder)不适用于从触发子系统导出函数。

要求

  • 模型求解器必须为固定步长离散求解器。

  • 对于会触发函数调用子系统的每个根级 Inport 模块,必须将其配置为输出一个函数调用触发器。这些 Inport 模块不能连接到 Asynchronous Task Specification 模块。

  • 模型或子系统,在根级必须仅包含以下模块:

    • 函数调用模块(如根级的 Function-Call Subsystem、Simulink Function、S-Functions 和 Function-Call Model 模块,如果求解器模型配置参数 Tasking and sample time options > Periodic sample time constraint 设置为 “Ensure sample time independent”)

    • Inport 和 Outport 模块(端口)

    • Constant 模块(包括解析为常量的模块,如 Add)

    • 采样时间为 Inf 的模块

    • Merge 和 Data Store Memory 模块

    • 虚拟连接模块(例如,Function-Call Split、Mux、Demux、Bus Creator、Bus Selector、Signal Specification 以及包含这些模块的虚拟子系统)

    • 信号查看器模块,如 Scope 模块(仅导出函数子系统)

  • 当模型或子系统的顶层出现常量模块时,您必须将模型或所在模型的配置参数 Optimization > Default parameter behavior 设置为 “Inlined”。

  • 模型或子系统中的模块必须支持代码生成。

  • 使用绝对时间或已用时间的模块必须位于周期性函数调用子系统内,且在对应的函数调用根级 Inport 模块上指定了离散采样时间。请参阅导出使用绝对时间或执行时长的函数 (Embedded Coder)

  • 对于导出的系统,跨越其边界的数据信号不能为虚拟总线,也无法实现为 Goto-From 连接。跨导出边界的数据信号必须为标量、多路或非虚拟总线。

此外,对于导出函数模型,您不能为包含多个导出函数模型实例的基于速率的模型生成代码。例如,对于在仿真期间用于调度可重用的导出函数模型的测试框架模型,您不能为其生成代码。

对于导出函数子系统,还适用以下附加要求:

  • 跨导出函数子系统边界的触发信号必须为标量。不作为触发信号的输入输出数据信号不必是标量。

  • 当常量信号驱动导出函数子系统的输出端口时,该信号必须指定存储类。

导出使用绝对时间或执行时长的函数

当建模组件具有使用绝对时间或执行时长的模块时,如果要为该建模组件导出函数代码,这些模块必须位于函数调用子系统内,该子系统需要满足以下条件:

  • 配置为周期性执行

  • 其根级 Inport 模块配置为使用离散采样时间

要将函数调用子系统配置为周期性执行,请执行以下操作:

  1. 在函数调用子系统中,右键点击 Trigger 模块,并从上下文菜单中选择 Block Parameters

  2. 将参数 Sample time type 设置为 “periodic”。

  3. Sample time 设置为函数调用发起方中指定的相同粒度(直接指定或通过继承)。

  4. 点击 OKApply

有关详细信息,请参阅Absolute and Elapsed Time Computation

导出函数子系统的限制

  • 子系统模块参数不控制包含生成代码的文件的名称。文件名以导出的子系统的名称开头。

  • 子系统模块参数不控制生成代码中顶层函数的名称。每个函数名称会反映触发该函数的信号的名称,如果信号未命名,则反映作为信号源的模块的名称。

  • 仅当 C++ 类代码接口打包的函数设定设置为 Default step method 时,才能导出配置了 C++ 类代码接口打包的函数调用系统。请参阅Interactively Configure C++ Interface (Embedded Coder)。导出的函数与单线程执行兼容。为了避免共享信号的潜在数据竞争情况,请从同一个执行线程调用类的所有成员。

  • 仅当其函数调用发起方在 Accelerator 模式下为非内联时,代码生成器才会在 Accelerator 模式下支持 SIL 或 PIL 模块。Stateflow® 图就是一个非内联的发起方。

  • 2 级 S-Function 发起方模块,如 Stateflow 图或内置 Function-Call Generator 模块,必须驱动 SIL 模块。

  • 您可以导出异步(采样时间)函数调用系统,但软件不支持异步系统的 SIL 或 PIL 模块。

  • 导出函数子系统不再使用 TLC 函数 LibIsFirstInit

工作流

要为导出的函数生成代码,请依次执行下表中列出的任务。

任务操作更多信息
1检查您对外部代码特性和集成要求的评估。Choose an External Code Integration Workflow (Embedded Coder)
2验证您正在导出的模型或子系统是否满足函数导出要求。要求 (Embedded Coder)
3通过修改模型或子系统来满足数据接口要求。 Exchange Data Between External C/C++ Code and Simulink Model or Generated Code (Embedded Coder)
4如有必要,配置函数原型。Configure Entry-Point Function Interfaces for Simulink Function and Function Caller Blocks (Embedded Coder);对于基于速率的固定步长模型,请参阅为模型入口函数配置 C 代码生成 (Embedded Coder)Interactively Configure C++ Interface (Embedded Coder)
5如有必要,更新模型,将外部的应用程序特定代码放入生成的系统函数中。 Place External C/C++ Code in Generated Code (Embedded Coder)
6通过创建和使用测试框架模型,验证函数在仿真期间的行为和性能是否符合预期。测试框架模型在仿真期间调度函数的执行。配置模型、生成代码和仿真 (Embedded Coder);如果您有 Simulink Test™ 软件,请参阅Test Authoring (Simulink Test)
7为代码生成配置模型或子系统。使用 Embedded Coder® 生成代码 (Embedded Coder)Generate Code That Matches Appearance of External Code (Embedded Coder)模型配置 (Embedded Coder)
8生成代码和代码生成报告。代码生成 (Embedded Coder)
9检查生成的代码接口和静态代码指标。Analyze the Generated Code Interface (Embedded Coder)Static Code Metrics (Embedded Coder)
10编译包含导出的函数代码的可执行程序。在 Simulink 环境之外编译集成的代码 (Embedded Coder)
11验证可执行程序的行为和性能是否符合预期。 

选择集成方法

有多种方法可用于生成导出到外部开发环境的函数代码。下表比较了各种方法。您可选择最符合您的集成要求的方法。有关如何创建导出函数模型的详细信息,请参阅导出函数模型概述。有关为函数调用子系统生成代码的详细信息,请参阅生成要导出到外部代码库的组件源代码 (Embedded Coder)

条件或要求使用更多信息
  • 建模元素和生成代码之间的可追溯性

  • 本地输入(Inport 模块)和输出(Outport 模块)

函数调用子系统
  • 对生成的函数原型的控制

  • 形式输入参数(Argument Inport 模块)和输出参数(Argument Outport 模块)

  • 本地输入(Inport 模块)和输出(Outport 模块)

Simulink Function 模块
代码响应初始化事件Initialize Function 模块
代码响应重置事件Reset Function 模块
代码包括的入口函数超出代码生成器默认生成的入口函数(model_initializemodel_stepmodel_terminateS-FunctionS-Function 和代码生成
单模型执行框架要用作测试框架并导出为模型部分生成的代码导出函数子系统

生成导出函数模型的 C 函数代码

此示例说明如何为模型中的各个 Simulink 函数模块和函数调用子系统生成函数代码,而不生成调度代码。

要生成要导出的函数代码,请执行下列操作:

  1. 创建一个包含要导出的函数的模型。

  2. 创建一个测试框架模型,用于在仿真期间调度函数的执行。

  3. 使用该测试框架模型对包含这些函数的模型进行仿真。

  4. 为包含这些函数的模型生成代码。

创建包含要导出的函数的模型

具有要导出的函数的模型必须满足模型根级别的架构约束。在根级别,有效模块为:

  • Inport

  • Outport

  • Function-Call Subsystem

  • Simulink Function

  • Goto

  • From

  • 合并

代码生成器为 Function-Call Subsystem、Simulink Function、Initialize Function 和 Reset Function 模块生成函数代码。对于 Function-call Subsystem 模块,您需要将模块输入端口连接到用于断言函数调用信号的根 Inport 模块。子系统会根据接收到的函数调用信号而执行。Simulink Function 模块会对相应 Function Caller 模块或 Stateflow 图的执行进行响应运行。在发生模型初始化事件时会执行 Initialize Function 模块,在发生用户定义的重置事件时会执行 Reset Function 模块。

为了导出函数,模型 rtwdemo_functions 包含两个函数调用子系统(f1_algf2_alg)和一个 Simulink Function 模块 (f3),它们均用于导出函数。该模型还包含一个 Initialize Function 模块 (Initialize Function) 和一个 Reset Function 模块 (Reset Function)。要计算在模型的其他部分中有状态的模块的初始条件,可在 Initialize Function 和 Reset Function 模块内部使用 State Writer 模块。

open_system('rtwdemo_functions')

创建包含 Function Caller 模型

使用 Function Caller 模块来调用 Simulink Function 模块。Function Caller 模块可以与 Simulink Function 模块在同一模型中,也可以在不同模型中。

多个 Function Caller 模块可以调用一个 Simulink Function 模块。您可以将 Function Caller 模块放在函数调用子系统内。在代码生成期间,代码生成器从函数调用子系统导出函数。

模型 rtwdemo_caller 导出包含 Function Caller 模块的函数调用子系统。

open_system('rtwdemo_caller')

创建用于仿真的测试框架模型

导出函数时,生成的代码不包含调度器。创建一个测试框架模型来处理仿真期间的调度。不要使用该测试框架模型来生成您部署的代码。

模型 rtwdemo_export_functions 是一个测试框架。模型:

  • rtwdemo_caller 中用 Function Caller 模块调度 Simulink Function 模块。

  • 向此示例中的其他模型提供函数调用信号以调度模型内容,包括模型初始化和重置事件。

open_system('rtwdemo_export_functions')

对测试框架模型进行仿真

通过对测试框架模型进行仿真,验证包含要导出的函数的模型是否按预期执行。例如,对 rtwdemo_export_functions 进行仿真。

sim('rtwdemo_export_functions')

生成函数代码

打开 Embedded Coder App。然后,为要导出的函数生成代码。例如,为 rtwdemo_functions 生成代码。

slbuild('rtwdemo_functions')
### Starting build procedure for: rtwdemo_functions
### Successful completion of code generation for: rtwdemo_functions

Build Summary

Top model targets built:

Model              Action          Rebuild Reason                                    
=====================================================================================
rtwdemo_functions  Code generated  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 15.866s

查看生成的代码

查看生成的代码。

  • ert_main.c 是模型的示例主程序(执行框架)。此代码显示如何调用导出的函数。该代码还显示如何初始化和执行生成的代码。

  • rtwdemo_functions.c 调用初始化函数(包括 Initialize Function)和针对模型组件 f1_algf2_algf3 导出的函数。

  • rtwdemo_functions.h 声明模型数据结构体和一个对接导出的入口函数和数据结构体的公共接口。

  • f3.h 是共享文件,用于声明 Simulink 函数 f3 的调用接口。

  • rtwtypes.h 定义生成的代码所需的数据类型、结构体和宏。

编写接口代码

打开并查看代码接口报告。要编写用于您的执行框架的接口代码,请使用该报告中的信息。

  1. 通过添加指令 #include rtwdemo_functions.h#include f3.h#include rtwtypes.h 来包含生成的头文件。

  2. 将输入数据写入模型的 Inport 模块的生成代码。

  3. 调用生成的入口函数。

  4. 从模型 Outport 模块的生成代码中读取数据。

输入端口:

  • rtU.U1 的类型为 real_T,维度为 1

  • rtU.U2 的类型为 real_T,维度为 1

入口函数:

  • 初始化入口函数 void rtwdemo_functions_initialize(void)。在启动时,调用一次此函数。

  • 重置入口函数 void rtwdemo_functions_reset(void)。根据需要调用此函数。

  • 导出的函数 void f1(void)。根据需要调用此函数。

  • 导出的函数 void f2(void)。根据需要调用此函数。

  • Simulink 函数 void f3(real_T rtu_u, real_T *rty_y)。根据需要调用此函数。

输出端口:

  • rtY.Accumulator1 的类型为 int8_T,维度为 [2]

  • rtY.Accumulator2 的类型为 int8_T,维度为 [2]

  • rtY.TicToc10 的类型为 int8_T,维度为 1

有关详细信息

关闭示例模型

bdclose('rtwdemo_export_functions')
bdclose('rtwdemo_functions')
bdclose('rtwdemo_caller')

为导出函数模型生成 C++ 函数和类代码

此示例说明如何为包含函数调用子系统的导出函数模型生成函数代码。代码生成器生成不包括调度代码的函数和类代码。

要生成要导出的函数代码,请执行下列操作:

  1. 创建一个包含要导出的函数的模型。

  2. 创建一个测试框架模型,用于在仿真期间调度函数的执行。

  3. 使用该测试框架模型对包含这些函数的模型进行仿真。

  4. 为包含这些函数的模型生成代码。

创建包含要导出的函数和 C++ 类接口的模型

包含带 C++ 模型类接口的导出函数的模型必须满足模型根级别的架构约束。对于 C++ 类的生成,在根级别有效的模块为:

  • Inport

  • Outport

  • Function-Call Subsystem

  • Goto

  • From

  • 合并

注意:若要导出的 Function-Call Subsystem 采用 C++ 类接口,则不支持 Simulink Function 模块。

代码生成器为 Function-Call Subsystem 模块生成函数代码。对于 Function-call Subsystem 模块,您需要将模块输入端口连接到用于断言函数调用信号的根 Inport 模块。子系统会根据接收到的函数调用信号而执行。

模型 rtwdemo_cppclass_functions 包含用于导出函数的函数调用子系统 f1f2f3

open_system('rtwdemo_cppclass_functions')

创建用于仿真的测试框架模型

导出函数时,生成的代码不包含调度器。创建一个测试框架模型来处理仿真期间的调度。不要使用该测试框架模型来生成您部署的代码。

模型 rtwdemo_cppclass_export_functions 是一个测试框架。该模型向此示例中的其他模型提供函数调用信号以调度模型内容。

open_system('rtwdemo_cppclass_export_functions')

对测试框架模型进行仿真

通过对测试框架模型进行仿真,验证包含要导出的函数的模型是否按预期执行。例如,对 rtwdemo_cppclass_export_functions 进行仿真。

sim('rtwdemo_cppclass_export_functions')

生成函数代码和报告

为要导出的函数生成代码和代码生成报告。例如,为 rtwdemo_cppclass_functions 生成代码。

slbuild('rtwdemo_cppclass_functions')
### Starting build procedure for: rtwdemo_cppclass_functions
### Successful completion of build procedure for: rtwdemo_cppclass_functions

Build Summary

Top model targets built:

Model                       Action                       Rebuild Reason                                    
===========================================================================================================
rtwdemo_cppclass_functions  Code generated and compiled  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 14.827s

查看生成的代码

从代码生成报告中,查看生成的代码。

  • ert_main.cpp 是模型的示例主程序(执行框架)。此代码显示如何调用导出的函数。该代码还显示如何初始化和执行生成的代码。

  • rtwdemo_cppclass_functions.cpp 调用初始化函数(包括 Initialize Function)和针对模型子系统组件 f1f2f3 导出的函数。

  • rtwdemo_cppclass_functions.h 声明模型数据结构体和一个对接导出的入口函数和数据结构体的公共接口。

  • rtwtypes.h 定义生成的代码所需的数据类型、结构体和宏。

编写接口代码

打开并查看代码接口报告。要编写用于您的执行框架的接口代码,请使用该报告中的信息。

  1. 通过添加指令 #include rtwdemo_cppclass_functions.h#include rtwtypes.h 来包含生成的头文件。

  2. 将输入数据写入模型的 Inport 模块的生成代码。

  3. 调用生成的入口函数。

  4. 从模型 Outport 模块的生成代码中读取数据。

输入端口:

  • rtU.U1 的类型为 real_T,维度为 1

  • rtU.U2 的类型为 real_T,维度为 1

  • rtU.U3 的类型为 real_T,维度为 1

入口函数:

  • 初始化入口函数 void initialize(void)。在启动时,调用一次此函数。

  • 导出的函数 void t_1tic_A(void)。根据需要调用此函数。

  • 导出的函数 void t_1tic_B(void)。根据需要调用此函数。

  • 导出的函数 void t_1tic_C(void)。根据需要调用此函数。

输出端口:

  • rtY.TicToc1 的类型为 int8_T,维度为 [2]

  • rtY.TicToc2 的类型为 int8_T,维度为 [2]

  • rtY.TicToc10 的类型为 int8_T,维度为 1

有关详细信息

关闭示例模型

bdclose('rtwdemo_cppclass_export_functions')
bdclose('rtwdemo_cppclass_functions')

为导出函数子系统生成代码

要为导出函数子系统生成代码,请执行以下操作:

  1. 验证您正在为其生成代码的子系统是否满足导出要求 (Embedded Coder)

  2. 在 Configuration Parameters 对话框中,执行以下操作:

    1. 将参数 System target file 设置为基于 ERT 的系统目标文件,如 ert.tlc

    2. 如果您需要包含生成代码的 SIL 模块以用于验证目的,请将模型配置参数 Create block 设置为 “SIL”。

    3. 点击 OKApply

  3. 右键点击子系统模块,并从上下文菜单中选择 C/C++ Code > Export Functions

    该操作创建一个新模型 subsystem.slx,其中包含原始子系统的内容,并创建包含 Model 模块的 ScratchModel。此模块引用新创建的 subsystem.slx 模型。

    Build code for subsystem: Subsystem 对话框将打开。此对话框不是导出函数子系统所特有的对话框。这里不需要在该对话框中输入信息。

  4. 点击 Build 以编译新创建的 subsystem.slx 模型。

    代码生成器生成代码并将其放在工作文件夹中。

    如果在步骤 2b 中将 Create block 设置为 “SIL”,Simulink 将打开一个新窗口,其中包含表示生成代码的 S-Function 模块。此模块与原始子系统具有相同的大小、形状和连接线。

代码生成和模块创建现已完成。您可以像对待生成的 ERT 代码和 S-Function 模块一样,测试和使用这些代码和模块(可选)。有关可选的工作流任务的信息,请参阅指定自定义初始化函数名称指定自定义说明

指定自定义初始化函数名称

您可以为导出函数的初始化函数指定自定义名称,以作为 slbuild 命令的参数。该命令为以下形式:

blockHandle = slbuild('subsystem', 'Mode', 'ExportFunctionCalls',..
             'ExportFunctionInitializeFunctionName', 'fcnname')

fcnname 指定函数名称。例如,如果您指定名称 'myinitfcn',编译过程将生成类似以下内容的代码:

/* Model initialize function */
void myinitfcn(void){
...
}

指定自定义说明

您可以使用 Inport 模块的 Block Properties 对话框输入导出函数的自定义说明。

  1. 对要导出代码的子系统,右键点击驱动其控制端口的 Inport 模块。

  2. 选择 Properties

  3. General 选项卡的 Description 字段中,输入您的描述性文本。

在函数导出过程中,您输入的文本将输出到 Inport 模块的生成代码的头部中。例如,如果您打开示例程序 rtwdemo_exporting_functions 并在端口 t_1tic_A 的 Block Properties 对话框中输入说明,代码生成器将生成类似以下内容的代码:

/*
 * Output and update for exported function: t_1tic_A
 *
 *  My custom description of the exported function
*/
void t_1tic_A(void)
{
...
}

优化为导出函数子系统生成的代码

要优化为导出函数子系统生成的代码,请为每个跨子系统边界的输入信号和输出信号指定单独的存储类。

对于要导出的每个函数调用子系统,请执行以下操作:

  1. 右键点击子系统。

  2. 从上下文菜单中,选择 Block Parameters (Subsystem)

  3. 选择 Code Generation 选项卡。

  4. Function packaging 设置为 “Auto”。

  5. 点击 OKApply

相关主题