代码生成器生成由模型定义的算法代码。中解释的技术可以在模型中包括外部(例如,自定义或遗留)代码选择一个外部代码集成工作流.
代码生成器还提供了一个执行生成的模型代码的接口。接口和模型代码一起编译以创建可执行程序。下一个图显示了可执行文件的高级面向对象视图。
实时程序的面向对象视图
通常,模型执行驱动程序的概念设计在生成代码的快速原型和嵌入式风格之间不会改变。下面几节描述模拟(非实时)和实时单任务和多任务环境的模型执行。对于大多数模型代码,多任务环境提供了最有效的模型执行(即最快的采样率)。
下面的概念在描述模型代码如何执行时很有用。
初始化:
初始化接口代码和模型代码。模型
_initialize
ModelOutputs:调用模型中当前时间有样本命中的块,并让它们产生输出。
可以在主要或次要时间步骤中完成。在主时间步长中,输出为给定的模拟时间步长。在较小的时间步长中,接口集成导数来更新连续状态。模型
_output
ModelUpdate:
调用在当前时间点有一个样本命中的块,并让它们更新其离散状态或类似类型的对象。模型
_update
ModelDerivatives:调用模型中具有连续状态的块,并让它们更新其导数。
只在较小的时间步长中调用。模型
_derivatives
ModelTerminate:
如果程序被设计为在有限时间内运行,则终止程序。它破坏实时模型数据结构,释放内存,并可以将数据写入文件。模型
_terminate
实时程序不能占用100%的CPU时间。这一需求提供了在空闲时间运行后台任务的机会。
后台任务包括将数据写入缓冲区或文件、允许第三方数据监控工具访问程序数据或更新程序参数等操作。
然而,重要的是程序能够抢占后台任务,以便模型代码能够实时执行。
程序管理任务的方式取决于它所运行的环境的能力。
实时程序需要仔细地计时任务调用(通过使用中断或实时操作系统任务原语),以便在发生另一个任务调用之前执行模型代码完成。计时包括从外部硬件读取和写入数据的时间。
下一个图说明了中断计时。
任务时间
样本间隔必须足够长,以允许在任务调用之间执行模型代码。
在上图中,两个相邻垂直箭头之间的时间就是采样间隔。上图中的空框显示了一个程序示例,该程序可以在间隔内完成一个步骤,并且仍然为后台任务留出时间。下图中的灰色框表示如果采样间隔过短会发生什么。在任务完成之前发生另一个任务调用。这样的计时会导致执行错误。
如果实时程序被设计为永远运行(即,最终时间为0或无穷大),那么而
循环永远不会退出),那么关闭代码就不会执行。
有关定时引擎如何工作的更多信息,请参见绝对时间和消耗时间计算.
外部模式允许Simulink之间进行通信金宝app®框图和由生成的代码构建的独立程序。在这种模式下,实时程序作为进程间通信服务器,响应来自Simulink引擎的请求。金宝app
配置调试模型解释了如何在模型执行完成时将系统状态、输出和时间保存到mat文件中。的LogTXY
函数执行数据记录,在单任务和多任务环境中的操作不同。
如果你研究一下LogTXY
在单任务和多任务环境中调用,注意到单任务吗LogTXY
之后调用。ModelOutputs
.在这ModelOutputs
调用,方块有一个命中的时间t执行,而在多任务处理中,LogTXY
之后调用。ModelOutputs (tid = 0)
,它只执行当时有命中的块t它的任务标识符是0。这导致单任务和多任务日志记录之间的日志值存在差异。具体来说,考虑一个有两个采样时间的模型,较快的采样时间周期为1.0秒,较慢的采样时间周期为10.0秒。在t = k*10时,k=0,1,2…无论是快的(tid = 0
)和慢(tid = 1
)块执行。在多任务模式下执行时,当LogTXY
调用时,执行慢块,但记录先前的值,而在单任务中记录当前值。
当在启用的子系统中记录数据时,会发生另一种不同。考虑一个启用的子系统,它有一个慢信号驱动启用端口,在启用的子系统中有一个快块。在这种情况下,使能信号的评估发生在慢任务中,快块看到一个采样周期的延迟;因此,日志值将显示这些差异。
为了总结单任务和多任务记录数据的差异,当
根输出端口块的采样时间比最快的采样时间要慢
带状态的块的采样时间比最快的采样时间要慢
在已启用的子系统中,驱动启用端口的信号比已启用的子系统中的块的速率要慢
对于前两种情况,即使单任务和多任务之间的日志值不同,模型结果也没有什么不同。唯一真正的区别是在哪里(在什么时间点)进行日志记录。第三种情况(启用子系统)会导致在实时环境中可以看到的延迟。
这段伪代码显示了非实时单任务系统模型的执行。
main(){初始化While(时间<最终时间)ModelOutputs——主要时间步长。LogTXY——记录时间、状态和根输出。ModelUpdate——主要时间步长。对连续状态模型进行小时间步积分。EndDo——迭代次数取决于求解器积分导数来更新连续状态。EndIntegrate EndWhile终止
初始化阶段首先开始。这包括初始化模型状态和设置执行引擎。然后,模型一步一步地执行。第一个ModelOutputs
按时间执行t,然后记录工作空间I/O数据,然后ModelUpdate
更新离散状态。接下来,如果你的模型有连续状态,ModelDerivatives
对连续状态的导数进行积分,得到时间状态
,在那里h是步长。时间向前移动到
这个过程不断重复。
在ModelOutputs
而且ModelUpdate
模型执行的阶段,只有到达当前时间点的块执行。
这段伪代码显示了非实时多任务系统模型的执行。
main(){初始化While(时间<最终时间)ModelOutputs(tid=0)——主要时间步长。LogTXY——日志时间、状态和根——输出。ModelUpdate(tid=0)——主要时间步长。对连续状态模型进行小时间步积分。EndDo(迭代次数取决于求解器)积分导数来更新连续状态。endintegration For i=1:NumTids ModelOutputs(tid=i)——主要时间步长。ModelUpdate(tid=i)——主要时间步长。EndFor EndWhile终止}
多任务操作比单任务执行更复杂,因为输出和更新功能被细分为任务标识符(tid
)传递给这些函数。这允许使用重叠中断使用不同任务标识符对这些函数进行多次调用,或者在使用实时操作系统时调用多个任务。在模拟中,通过按照实时系统中不存在抢占时发生的顺序执行代码来模拟多个任务。
多任务执行假设任务速率是基本速率的倍数。当您创建金宝app一个固定步骤的多任务模型时,Simulink产品会强制执行这一点。多任务执行循环与单任务非常相似,除了使用任务标识符(tid
)争论ModelOutputs
而且ModelUpdate
.
你不能使用tid
值来自目标文件生成的代码,而不是由金宝app仿真软件编码器™.金宝app仿真软件编码器跟踪使用tid
在为特定子系统或函数类型生成代码时。在目标文件中生成代码时,不能跟踪此参数,因为作用域没有子系统或函数类型。因此,tid
变成一个未定义的变量,目标文件无法编译。
这段伪代码显示了在实时单任务系统中模型的执行,其中模型在中断级别运行。
rtOneStep(){检查中断溢出使能“rtOneStep”中断ModelOutputs—主要时间步长。LogTXY——记录时间、状态和根输出。ModelUpdate——主要时间步长。积分——对连续状态的模型进行小时间步积分。EndDo(迭代次数取决于求解器。)积分导数来更新连续状态。EndIntegrate} main(){初始化(包括安装rtOneStep作为中断服务例程,ISR,用于实时时钟)。While(time < final time)后台任务。EndWhile掩码中断(禁止rtOneStep执行)完成任何后台任务。 Shutdown }
实时单任务执行与非实时单任务执行非常相似,不同之处在于rt_OneStep
函数由周期性的计时器中断驱动。
在程序的基本采样率指定的时间间隔内,中断服务例程(ISR)抢占后台任务以执行模型代码。在模型中,基础抽样率是最快的。如果模型有连续的块,那么积分步长决定了基本采样率。
例如,如果模型代码是一个工作在100hz的控制器,那么每0.01秒后台任务就会中断一次。在此中断期间,控制器从模数转换器(ADC)读取输入,计算输出,将这些输出写入数模转换器(DAC),并更新其状态。然后程序控制返回到后台任务。这些步骤必须在下一次中断之前发生。
这段伪代码展示了模型如何在实时多任务系统中执行,其中模型在中断级别运行。
rtOneStep(){检查中断溢出使能“rtOneStep”中断ModelOutputs(tid=0)——主要时间步长。LogTXY——记录时间、状态和根输出。ModelUpdate(tid=0)——主要时间步长。对连续状态模型进行小时间步积分。EndDo(迭代次数取决于求解器)积分导数,更新连续状态。endintegration For i=1:NumTasks If(在任务i中命中)ModelOutputs(tid=i) ModelUpdate(tid=i) EndIf EndFor} main(){初始化(包括安装rtOneStep作为中断服务例程,ISR,用于实时时钟)。While(time < final time)后台任务。EndWhile掩码中断(禁止rtOneStep执行)完成任何后台任务。 Shutdown }
在实时多任务环境中以中断级别运行模型与之前的单任务环境非常相似,不同之处在于任务的并发执行使用了重叠的中断。
在使用实时操作系统任务原语时,在单任务或多任务环境中执行模型与上面讨论的中断级示例非常相似。下面的伪代码是使用实时任务原语的单任务模型。
qinglerate () {MainLoop:如果clockSem已经“给定”,则由于溢出而错误退出。等待clockSem ModelOutputs——主要时间步长。LogTXY—日志时间,状态和根—输出ModelUpdate—主要时间步长集成—小时间步长集成—用于具有连续—状态的模型。EndDo(迭代次数取决于求解器。)积分导数来更新连续状态。EndIntegrate EndMainLoop} main(){初始化启动/生成任务“tSingleRate”。在clockSem信号量上执行semGive的启动时钟。等待“模型运行”信号量。关闭}
在这种单任务环境中,模型作为实时操作系统任务原语执行。在此环境中,创建单个任务(tSingleRate
)以运行模型代码。当时钟滴答声发生时调用此任务。时钟滴答一声clockSem
(时钟信号量)到模型任务(tSingleRate
).模型任务在执行之前等待信号量。时钟滴答以模型的基本步长(基本速率)发生。
此伪代码用于使用实时任务原语的多任务模型。
tSubRate(subTaskSem,i){循环:等待信号量subTaskSem。ModelOutputs(tid=i) ModelUpdate(tid=i) EndLoop} tBaseRate() {MainLoop:如果clockSem已经“给定”,那么由于溢出错误。Wait on clockSem For i=1:NumTasks If (hit in task i)如果task i正在执行,则由于溢出而错误退出。对任务i的subTaskSem执行“semGive”。EndIf EndFor ModelOutputs(tid=0)——主要时间步长。LogTXY——记录时间、状态和根输出。ModelUpdate(tid=0)——主要时间步长。循环:——对连续状态——模型的小时间步积分。EndDo(迭代次数取决于求解器)积分导数来更新连续状态。EndLoop EndMainLoop} main(){初始化启动/生成任务“tSubRate”。 Start/spawn task "tBaseRate". Start clock that does a "semGive" on a clockSem semaphore. Wait on "model-running" semaphore. Shutdown }
在这个多任务环境中,模型使用实时操作系统任务原语执行。这样的环境需要几个模型任务(tBaseRate
和几个tSubRate
任务)来运行模型代码。基本费率任务(tBaseRate
)的优先级高于子任务。的子任务tid = 1
优先级高于子任务的tid = 2
等等。基本速率任务在时钟滴答声发生时被调用。时钟滴答一声clockSem
来tBaseRate
.第一件事tBaseRate
Does是将信号量提供给在当前时间点有命中的子任务。由于基本速率任务具有更高的优先级,因此它将继续执行。接下来它执行最快的任务(tid = 0
),由模型中具有最快采样时间的块组成。执行之后,它继续等待时钟信号量。时钟滴答被配置为在模型的基本步长发生。
快速原型程序框架提供了一个公共应用程序编程接口(API),该接口在模型定义之间不会更改。
嵌入式编码器®产品提供了一个不同的框架,称为嵌入式程序框架。嵌入式程序框架提供了针对您的模型量身定制的优化API。当您使用生成代码的嵌入式样式时,您是在建模您希望代码在嵌入式系统中如何执行。因此,在模型中定义的定义应该特定于嵌入式目标。诸如模型名称、参数和信号存储类等项都作为API的一部分包含在代码的嵌入式样式中。
快速原型和生成代码的嵌入式风格之间的一个主要区别是,后者包含更少的入口点函数。嵌入式代码样式可以配置为只有一个函数,
.模型
_step
因此,模型执行代码消除了循环…EndLoop
语句和组ModelOutputs
,LogTXY
,ModelUpdate
变成一个单一的陈述,
.模型
_step
有关生成的嵌入式代码如何执行的详细信息,请参见为模型入口函数配置C代码生成.