Main Content

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

调用可重用的外部算法代码进行仿真和代码生成

代码重用可带来业务和技术上的优势。从业务角度来看,代码重用可节省时间和资源。从技术角度来看,代码重用可提高一致性,减少内存要求。其他考虑因素包括:

  • 模块化应用程序

  • 重用优化的算法

  • 与预定义数据集集成

  • 开发应用程序变体

以下各项为独立于硬件且可重用的算法代码,可考虑导入 Simulink® 环境中以进行仿真和代码生成:

  • 工具函数

  • 查找表

  • 数字滤波器

  • 专用积分器

  • 比例积分导数 (PID) 控制模块

工作流

要调用可重用的外部算法代码进行仿真和代码生成,请依次执行下表中列出的任务。

任务操作更多信息
1检查您对外部代码特性和集成要求的评估。Choose an External Code Integration Workflow (Embedded Coder)
2基于外部代码的编程语言,选择一种集成方法以将外部代码添加到 Simulink 模型中。选择集成方法 (Embedded Coder)
3通过仿真模型验证算法行为和性能。仿真
4为代码生成定义模型数据的表示。Exchange Data Between External C/C++ Code and Simulink Model or Generated Code (Embedded Coder)
5配置模型以进行代码生成。Generate Code That Matches Appearance of External Code (Embedded Coder)模型配置 (Embedded Coder)
6生成代码和代码生成报告。代码生成 (Embedded Coder)
7检查生成的代码接口和静态代码指标。Analyze the Generated Code Interface (Embedded Coder)Static Code Metrics (Embedded Coder)
8从模型中编译可执行程序。在 Simulink 环境中编译集成的代码 (Embedded Coder)
9验证可执行程序的行为是否符合预期。数值等效性测试 (Embedded Coder)
10验证可执行程序是否按预期执行。代码执行探查 (Embedded Coder)

选择集成方法

有几种方法可以将可重用的算法代码集成到 Simulink 环境中进行代码生成。有些方法直接集成外部代码。其他方法先将外部代码转换为 Simulink 或 Stateflow® 建模元素用于仿真,随后从建立的模型设计进行代码生成。您选择的集成方法取决于:

  • 外部代码的编程语言 - MATLAB®、C、C++ 或 Fortran

  • 您的编程语言经验和偏好

  • 性能要求

  • 算法必须用于建立连续时间动态模型,还是将算法集成到使用离散和连续时间的应用程序中

  • 是否希望利用基于模型的设计

  • 所需的对代码生成器生成的代码的控制程度

要为可重用算法选择一种方法,请参阅与您的外部算法代码的编程语言相关的小节。

外部 MATLAB 代码的集成方法

有多种方法可以将外部 MATLAB 代码集成到 Simulink 环境中。以下图表可帮助您根据集成要求为应用程序选择最佳集成方法。

 条件或要求操作更多信息
1算法必须用于建立连续状态动态模型。编写一个 MATLAB S-Function,并为生成代码编写对应的算法的 TLC 文件。将 S-Function 添加到您的模型中。
2外部代码是支持代码生成的 MATLAB 代码,并且您要从 Simulink 模型中调用 MATLAB 代码。将 MATLAB Function 模块添加到模型中。在该模块中嵌入 MATLAB 代码。
3外部代码是支持代码生成的 MATLAB 代码,您要从 Simulink 模型调用 MATLAB 代码,并且您的算法包括处理大数据流的迭代计算。向模型中添加 MATLAB System 模块。将 MATLAB 代码作为 System object™ 嵌入该模块。
4外部代码是支持代码生成的 MATLAB 代码,您要从 Simulink 模型调用 MATLAB 代码,并且您的算法包括基于状态机和流程图的设计逻辑。向模型中添加 Stateflow 图。使用 MATLAB 作为动作语言,从图中调用外部代码。
5您想使用 parfor 函数进行并行计算或使用 MATLAB Coder™Simulink Coder 和 Embedded Coder® 可用的接口数据类型。要使用 parfor,必须安装 Parallel Computing Toolbox™。使用软件生成 C 代码。然后,以外部 C 代码形式调用生成的代码。
6您拥有 C 或 C++ 编程经验,外部 MATLAB 代码非常简洁且主要使用 C 或 C++ 构造。手动将 MATLAB 代码转换为 C 或 C++ 代码。为 C 或 C++ 代码选择一种集成方法。

外部 C 或 C++ 代码的集成方法 (Embedded Coder)

7外部 MATLAB 代码的有些部分可对应到内置模块。使用适用的内置模块在模型中开发算法。

要将外部 MATLAB 代码嵌入 MATLAB Function 模块中或使用 MATLAB Coder 软件从 MATLAB 代码中生成 C 或 C++ 代码,MATLAB 代码必须使用 C/C++ 代码生成支持的函数和类。

外部 C 或 C++ 代码的集成方法

在大多数情况下,您可以通过使用代码继承工具生成 S-Function 和 TLC 文件,将用 C 或 C++ 编写的外部代码集成到 Simulink 环境中。此工具使用您以 MATLAB 代码形式提供的设定,将现有 MATLAB 函数转换为您可以包含在 Simulink 模型中并从生成的代码中调用的 C MEX S-Function。有关详细信息,请参阅使用代码继承工具实现算法使用代码继承工具在生成的代码中导入对外部代码的调用 (Embedded Coder)

