为非虚拟子系统生成模块化函数码
关于非虚拟子系统代码生成
默认情况下,当为非虚拟子系统生成代码时,代码生成器将与非虚拟子系统关联的内部数据放在与父模型的内部数据相同的数据结构中。这使得跟踪和测试代码变得困难,特别是对于不可重用的子系统。此外,在包含非虚拟子系统的大型模型中,数据结构可能变得很大,可能难以编译。
要为非虚拟子系统(包括原子子系统和有条件执行的子系统)生成模块化函数代码,请使用子系统块参数具有独立数据的函数.此块参数指示代码生成器为独立于父模型数据结构的非虚子系统函数生成块I/O和DWork数据结构。因此,为子系统生成代码:
更容易追踪。
更容易测试。
减少模型的全局数据结构的大小。
使用具有独立数据的函数参数,
使用基于ert的系统目标文件配置模型。
将子系统配置为原子的或有条件执行的。
设置子系统块参数函数包装来
那种一次性的功能
.
要配置子系统以生成模块化函数代码,请调用“子系统参数”对话框并进行一系列选择以显示和启用具有独立数据的函数选择。看到配置模块功能码生成子系统而且非虚拟子系统模块函数代码获取详细信息。有关适用的限制,请参见非虚子系统模块函数代码限制.
有关为原子子系统生成代码的更多信息,请参见将子系统代码生成为独立的函数和文件而且为各个子系统生成代码和可执行文件.
配置模块功能码生成子系统
验证包含子系统的模型使用基于ert的系统目标文件。
选择需要生成模块化功能代码的子系统,打开“子系统参数”对话框。原子子系统的对话框如下所示。(在有条件执行的子系统的对话框中,对话框选项将其视为原子单位显示为灰色,您可以跳过步骤3。)
如果block参数将其视为原子单位可供选择但未被选择,则该子系统既不是原子的,也不是有条件执行的。选择参数将其视为原子单位,这将启用函数包装参数。代码生成选项卡。选择代码生成选项卡。
为函数包装参数,选择
那种一次性的功能
.进行此选择之后,具有独立数据的函数参数显示。类为非虚拟子系统生成代码之前具有独立数据的函数参数选中后,考虑生成清除参数的函数代码并保存生成的函数
.c
而且.h
文件放在单独的目录中,以便以后进行比较。选择具有独立数据的函数参数。出现其他参数。
如果要控制生成的子系统函数和子系统文件的命名,可以修改子系统参数函数名选项而且文件名选项.
保存子系统参数更改并单击退出对话框好吧.
为子系统生成代码并检查生成的文件,包括函数
.c
而且.h
根据子系统参数规范命名的文件。
有关为非虚拟子系统生成代码的更多信息,请参见将子系统代码生成为独立的函数和文件.有关生成的子系统函数代码示例,请参见非虚拟子系统模块函数代码.
非虚拟子系统模块函数代码
方法生成非虚拟子系统函数代码具有独立数据的函数清除并选择参数,并比较结果。
开放范例模型
rtwdemo_atomic
.然后,打开嵌入式编码器将系统目标文件修改为ert.tlc
.这个模型展示了如何保持虚拟子系统的边界。当您选择子系统块参数时将其视为原子单位,代码生成器为子系统生成的代码作为原子单元执行。属性配置为原子时,可以指定代码生成器如何表示子系统函数包装参数。代码生成选项卡。您可以指定子系统转换为以下类型的实现之一:
内联
:调用站点的内联子系统代码。函数
:空/空
函数与I/O和模型全局数据结构中的内部数据。可重用的功能
:带有作为函数参数传入的数据的可重入函数。汽车
:代码生成器基于上下文优化实现。
双击SS1子系统并检查内容。
然后,关闭子系统窗口。
右键单击SS1子系统,从上下文菜单中选择块参数(子系统),并检查设置。万博1manbetx动态仿真模块®当您使用子系统参数使子系统原子化时,代码生成器可以避免“人为的”代数循环尽量减少代数循环的出现.
创建一个变体
rtwdemo_atomic
显示了函数代码没有数据分离。在“子系统参数”对话框中,
在主要选项卡上,选择将其视为原子单位.
在代码生成标签:
集函数包装来
那种一次性的功能
.集函数名选项来
用户指定的
.集函数名来
myfun
.集文件名选项来
使用函数名
.此设置是可选的,但通过将原子子系统函数代码生成到文件中,简化了后面的代码比较任务myfun.c
而且myfun.h
.
做不选择具有独立数据的函数参数。
点击应用应用更改,单击好吧退出对话框。
使用唯一的文件名保存模型变量(例如,
rtwdemo_atomic1
)到可写位置。
创建一个变体
rtwdemo_atomic
显示了函数代码与数据分离。开放模式
rtwdemo_atomic
.打开嵌入式编码器将系统目标文件修改为
ert.tlc
.在模型画布中,右键单击SS1子系统并选择块参数(子系统).在“子系统参数”对话框中,
在主要选项卡上,选择将其视为原子单位.
在代码生成标签:
集函数包装来
那种一次性的功能
.集函数名选项来
用户指定的
.集函数名来
myfun
.集文件名选项来
使用函数名
.选择具有独立数据的函数.
点击应用应用更改,单击好吧退出对话框。
使用唯一的文件名保存模型变量(例如,
rtwdemo_atomic2
)到可写位置。
为每个模型生成代码(例如,
rtwdemo_atomic1
而且rtwdemo_atomic2
).比较
/模型
.c.h
而且myfun.c
/.h
为两个模型生成的文件。有关代码比较讨论,请参见H非虚拟子系统功能数据分离的文件差异而且非虚拟子系统函数数据分离的C文件差异.在本例中,生成的变量之间没有显著差异
ert_main.c
,
,模型
_private.h
,或模型
_types.hrtwtypes.h
.
H非虚拟子系统功能数据分离的文件差异
选择具有独立数据的函数使代码生成器将子系统数据的类型定义放在
myfun.h
文件rtwdemo_atomic2
:/*系统'<根>/SS1'的块状态(默认存储)*/ typedef struct {real_T Integrator_DSTATE;/* '
/Integrator' */} DW_myfun_T; 为
rtwdemo_atomic1
时,子系统数据的类型定义属于模型并出现在rtwdemo_atomic1.h
:/*块信号(默认存储)*/ typedef struct {real_T Sum;/* '
/Sum' */} B_rtwdemo_atomic_1_T;/*系统'<根>'的块状态(默认存储)*/ typedef struct {real_T Integrator_DSTATE;/* ' /Integrator' */} DW_rtwdemo_atomic_1_T; 选择具有独立数据的函数对象中生成以下外部声明
myfun.h
文件rtwdemo_atomic2
:/*外部声明的内部数据的系统'<根>/SS1' */ Extern DW_myfun_T myfun_DW;myfun_Update(void);Extern void myfun(void);
相比之下,生成的代码
rtwdemo_atomic1
包含子系统的模型级外部声明BlockIO
而且D_Work
数据,在rtwdemo_atomic1.h
:/*块信号(默认存储)*/ extern B_rtwdemo_atomic_1_T rtwdemo_atomic_1_B;/*块状态(默认存储)*/ extern DW_rtwdemo_atomic_1_T rtwdemo_atomic_1_DW;
非虚拟子系统函数数据分离的C文件差异
选择具有独立数据的函数导致单独的子系统初始化函数,
myfun_initialize
,将在myfun.c
文件rtwdemo_atomic2
:void myfun_initialize(void) {{((real_T*)&rtwdemo_atomic2_myfunB.Integrator)[0] = 0.0;} rtwdemo_atomic2_myfunDW。Integrator_DSTATE = 0.0;}
子系统中的初始化函数
myfun.c
被模型初始化函数调用在rtwdemo_atomic2.c
:/*模型初始化函数*/ void rtwdemo_atomic2_initialize(void){…/*初始化子系统数据*/ myfun_initialize();}
相比之下,对于
rtwdemo_atomic1
中,子系统数据由模型初始化函数初始化rtwdemo_atomic1.c
:/*模型初始化函数*/ void rtwdemo_atomic1_initialize(void){…/* I/O */{…((real_T*)&rtwdemo_atomic1_B.Integrator)[0] = 0.0;} /* states (dwork) */ rtwdemo_atomic1_DWork。Integrator_DSTATE = 0.0;…}
选择具有独立数据的函数控件中生成以下声明
myfun.c
文件rtwdemo_atomic2
:/*为系统内部数据声明变量'
/SS1' */ dw_myfun_dw; 相比之下,生成的代码
rtwdemo_atomic1
包含子系统的模型级声明BlockIO
而且D_Work
数据,在rtwdemo_atomic1.c
:/*块信号(默认存储)*/ B_rtwdemo_atomic_1_T rtwdemo_atomic_1_B;/*块状态(默认存储)*/ DW_rtwdemo_atomic_1_T rtwdemo_atomic_1_DW;
选择具有独立数据的函数生成反映数据项的子系统方向的标识符命名。子系统函数中对子系统数据的引用,例如
myfun
而且myfun_update
,都在模型中
函数。例如,比较下面的代码模型
_stepmyfun
为rtwdemo_atomic2
/*离散Integrator: '
/Integrator' */ rtwdemo_atomic_2_Y。Out1 = myfun_d . integrator_dstate; 对应的代码
myfun
为rtwdemo_atomic1
./*离散积分器:'
/积分器' */ rtwdemo_atomic_1_Y. 'Out1 = rtwdemo_atomic_1_DW.Integrator_DSTATE;
生成代码中的分区函数
这个例子展示了如何将模型中的子系统与函数名和文件相关联。
学习如何:
在生成的代码中指定函数和文件名。
识别生成的代码中集成所需的部分。
为原子子系统生成代码。
识别执行生成的函数所需的数据。
有关本系列中的示例模型和其他示例的信息,请参见为C代码生成准备一个控制算法模型.
原子子系统和虚拟子系统
示例中的模型为C代码生成准备一个控制算法模型而且在生成代码中配置数据接口使用虚拟子系统.虚拟子系统可视地组织块,但不影响模型功能。原子子系统将模型中包含的块作为一个单元进行评估。使用原子子系统,您可以指定额外的函数分区信息。在模型中,原子子系统以粗体边框显示。
查看模型体系结构中的更改
打开示例模型rtwdemo_PCG_Eval_P3.
将模型的副本保存到可写文件夹中。
这个例子展示了如何将虚拟子系统替换为函数调用子系统.函数调用子系统:
是原子子系统
使您能够控制子系统的执行顺序
当函数调用信号触发器
通过控制子系统的执行顺序,您可以将模型与具有特定执行顺序的现有系统相匹配。
图中标识了函数调用子系统(1)PI_ctrl_1
,PI_ctrl_2
,Pos_Command_Arbitration
.
模型的这个版本包含了新的子系统Execution_Order_Control
(2),其中包含一个Stateflow®图表,它对调度程序的调用功能进行建模。子系统通过函数调用信号(3)控制函数调用子系统的执行顺序。在本例的后面,您将研究如何改变执行顺序可以改变模拟结果。
这个版本的模型在PI控制器的输出端包含了新的信号转换模块(4)。有了这些额外的块,代码生成器就可以为PI控制器生成一个可重入函数。
控制生成代码中的函数位置和文件位置
在为C代码生成准备一个控制算法模型而且在生成代码中配置数据接口,代码生成器创建一个单一的模型
_step
函数,该函数包含控制算法代码。但是,许多应用程序需要对函数的文件放置进行更高级别的控制。通过修改原子子系统的参数,您可以在单个模型中指定多个函数。
的子系统参数PI_ctrl_1
.
将其视为原子单位
启用其他子菜单。对于原子子系统,该参数将被自动选择并禁用。
样品时间
指定执行的采样时间。不适用于函数调用子系统。
功能打包选项
汽车
——决定子系统如何出现在生成的代码中。此值为默认值。内联
——子系统代码与模型代码的其余部分放在一起。函数
——生成子系统的代码作为函数。可重用的功能
——从子系统生成一个可重用(可重入)函数。该函数通过形式参数传递所有输入和输出数据。该函数不直接访问全局变量。
函数名选项
选择
函数
或可重用的功能
为函数包装启用函数名选项。汽车
——决定功能。使用子系统名称
——基于子系统名称的功能。用户指定的
——应用指定的文件名。
文件名选项
选择
函数
或可重用的功能
为函数包装启用文件名选项。汽车
——将函数定义放在为父系统生成的模块中,或者,如果模型根是父系统,则放在model.c
.使用子系统名称
——生成一个单独的文件。文件的名称是子系统或库块的名称。使用函数名
——生成一个单独的文件。文件的名称是您指定的名称函数名选项.用户指定的
——应用指定的惟一文件名。
具有独立数据的函数
当您设置为函数包装来
函数
.选中后,代码生成器将子系统的内部数据(例如,信号)与父模型的数据分离。子系统拥有这些单独的数据。
生成可重入代码
嵌入式Coder®支持万博1manbetx可重入代码.可重入代码是一种可重用的编程例程,多个程序可以同时使用。可重入代码用于使用多线程处理并发事件的操作系统和其他系统软件。可重入代码不维护状态数据,因此函数中没有持久变量。调用程序维护状态变量,并且必须将状态数据传递给函数。多个用户或进程可以共享一个可重入函数的副本。
要生成可重入代码,必须首先通过配置子系统参数将子系统指定为可重用的函数包装.
在某些情况下,模型的配置阻止了代码的可重用。该表列出了常见问题。
原因解决方案
在数据子系统和全局信号之间增加一个信号转换块。
生成的函数接收数据通过指针选择配置参数>(形式参数)模型引用>通过值传递固定大小的标量根输入用于代码生成。
子系统使用全局信号数据通过端口在子系统内部算法中传递全局数据。
使用掩码将参数值传递到库子系统
要在可重用库块或子系统的范围之外定义算法参数数据(例如增益或系数),可以应用面具到块或子系统,并创建掩码参数。然后,您可以为块或子系统的每个实例指定不同的参数值。每个掩码参数都作为可重入函数的正式参数出现在生成的代码中。
在这个版本的模型中,子系统PI_ctrl_1
而且PI_ctrl_2
是被掩盖的。的值P
而且我
增益由数据对象设置,例如I_Gain_2
而且P_Gain_2
.
为原子子系统生成代码
在为C代码生成准备一个控制算法模型而且在生成代码中配置数据接口,您将在模型的根级别生成代码。或者,您可以构建一个特定的子系统。
要启动子系统构建,请使用上下文菜单。您可以从以下选项中选择:
构建这个子系统:将子系统作为一个单独的模式,创建完整的源C文件和头文件。此选项不支持函数调用子系统。万博1manbetx
生成功能:为子系统生成C代码并创建S-Function包装器。然后您可以在原始模型中模拟代码。此选项不支持函数调用子系统。万博1manbetx
导出功能:生成C代码,而不包含随构建这个子系统选择。使用此选项构建使用触发器的子系统,例如函数调用子系统。
或者,打开嵌入式编码器应用程序,选择子系统和C代码选项卡上,单击构建.
检查生成的代码
这个例子比较了为完整系统构建生成的文件和为导出函数生成的文件。您还将检查掩码数据在代码中的显示方式。
为这三个选项运行构建脚本。然后,通过单击超链接检查生成的文件。
rtwdemo_PCG_Eval_P3.c
完整的构建:是的,阶跃函数
PI_ctrl_1:不
Pos_Command_Arbitration:不
PI_ctrl_1.c
完整版:没有
PI_ctrl_1:是的、触发功能
Pos_Command_Arbitration:不
Pos_Command_Arbitration.c
完整版:没有
PI_ctrl_1:不
Pos_Command_Arbitration:是的、Init和Function
PI_Ctrl_Reusable.c
ert_main.c
eval_data.c
(1)eval_data.c
有不同内容的完整和导出功能构建。完整的构建包含模型使用的所有参数。导出函数只包含子系统使用的变量。
生成代码中的屏蔽数据
在文件中rtwdemo_PCG_Eval_P3.c
,可重入函数的调用点使用数据对象P_Gain
,I_Gain
,P_Gain_2
,I_Gain_2
作为参数。
执行顺序对仿真结果的影响
默认情况下,Simuli万博1manbetxnk®执行子系统的顺序如下:
PI_ctrl_1
PI_ctrl_2
Pos_Command_Arbitration
对于本例,您可以指定两个执行顺序中的一个。然后您可以使用测试工具来观察执行顺序对模拟结果的影响。子系统Execution_Order_Control
有两个控制执行顺序的配置。要选择配置,请使用子系统上下文菜单。
改变执行顺序并观察结果。
根据执行顺序的不同,模拟结果(节流位置随时间的变化)略有不同。当油门要求改变时,您可以最清楚地看到差异。
有关本系列的下一个示例,请参见从模型和生成的代码调用外部C代码.
非虚子系统模块函数代码限制
非虚拟子系统块参数具有独立数据的函数具有以下局限性:
该参数可用于配置了基于ert的系统目标文件的模型。
应用该参数的非虚拟子系统不能有多次采样次数或连续采样次数;也就是说,子系统必须是单速率的离散采样时间。
非虚拟子系统不能包含连续状态。
非虚子系统不能输出函数调用信号。
非虚子系统不能包含非内联的s函数。
为非虚拟子系统生成的文件将引用模型范围的头文件,例如
而且模型
.h
.模型
_private.h参数与经典调用接口参数。同时选择两个参数会产生错误。
参数与
可重用的功能
模型配置参数设置代码接口打包.同时选择两个参数会产生错误。
为子系统选择参数时,包含该子系统的模型不能包含数据存储内存块与跨模型实例共享选中。看到数据存储内存.