多速率多任务环境的s函数
关于多速率多任务环境的s函数
s函数可用于具有多个采样率的模型,并可部署在多任务目标环境中。同样,s函数本身也可以有多个运行速率。代码生成器使用一种称为率分组.在为基于ert的目标生成的代码中,速率分组生成单独的模型
_step
基本费率任务和模型中的每个子任务的函数。尽管费率分组是只在ERT目标中发现的代码生成特性,但您的s函数在编写它们时可以在其他上下文中使用它,如下所述。
S-Functions中的速金宝app率分组支持
要利用速率分组的优势,如果还没有这样做,就必须内联多速率s函数。您需要遵循某些目标语言编译器协议来利用速率分组。编码TLC以利用速率分组并不妨碍您的内联s函数在GRT中正常工作。同样,即使不使内联的s函数符合速率分组,它们也仍然会生成有效的ERT代码。但是,如果您这样做,它们将为多速率模型生成更有效的代码。
有关说明如何创建和升级s函数以生成符合速率分组的代码的目标语言编译器代码的说明和示例,请参见速率分组遵从性和兼容性问题.
对于每个不符合速率分组的多速率s函数,代码生成器在构建时发出以下警告:
Simulink 金宝appCoder:多速率块'/S-Function'的输出函数代码由样本命中检查保护,而不是被速率分组。这将为块使用的所有速率生成相同的代码,可能会生成死代码。为了避免死代码,必须更新块的TLC文件。
您还会在为每个不兼容的s函数生成的代码中发现如下注释:
/*因为multirate block/S-Function的输出函数没有速率分组,下面的代码可能包含不可访问的代码块。为了避免这种情况,必须更新块TLC文件。* /
在这些警告中,“更新函数”被替换为“输出函数”。
创建多任务,多速率,基于端口的采样时间s函数
下面的说明说明如何在多速率s函数中同时支持数据确定性和数据完整性。金宝app它们不包括没有决定论或完整性的情况。金宝app支持基于框架的处理并不影响需求。
请注意
慢速率必须是最快速率的倍数。当接口的两个速率不是倍数或者速率不是周期性的时候,这些说明不适用。
正确处理快速到缓慢过渡的规则
对于输入,多速率s函数应该遵循的规则是
只能以与输入端口采样时间相关联的速率读取输入。
通常,输入数据被写入
DWork
,DWork
然后可以以较慢的(下行)速率访问。
输入可以在输入速率的每次命中样本时读取并写入DWork
记忆,但这DWork
这样,较慢的速率就不能直接访问内存。DWork
用慢速读取的内存,只能用快速写入特殊的样品了.当这个输入端口速率和它所连接的速率都有一个命中时,就会出现一个特殊的命中示例。根据它们的需求和设计,算法可以在多个位置处理数据。
多速率s函数的输出应遵循的规则为
输出不应该用分配给输出端口的速率以外的速率写入,除非在下面描述的优化情况下。
当输出端口的采样速率命中时,应该始终写入输出。
如果满足这些条件,S-Function块可以指定输入端口和输出端口都可以成为本地的和可重用的。
当很少或不需要对数据进行处理时,可以包含优化。在这种情况下,当有特殊的示例命中时,输入速率代码可以直接写入输出(而不是使用DWork)。但是,如果这样做,则必须将输出端口声明为全球而且没有可重用.这种优化减少了一个memcpy
但是在更快的速率上引入了不均匀的处理要求。
无论您是否使用这种优化,最近的输入数据(由较慢的速率看到的)都是较快和较慢的速率都有命中率时的值(以及更早的输入数据,这取决于算法)。较慢的速率的后续步骤和相关的输入数据更新不会被较慢的速率看到,直到下一次遇到较慢的速率。
快到慢速率转换的例子
下面的伪代码抽象了应该如何编写C MEX代码来处理从快到慢的转换,说明了输入速率为0.1秒驱动输出速率为1秒的情况。在内联代码时可以采用类似的方法。该块具有以下特点:
文件:
sfun_multirate_zoh.c
方程:y = u (tslow)
输入:本地可重用
输出:本地和可重用
DirectFeedthrough:是的
OutputFcn if (ssIsSepcialSampleHit(" .1")) {if (ssIsSepcialSampleHit("1")) {DWork = u;}} if (ssIsSampleHit("1")) {y = DWork;}
另一种稍微优化的简单算法方法:
输入:本地可重用
输出:全局且不可重用,因为它需要在特殊的示例命中之间持久存在
DirectFeedthrough:是的
OutputFcn if (ssIsSampleHit(".1")) {if (ssIsSpecialSampleHit("1")) {y = u;}}
添加一个简单算法的示例:
文件:
sfun_multirate_avg.c
;方程:y =平均(u)
输入:本地可重用
输出:本地和可重用
DirectFeedthrough:是的
(假设
DWork [0:10]
而且DWork [mycounter]
初始化为零)一般来说,在这里可以对'u'进行处理,它在每次快速的命中时运行。*/ DWork[DWork[mycounter]++] = u;一般来说,DWork[0:10]上的处理可以在这里完成,但它确实会导致更快的速率有不一致的处理需求(每10次命中,就需要运行更多的代码)。*/ DWork[10] = sum(DWork[0:9])/10;DWork [mycounter] = 0;}} if (ssIsSampleHit("1")) {/* DWork[10]上的处理可以在输出之前在这里完成。这段代码在较慢的任务的每次命中时运行。*/ y = DWork[10];}
正确处理从慢到快转换的规则
当输出速率大于输入速率时,只能以与输入端口采样时间相关的速率读取输入,并遵循以下规则:
总是从更新函数读取输入。
读取输入时不要使用特殊的示例命中检查。
将输入写入DWork。
当速率之间有一个特殊的采样命中时,将DWork复制到输出函数中的第二个DWork中。
在输出采样率的每次命中时,将第二个DWork写入输出。
该块可以请求将输入端口设置为本地端口,但不能将其设置为可重用端口。输出端口可以设置为本地,并可重复使用。
与快速到缓慢转换的情况一样,不应该用分配给输入端口的速率以外的速率读取输入。同样,不应该以分配给输出端口的速率以外的速率写入输出。
当需要实现的算法只需要以较慢的速度运行时,就可以进行优化。在这种情况下,您只使用一个DWork。输入仍然写入更新函数中的DWork。当速率之间有一个特殊的采样命中时,输出函数将相同的DWork直接复制到输出。在这种情况下,必须将输出端口设置为全局的且不可重用。这种优化减少了一个memcpy
每个特殊样本命中的操作。
在这两种情况下,快速率计算操作的数据总是延迟的,也就是说,数据来自慢速率代码的前一步。
慢到快速率转换的例子
下面的伪代码抽象了s函数处理从慢到快的转换需要做什么,说明了输入速率为1秒驱动输出速率为0.1秒。该块具有以下特点:
文件:
sfun_multirate_delay.c
方程:y = u (tslow-1)
输入:设置为本地,如果输出/更新合并(ERT)将为本地,否则将为全局。设置为不可重用,因为在更新函数运行之前需要保留输入。
输出:本地和可重用
DirectFeedthrough:不
OutputFcn if (ssIsSampleHit(".1") {if (ssIsSpecialSampleHit("1") {DWork[1] = DWork[0];} y = DWork[1];} UpdateFcn if (ssIsSampleHit("1")) {DWork[0] = u;}
一些算法可以使用另一种优化的方法:
输入:设置为本地,如果输出/更新合并(ERT)将为本地,否则将为全局。设置为不可重用,因为在更新函数运行之前需要保留输入。
输出:全局且不可重用,因为它需要在特殊的示例命中之间持久存在。
DirectFeedthrough:不
OutputFcn if (ssIsSampleHit(".1") {if (ssIsSpecialSampleHit("1") {y = DWork;}} UpdateFcn if (ssIsSampleHit("1")) {DWork = u;}
添加一个简单算法的示例:
文件:
sfun_multirate_modulate.c
方程:Y = sin(tfast) + u(tslow-1)
输入:设置为local,如果输出/更新合并则为本地(ERT特性),否则为全局。设置为不可重用,因为在更新函数运行之前需要保留输入。
输出:本地和可重用
DirectFeedthrough:不
OutputFcn if (ssIsSampleHit(".1") {if (ssIsSpecialSampleHit("1"){/*这里不太可能进行处理。它导致*更快的速率具有不一致的处理*需求(每10次命中,就需要*运行更多的代码)。*/ DWork[1] = DWork[0];} /*处理速度快*/ y = sin(ssGetTaskTime(".1")) + DWork[1];} UpdateFcn if (ssIsSampleHit("1")){/*处理'u'可以在这里完成。在快速率看到它之前有一个慢速率周期的延迟。*/ DWork[0] = u;}