与替代方法相比,代码继承工具是为嵌入式系统生成足够优化的代码的最佳选择。如果存在以下一种或多种情况,请考虑替代方法:

  • 外部代码使用全局变量来交换数据。

  • 编程经验有限。

  • 算法必须用于建立离散和连续状态动态模型。

  • 您要在 Stateflow 图中包含集成的外部代码。

  • 外部代码需要定点接口。

  • 您需要最大的灵活性来控制代码生成器生成的代码。

  • 您很快要在对嵌入 MATLAB Function 模块中的 coder.ceval 函数的调用中嵌入对外部代码的调用,并且性能不是问题。

以下图表可帮助您根据您的集成要求选择最佳集成方法。

 条件或要求操作更多信息
1您要将外部 C 代码与生成的 C++ 代码集成在一起,或者反之通过修改外部代码的语言以与生成代码的语言选择一致。修改外部代码的编程语言以匹配生成的代码 (Embedded Coder)
2您的算法包括基于状态机和流程图的设计逻辑。或者,要集成的函数必须使用全局变量与模型交换数据。该函数定义全局变量,并使用它们来写函数的输出,而不是返回值(返回)或将输出写至参数。向模型中添加 Stateflow 图。使用 C 语言作为动作语言从图中调用外部代码。在图中,编写调用外部函数并读写全局变量的代码。要使用外部代码的输出来执行计算,模型必须在执行期间从全局变量中读取数据。将外部代码插入 Stateflow 图中 (Embedded Coder)
3您要在 Stateflow 图中包含外部 C 或 C++ 代码,用于仿真和代码生成。配置包含图的模型以应用外部 C 或 C++ 代码。
4您很快要在模型中嵌入对外部 C 或 C++ 代码的调用。性能不是问题。从 MATLAB Function 模块中调用具有 coder.ceval 函数的 C 或 C++ 代码。
5应用程序需要的入口函数多于代码生成器通常生成的入口函数,例如,需要的入口函数不仅仅是 model_stepmodel_initializemodel_terminate 入口函数。您需要最大的灵活性来控制代码生成器生成的代码。手动编写 S-Function 和 TLC 文件。
6您要对离散时间应用程序进行仿真和生成外部代码。优化生成的代码至关重要。您希望能够以适度的灵活性轻松控制代码生成器生成的代码。您有 C 或 C++ 编程经验,但您更喜欢生成用于将代码添加到模型中的文件。

使用代码继承工具生成 S-Function 和 TLC 文件。如有必要,手动细化生成的代码以满足应用程序要求。(如果您更改了生成的代码,则在您重新生成 S-Function 和 TLC 文件时,您将丢失这些更改。)

对于用 C 语言编写的简单算法,考虑使用 Simulink C Caller 模块。

7算法必须用于对离散和连续的状态动态建模以进行仿真和快速原型。外部代码需要定点接口。编程经验有限。您希望能够以基本的灵活性轻松控制代码生成器为快速原型生成的代码。使用 S-Function Builder 生成 S-Function 和 TLC 文件。如有必要,手动细化生成的代码以满足应用程序要求。(如果您更改了生成的代码,则在您重新生成 S-Function 和 TLC 文件时,您将丢失这些更改。)

修改外部代码的编程语言以匹配生成的代码

要将外部 C 代码与生成的 C++ 代码集成(或者反之),请修改外部代码的语言以与生成代码的编程语言选择一致。使编程语言匹配的选项包括:

  • 用生成代码的语言选择编写或重写外部代码。

  • 如果您正在生成 C++ 代码并且外部代码是 C 代码,则为每个 C 函数创建一个为该函数建立原型的头文件。使用以下格式:

    #ifdef __cplusplus
    extern "C" {
    #endif
    int my_c_function_wrapper();
    #ifdef __cplusplus
    }
    #endif

    原型充当函数封装程序。如果您的编译器支持 C++ 代码,则会定义值 __cplusplus。链接设定 extern "C" 指定不带名称修饰的 C 链接。

  • 如果您正在生成 C 代码并且外部代码是 C++ 代码,请在每个 .cpp 文件中包含 extern "C" 链接设定。例如,以下示例展示文件 my_func.cpp 中的 C++ 代码:

    extern "C" {
    
    int my_cpp_function()
    {
      ...
    }
    }

外部 Fortran 代码的集成方法

要集成外部 Fortran 代码,请编写 S-Function 和对应的 TLC 文件。

请参阅 C/C++ S-Function 基础知识用 Fortran 代码实现算法S-Function 和代码生成 (Embedded Coder)Fortran S-Function Examples

将外部代码插入 Stateflow 图中

为库图集成外部代码

