和大量的模型、文档和软件资源 如何获取请参考@所有读者:autoMBD发布《autoMBD原创技术文章合集》。 本篇文章继续介绍Simulink生成代码相关的内容,之前的内容可查看往期文章: 特别提示:在本篇文章中使用到的虚拟子系统模型、原子子系统模型,可以在autoMBD资源库的“临时资源分享”文件夹中找到该模型(资源序号tA24)。资源库链接的获取可以在《autoMBD原创技术文章合集》中找到(见文章开头)。文末有活动福利!! 1 虚拟子系统的代码生成 实际中,为了使模型更加简洁,我们通常会使用到虚拟子系统(Virtual Subsystem)。虚拟子系统表示它只是看起来是子系统,实际上,对于生成代码来说是假的、不存在的。 紧接着上一篇文章中的PI控制器模型,可以为模型中的PI控制器部分构建一个虚拟子系统,并为子系统命名为PI_Controller,如下图所示: 框选PI控制器模块 - From autoMBD 模型的内部信号 - From autoMBD 具体地,虚拟子系统的输入、输出端口(即上图中的Err端口和PI_Ctrl端口)是“假的”;与输入、输出端口连接的传递方向第一根信号线(即上图中的Err信号线和虚拟子系统外部输出的PI_Ctrl信号线)也是“假的”。 “假的”意味着不会生成任何代码,修改这些端口和信号线的名称、属性不会影响代码的生成。点击“Generate Code”生成新的代码: 生成代码 - From autoMBD 选中上述的“假”端口或“假”信号线,可以在Code面板中看到提示:“xxx is a virtual block. Code is not generated for virtual blocks.” 2 原子子系统的代码生成 与虚拟子系统相对应的是原子子系统(Atomic Subsystem)。 虚拟子系统如上述所说,不影响代码生成;但原子子系统可以通过配置相关属性,为子系统内部的模型生成单独的函数接口,可以生成inline函数或可重用函数,自定义函数接口形式、函数名称,甚至可以生成单独的源文件。 所以,在生成代码的过程中,一定要注意区分原子子系统与虚拟子系统。 原子子系统的与虚拟子系统外观非常相似,但仔细观察可以发现,原子子系统的边框更加粗,如下图所示: 原子子系统和虚拟子系统 - From autoMBD 需要特别强调的是,所有的触发子系统都是原子子系统,例如上图中的Enable and Triggered Subsystem、If Action Subsystem等,都是原子子系统。 虚拟子系统和原子子系统可以相互转换。但触发子系统不能转换为虚拟子系统,并且在虚拟子系统中添加触发模块后,虚拟子系统会自动转换为原子子系统。 下面是原子子系统的实践。 在PI控制器示例模型中,选择刚刚创建的虚拟子系统,在工具栏中出现“SUBSYSTEM BLOCK”标签,进入SUBSYSTEM BLOCK,可以看到一个“Is Atomic Subsystem”的按钮,点击该按钮即可将虚拟子系统转换为原子子系统,如下图所示: 虚拟子系统转换为原子子系统 - From autoMBD 当然也可以通过“Atomic Subsystem”模块直接创建原子子系统,这里不再赘述。 可以通过对子系统右键单击,来查看原子子系统和虚拟子系统的模块参数,具体对比它们的区别,如下图所示: 查看子系统模块参数的方法 - From autoMBD 查看子系统模块参数:Main - From autoMBD 查看子系统模块参数:Code Generation- From autoMBD 上图中,左边窗口是虚拟子系统的模块参数,右边窗口是原子子系统的模块参数。 可以看到,在Main标签中,原子子系统多了两项参数:
对于代数环,是因为原子子模型会改变模型的执行顺序,这可能会产生代数环。原子子系统可以使用与模型其他部分不同的采样时间。 在Code Generation配置页面中,虚拟子系统是不可编辑的状态,而原子子系统是可以编辑的。 默认情况下,原子子系统的Function packaging的参数设置为“Auto”。设置为“Auto”,那么该原子子系统的代码生成方式和虚拟子系统是相同的。除了Auto,Function packaging还可以设置为:
对于Inline的代码生成方式,实际上Auto和虚拟子系统选择的就是Inline的代码生成方式,Inline的方式具体为,子系统内的模型会嵌在上层模型中,是上层模型代码的一部分。 对于不可复用函数(Noresuable function)和可复用函数(Reusable function)的代码生成方式,该配置会为原子子系统内的模型生成单独的函数,并且上层模型生成的代码是通过函数调用的方式使用原子子系统内的算法。 它们的区别从名称也能看出来,前者生成不可复用的函数,后者生成可复用的函数。什么是可复用和不可复用?可以简单地理解:
可以看到,不可复用函数会产生代码之间的耦合,而可复用函数没有这种耦合。但并不是说不可复用函数就不好,可复用函数也有缺点,它会占用更多的内存,这在嵌入式中尤为敏感。 总的来说可复用函数和不可复用函数有各种的应用场景,要根据实际情况具体分析,选择恰当的形式。 Tips:以我的经验来看,嵌入式领域手写代码的开发中,除了软件库,不可复用函数还更多一些。 在本文的PI控制器示例模型中,以原子子系统的可复用函数代码生成为例,配置Code Generation如下图所示: 配置原子子系统可复用函数Code Generation- From autoMBD 点击“Generate Code”,可以看到两个新生成的子系统.c/.h文件: 两个新生成的子系统.c/.h文件- From autoMBD 可以看到这两个源文件文件名与原子子系统的模块名一致(所以要规范建模)。它们是独立于整个系统模型的,所以可复用,用户在自己的工程中包含这两个文件,即可使用原子子系统内的模型算法了。 打开“PI_Controller.c”文件,可以查看原子子模型内的PI控制器所生成的代码: /* 原子子模型生成的可复用函数 代码生成于“PI_Controller.c” */ /* Output and update for atomic system: '<Root>/PI_Controller' */ real_T autoMBD_example_P_PI_Controller(real_T rtu_Err, DW_PI_Controller_autoMBD_exam_T *localDW) { real_T rty_PI_Ctrl_0;
/* Sum: '<S1>/Sum1' incorporates: * DiscreteIntegrator: '<S1>/Discrete-Time Integrator' * Gain: '<S1>/Kp' */ rty_PI_Ctrl_0 = 2.0 * rtu_Err + localDW->DiscreteTimeIntegrator_DSTATE;
/* Update for DiscreteIntegrator: '<S1>/Discrete-Time Integrator' incorporates: * Gain: '<S1>/Ki' */ localDW->DiscreteTimeIntegrator_DSTATE += 3.0 * rtu_Err * 0.001; return rty_PI_Ctrl_0; }
对于原子子系统不可复用函数的代码生成,以及其他配置选项,由于过程是类似的,读者可以自行探索、体验,这里不再赘述。 3 结束语 为了收集读者对《autoMBD原创技术文章合集》的反馈,特地举行反馈征集活动。在8月31日之前,所有读者可以向作者反馈阅读和使用《autoMBD原创技术文章合集》的好评、差评、想法、感受或建议,参与到活动中。 |
|