分享

模拟软件的启动和关闭

 ghostvip 2022-09-17 发布于广东
  
Guy Rouleau
MathWorks 应用工程师

博客介绍 Simulink 以及其它 MathWorks 的基于模型设计工具。
http://blogs./simulink/

声明:本系列已取得原作者授权翻译转发。

其它:暂定本系列一共翻译十五篇。

这周,我们终于要来说说 Initialize Function,Reset Function 和 Terminate Function 这几个模块了。

作为入门,我建议大家有空先看看我同事 Teresa Hunscher-Younger 做的这个短视频:Initialize and Terminate Functions。(介绍各个模块的用法,我做不了链接,官网可以搜到)

1模拟代码的启动和关闭

在前几天发布的导出函数的贴子里,我们介绍了如何配置仿真模型并将它导出为函数:使用 Model 模块 。

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

这个倒是有点意思。

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

这就是为什么会有 Initialize Function 和 Terminate Function 模块。

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

图片

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

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

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

让我们来看看,在这里我们如何实现上述逻辑

2使用 Initialize 和 Terminate 事件

我们先来看这个简单的计数器的例子,用来模拟发动机运行时间计数:

图片

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

图片

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

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


图片

我们来看一下仿真结果。

图片

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

3自定义 Initialization 和 Terminate 事件

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

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

图片

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

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

我们来看看它的结果。

图片

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

4Reset Function

就像之前说的,我们还需要模拟由于电池电压过低引起的车辆重启。
这表示,在有些真实场景下,有时候计数器模型结束运行的时候,我们是没机会往 Data Store 里写数据的。

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

图片

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

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


图片

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

现在来看看仿真结果。


图片

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

5代码生成

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

在生成的代码里,写入 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  函数。

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


图片

5现在到你了

你觉得这个用法怎么样?

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多