在生成的代码中进行函数划分
此示例说明如何将模型中的子系统与函数名称和文件相关联。
了解如何:
在生成的代码中指定函数和文件名称。
确定生成的代码中集成所需要的部分。
为原子子系统生成代码。
确定执行生成的函数所需的数据。
有关本系列中示例模型和其他示例的信息,请参阅 为 C 代码生成准备控制算法模型。
原子子系统和虚拟子系统
为 C 代码生成准备控制算法模型 和 配置生成的代码中的数据接口 中的示例模型使用虚拟子系统。虚拟子系统在视图层面上组织模块,但不影响模型功能。原子子系统将一个模型中包含的模块作为一个单元进行计算。您可以使用原子子系统指定其他函数划分信息。在模型中,原子子系统用粗边框显示。
查看模型架构中的更改
打开模型示例 rtwdemo_PCG_Eval_P3。
将模型的副本保存到可写文件夹中。
此示例说明如何使用函数调用子系统替换虚拟子系统。函数调用子系统:
是原子子系统
使您能够控制子系统执行顺序
当函数调用信号触发时执行
通过控制子系统的执行顺序,您可以将模型与具有特定执行顺序的现有系统进行匹配。
下图标识了函数调用子系统 (1) PI_ctrl_1
、PI_ctrl_2
和 Pos_Command_Arbitration
。
此版本的模型包含新的子系统 Execution_Order_Control
(2),其中包含一个 Stateflow® 图,后者用于对调度器的调用功能进行建模。子系统通过函数调用信号 (3) 控制函数调用子系统的执行顺序。在本示例后面的部分,您将检验更改执行顺序如何改变仿真结果。
此版本的模型在 PI 控制器的输出端包含新的 Signal Conversion 模块 (4)。使用这些附加模块,代码生成器可以为 PI 控制器生成单一可重入函数。
控制生成的代码中的函数位置和文件放置
在为 C 代码生成准备控制算法模型和配置生成的代码中的数据接口中,代码生成器创建包含控制算法代码的单个 model
_step
函数。然而,许多应用都需要对函数的文件放置进行更高级别的控制。通过修改原子子系统的参数,您可以在单个模型内指定多个函数。
下图显示 PI_ctrl_1
的子系统参数。
Treat as atomic unit
启用其他子菜单。对于原子子系统,此参数将自动选中并处于禁用状态。
Sample time
指定执行的采样时间。不适用于函数调用子系统。
Function packaging 选项
Auto
- 确定子系统在生成的代码中的显示方式。此值是默认值。Inline
- 将子系统代码与其余的模型代码进行内联。Function
- 将子系统的代码生成为一个函数。Reusable function
- 从子系统生成可重用(可重入)函数。该函数通过形参传递所有输入和输出数据。该函数不直接访问全局变量。
Function name options
为 Function packaging 选择
Function
或Reusable function
会启用函数名称选项。Auto
- 确定函数。Use subsystem name
- 使用子系统名称作为函数名称。User specified
- 应用指定的文件名。
File name options
为 Function packaging 选择
Function
或Reusable function
会启用文件名选项。Auto
- 将函数定义放在为父系统生成的模块中;如果模型根是父模型,则放在model.c
中。Use subsystem name
- 生成单独的文件。该文件的名称是子系统或库模块的名称。Use function name
- 生成单独的文件。该文件的名称是您使用 Function name options 指定的名称。User specified
- 应用指定的唯一文件名。
Function with separate data
当您将 Function packaging 设置为
Function
时启用。如果选择此选项,代码生成器会将子系统的内部数据(例如信号)从父模型的数据分离出来。子系统拥有这些单独的数据。
生成可重入代码
Embedded Coder® 支持可重入代码。可重入代码是一种可重用的编程例程,可供多个程序同时使用。可重入代码在使用多线程处理并发事件的操作系统和其他系统软件中使用。可重入代码不维护状态数据,因此在函数中没有持久变量。调用程序会维护状态变量,且必须将状态数据传递到函数中。多个用户或进程可以共享可重入函数的一个副本。
要生成可重入代码,您必须首先通过配置子系统参数 Function packaging 将子系统指定为可重用子系统。
在某些情况下,模型的配置会阻止使用可重用代码。下表列出了常见问题。
Cause Solution
Subsystem output feeds global signal Add a Signal Conversion block between the data subsystem and the global signal.
Generated function receives data Select Configuration Parameters > (formal parameters) through pointers Model Referencing > Pass fixed-size scalar root inputs by value for code generation.
Subsystem uses global signal data Use a port to pass the global data in and out in internal algorithm of the subsystem.
使用封装将参数值传递到库子系统中
要在可重用库模块或子系统范围之外定义算法参数数据(例如增益或系数),您可以向模块或子系统应用封装并创建封装参数。然后,您可以为模块或子系统的每个实例指定不同的参数值。每个封装参数在生成的代码中都显示为可重入函数的形参。
在此版本的模型中,子系统 PI_ctrl_1
和 PI_ctrl_2
已封装。在每个封装中,P
和 I
增益的值由数据对象设置,例如 I_Gain_2
和 P_Gain_2
。
为原子子系统生成代码
在 为 C 代码生成准备控制算法模型 和 配置生成的代码中的数据接口 中,您可以在模型的根级别生成代码。您也可以编译特定的子系统。
要启动子系统编译,请使用上下文菜单。您可以从以下选项中进行选择:
Build This Subsystem:将子系统视为单独的模型,并创建整套的源 C 文件和头文件。此选项不支持函数调用子系统。
Generate S-Function:为子系统生成 C 代码并创建 S-Function 包装器。然后,您可以在原始模型中对代码进行仿真。此选项不支持函数调用子系统。
Export Functions:生成 C 代码,其中不带有使用 Build This Subsystem 选项时会生成的调度代码。使用此选项可编译使用触发器的子系统,例如函数调用子系统。
或者,打开 Embedded Coder,选择子系统,然后在 C Code 选项卡上,点击 Build。
检查生成的代码
此示例将为完整系统编译生成的文件与为导出的函数生成的文件进行比较。您还可以检查封装的数据在代码中的显示方式。
对这三个选项运行编译脚本。然后,通过点击超链接检查生成的文件。
rtwdemo_PCG_Eval_P3.c
Full Build:Yes, Step function
PI_ctrl_1:No
Pos_Command_Arbitration:No
PI_ctrl_1.c
Full Build:No
PI_ctrl_1:Yes, Trigger function
Pos_Command_Arbitration:No
Pos_Command_Arbitration.c
Full Build:No
PI_ctrl_1:No
Pos_Command_Arbitration:Yes, Init and Function
PI_Ctrl_Reusable.c
ert_main.c
eval_data.c
(1) eval_data.c
在完整编译和导出函数编译中具有不同的内容。完整编译包括模型使用的所有参数。导出函数编译只包含子系统使用的变量。
Masked Data in the Generated Code
在文件 rtwdemo_PCG_Eval_P3.c
中,可重入函数的调用位置使用数据对象 P_Gain
、I_Gain
、P_Gain_2
和 I_Gain_2
作为参数。
执行顺序对仿真结果的影响
默认情况下,Simulink® 按以下顺序执行子系统:
PI_ctrl_1
PI_ctrl_2
Pos_Command_Arbitration
对于此示例,您可以指定两种执行顺序之一。然后,您可以使用测试框架观察执行顺序对仿真结果的影响。子系统 Execution_Order_Control
有两个控制执行顺序的配置。要选择一个配置,请使用子系统上下文菜单。
更改执行顺序并观察结果。
仿真结果(随时间变化的节气门位置)根据执行顺序而略有不同。当节气门请求发生变化时,显示的差异最为明显。
有关本系列中的下一个示例,请参阅 从模型和生成的代码调用外部 C 代码。