关于模拟软件的启动和关闭的分析说明

描述

在之前的导出函数文章中,介绍了如何配置仿真模型并将它导出为函数:使用 Model 模块。

案例中,我们的模型模拟的是代码运行一次的行为。也就是说:假设这些代码要跑在 ECU 里,当模型仿真开始 ECU 启动,这些代码就运行,当仿真结束 ECU 停止。

这是个有意思的过程,不过,要是你想仿真一些更复杂的场景,比如 ECU 多次启动和关闭的场景呢?

这就是需要用的 Initialize Function 和 Terminate Function 模块。

下面的例子模拟了一辆车在两种不同情况下多次启动和关闭的场景:

计数器

a. 当车在运行时,我们使用计数器累加来跟踪记录发动机的在它的整个生命周期里运行的全部时间。

b. 在正常关停的场景下,车钥匙熄火,我们将累计的时间写入一个 non-volatile 内存。所以,在下次汽车启动的时候它还可以被读出来。

c. 假如电池没电了汽车也会关停,但是这时候我们就没有机会把累计时间写入 non-volatile 内存了。

下面我们来看看如何实现上述逻辑。

使用 Initialize 和 Terminate 事件

首先用一个简单的计数器例子,用来模拟发动机运行时间计数:

计数器

把上图这种形式的导出函数模型,使用一个 Model 模块引用起来。在 R2016b 里 Model 模块的参数设置对话框里就会出现两个新选项:

计数器

勾选这两个选项,Model 模块就多出两个输入端口,可连接 Function-call 信号(见下图)。

作为第一次简单尝试,我们用一个 Stateflow chart 来仿真车钥匙启、停时分别启动和关闭这个计数器 EngineRunTime。

计数器

仿真结果如下:

计数器

当车钥匙启动时,计数器在增长,而当车钥匙关停时,计数器停止计数。而当车钥匙再次启动的时候,计数器重置了。

自定义 Initialize 和 Terminate 事件

就像之前所说的,我们并不希望这个计数器在每一次车钥匙关停的时候都重置,不然就没法累计发动机的运行时间了。

为了保留这个计数器的值,我们这时候就可以使用 Initialization 和 Terminate 模块了。

计数器

在 Terminate Function 内部,我们使用 State Reader 模块来获取当前的计数值,并保存在 Data Store 模块里。类似的,在 Initialize Function 内部,我们可以读取这个 Data Store 模块,并用这个值来初始化计数器。

译者补充:熟悉 Simulink 代码生成的朋友都知道,EngineRunTime 模型生成代码时,会生成 step 函数,以及相应的 Initialize 和 terminate 函数。默认情况下 Initialize 函数里进行输入、输出以及状态量的初始化,terminate函数里是空的。R2016b 之后,新增加的这个 Initialize 和 terminate 模块,可以让你显示的定义这两个函数里的内容。

我们来看结果:

计数器

在车钥匙关闭和重启后,每一次 EngineRunTime  被重新调用运行时,是在持续计数的。

Reset Function

就像之前说的,我们还需要模拟由于电池电压过低引起的车辆重启。

这表示,在有些真实场景下,有时候计数器模型结束运行的时候,我们是没机会往 Data Store 里写数据的。

为了模拟这种场景,我们把原来的 Terminate 模块里面的 Terminate Event Listener 模块的事件类型从 Terminate 改为 Reset,并设置一个有含义的名字 writeNVmem。

计数器

这样一来,模型里就不再有 Terminate Function 模块了。当仿真触发 terminate 事件后,就会执行默认的模块 terminate 函数。

我们重写 Stateflow 调度器,处理这两种关停的情况:

计数器

注意,在上面这个模型中,我们在 Model 的参数对话框里勾选了 "Show model reset ports",所以就出来了这个额外的 writeNVmem 端口。

仿真结果如下:

计数器

可以看到,在因为电池失效而引起的关停时,计数器的值并不会保存给下次重启的时候用。

代码生成

现在,仿真结果跟我们想要的一致了,接着我们来生成代码。

在生成的代码里,写入 non-volatile 内存一般都是用户自定义代码来实现,或者是 Embedded target 提供的硬件服务。为了模拟这种情况,我们使用 Function call 模块和 Simulink Function 来实现,以前的帖子有介绍过这两个模块的用法。

简单来说,就是把上面 EngineRunTime 模型里的 Data Store Read/Write 模块替换为 Function call。

另外,为了能在仿真的时候得到同样的结果,我们使用 Simulink Function 来完成读写 Data Store 这个功能,跟之前在Initialize 和Terminate function 的读写功能一样。

这就是整个模型的结构:

计数器

之前的帖子有介绍过,在代码生成的时候,可以在 EngineRunTime 模型里设置配置选项,告诉 Simulink 在链接的时候,去哪里找这个 writeEngineRunTimNV 和 readEngineRunTimNV  函数。

我们把这个导出函数模型生成如下代码:

计数器

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分