要集成仅应用于 Stateflow 库图的外部代码以进行代码生成,则对于为您的主模型提供了 Stateflow 图的每个库模型,请完成以下步骤。然后再生成代码。

  1. 在 Stateflow Editor 中,打开 Model Configuration Parameters 对话框。选择参数 Use local custom code settings (do not inherit from main model)

    库模型在代码生成过程中会保留它自己的自定义代码设置。

  2. 在子窗格中指定您的自定义代码。

    请遵循指定自定义代码的相对路径 (Stateflow) 中的规范。

    如果您为仿真指定了自定义代码设置,则可以将这些设置应用于代码生成。要避免两次输入相同的信息,请选择 Use the same custom code settings as Simulation Target

  3. 点击 OK

为每个库模型完成上述步骤后,生成代码。

为所有图集成外部代码

要集成应用于所有图的外部代码以进行代码生成,请执行以下操作:

  1. 为主模型的代码生成指定自定义代码选项。

    1. 在 Model Configuration Parameters 对话框中,选择 Code Generation > Custom Code

    2. 在自定义代码文本字段中,指定您的自定义代码。

      请遵循指定自定义代码的相对路径 (Stateflow) 中的规范。

      如果您为仿真指定了自定义代码设置,则可以将这些设置应用于代码生成。要避免两次输入相同的信息,请选择 Use the same custom code settings as Simulation Target

  2. 为向主模型提供 Stateflow 图的每个库模型进行代码生成配置。在 Stateflow Editor 中,打开 Model Configuration Parameters 对话框。清除参数 Use local custom code settings (do not inherit from main model)。库图将继承主模型的自定义代码设置。点击 OK

  3. 生成代码。

从模型和生成的代码调用外部 C 代码

通过使用代码继承工具从仿真或生成的代码调用现有外部函数。

了解如何:

  • 将 C 函数作为 Simulink® 模型仿真的一部分进行计算。

  • 从您从模型生成的代码中调用 C 函数。

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

替换过程

打开示例模型 rtwdemo_PCG_Eval_P4

对于许多基于模型的设计应用,除了 Simulink® 模型之外,设计还包括一组经过测试和验证的现有 C 函数。您可以将这些函数集成到一个 Simulink® 模型中,并生成使用这些函数的代码。

在本示例中,您将创建一个调用现有 C 函数的自定义 Simulink® 模块。然后,将该模块包含在一个模型中,并通过在 Simulink® 中进行模型仿真来测试整个系统。

在示例模型中,您可以将 PI 控制器中的 Lookup 模块(查找表)替换为对一个现有 C 函数的调用。该函数在文件 SimpleTable.cSimpleTable.h 中定义。

查看 SimpleTable.c

查看 SimpleTable.h

创建调用 C 函数的模块

要指定对现有 C 函数的调用,请使用 S-Function 模块。您可以使用代码继承工具自动创建 S-Function 模块。在该工具中,您将首先为现有 C 函数指定接口。然后,工具使用该接口创建一个 S-Function 模块。

使用代码继承工具为 SimpleTable.c 中的现有 C 函数创建一个 S-Function 模块。

1.创建一个结构体以包含函数接口的定义。

def = legacy_code('initialize')

您可以使用结构体 def 定义现有 C 代码的函数接口。

2.填充该结构体的字段。

3.创建 S-Function。

legacy_code('sfcn_cmex_generate',def)

4.编译 S-Function。

legacy_code('compile',def)

5.创建 S-Function 模块。

legacy_code('slblock_generate',def)

生成的 S-Function 模块调用 SimpleTable.c 中的 C 函数。您现在可以在模型中使用此 S-Function 模块。

6.创建 TLC 文件。

legacy_code('sfcn_tlc_generate',def)

此命令创建一个 TLC 文件,该文件是 S-Function 的一个组件,指定如何为模块生成代码。

通过仿真验证外部代码

在 Simulink® 模型中集成现有 C 代码时,将验证生成的 S-Function 模块。

要验证 Lookup 模块的替换,请将 Lookup 模块产生的仿真结果与新 S-Function 模块产生的结果进行比较。

1.打开验证模型。

  • Sine Wave 模块从 [-2 :2] 产生输出值。

  • 查找表的输入范围来自 [-1 :1]。

  • 查找表输出输入的绝对值。

  • 查找表根据输入范围对输出进行裁剪。

2.运行验证模型。

下图显示验证结果。现有 C 代码和 Simulink® 表模块产生相同的输出值。

将 C 代码作为 Simulink® 模型的一部分进行验证

将现有 C 函数代码作为独立组件进行验证后,在模型中验证 S-Function。要完成验证,请使用测试框架模型。

1.打开测试框架。

2.运行测试框架。

仿真结果与黄金值匹配。

从生成的代码调用 C 函数

代码生成器使用 TLC 文件处理 S-Function 模块,就像处理任何其他模块一样。代码生成器可以使用 S-Function 模块实现表达式折叠,这是一种将多个计算合并到一个输出计算中的操作。

1.编译完整模型。

2.PI_Control_Reusable.c 中检查生成的代码。

生成的代码现在调用 SimpleTable C 函数。

下面的图显示生成的代码在 C 代码集成之前和之后的情况。在集成之前,代码调用生成的查找例程。在集成之后,生成的代码调用 C 函数 SimpleTable

相关主题