MBD的Simulink使用技巧⑩:数据存储类的使用方法

您所在的位置:网站首页 simulink中输入信号为t MBD的Simulink使用技巧⑩:数据存储类的使用方法

MBD的Simulink使用技巧⑩:数据存储类的使用方法

#MBD的Simulink使用技巧⑩:数据存储类的使用方法| 来源: 网络整理| 查看: 265

autoMBD最近发布了《autoMBD原创技术文章合集》

《合集》包含156页丰富的MBD入门基础和MBDT硬件支持包的使用,还包含基于MBD的电机控制算法开源项目——AMBD-MC,《合集》配备了丰富的视频讲解和大量的模型、文档和软件资源。获取方式、详情点击下面文章。

在《MBD的Simulink使用技巧⑦:自动生成代码的集成方法》中我们初次接触了存储类的使用,本篇文章将详细介绍Simulink数据存储类的使用方法。

点击以下链接,可以查看MBD的Simulink使用技巧系列的往期文章:

MBD的Simulink使用技巧①:Simulink代码生成的基本概念MBD的Simulink使用技巧②:详解代码生成中的模型与代码MBD的Simulink使用技巧③:虚拟子系统与原子子系统的代码生成MBD的Simulink使用技巧④:详解生成代码的结构与代码的生成流程MBD的Simulink使用技巧⑤:详解自动代码生成的配置与优化MBD的Simulink使用技巧⑥:代码生成目标配置工具MBD的Simulink使用技巧⑦:自动生成代码的集成方法MBD的Simulink使用技巧⑧:函数原型、传参的控制与修改MBD的Simulink使用技巧⑧(续):函数接口封装的控制和修改MBD的Simulink使用技巧⑨:代码数据类型的修改和控制

特别提示:在本篇文章中使用到的PI控制器模型等文件,可以在autoMBD资源库的“临时资源分享”文件夹中找到(资源序号为tA22、tA32、tA33)。资源库链接的获取可以在《autoMBD原创技术文章合集》中找到(见文章开头)。

1 存储类基本介绍

在上一篇文章中我们提到:存储类(Store Classes)控制数据存储的位置,或指示数据的来源

具体而言,存储类控制着代码中的变量声明、变量定义、宏等代码生成;特别地,还控制部分数据类型、数据函数、编译器指令等功能。这部分开发工作在嵌入式C语言中是非常关键的部分。

存储类与代码的编译、链接环节关系紧密,不同的变量声明和定义,编译器会有不同的操作。要想使用好存储类,需要了解必要的编译、链接知识

Tips:这也是为什么一直强调,要完全掌握MBD开发,一定需要纯代码开发的能力。

开发者可以通过“Embedded Coder Dictionary”查看系统预设的存储类,或者新建自定义存储类

“Embedded Coder Dictionary”可以在“C CODE”工作视图中“Code Interface”菜单下找到,如下所示:

Embedded Coder Dictionary入口Embedded Coder Dictionary窗口

可以看到Simulink预设了十几种存储类,点击任意存储类,可以在右边窗口查看它的属性,也可以在底部窗口预览它的代码实现。预设的存储类是不可以编辑的。左上角的“Add”按钮可以新建自定义存储类。

读者可能也发现了,在存储类旁边还有两个标签页:

Function Customization TemplatesMemory SectionMemory Section

“Memory Sections”是对部分存储类的的存储位置进行定义,可以在存储类的属性中看到,“Memory Section”的选项。

除了存储类(针对变量)可以使用,“Memory Sections”还支持对函数的存储位置进行定义,即函数模板,后文会对此进行进一步介绍。

特别注意:“Embedded Coder Dictionary”与数据字典“Data Dictionary”不是同一个东西,前者负责存储类的定义和管理,后者是对数据对象(Data Objects)的管理,后续文章再进行详细介绍,欢迎持续关注autoMBD。

2 存储类的使用

这里不会对所有的存储类都进行介绍,重点介绍存储类的设置方法,以及常用存储类的用法、特点

模型依然采用初始的PI控制器示例模型tA22为例,经过后文修改后的模型为tA32

Tips:在《MBD的Simulink使用技巧⑦:自动生成代码的集成方法》中已经详细介绍了“GetSet”存储类的使用方法和使用场景,这里不再赘述。2.1 设置存储类

设置存储类需要借助“Code Mappings - C”窗口(第8期中介绍的函数原型控制也是在该窗口进行的)。可以通过“C CODE”工作视图中“Code Interface”菜单打开该窗口:

“Code Mappings - C”窗口存储类设置页面

在“Data Default”标签页可以看到存储类相关设置,当然也可以在Inports、Outports、Parameters等标签页针对特定信号、参数进行设置。存储类也是以信号、参数、状态三大类模型数据为基础进行控制的。

特别提示,在Inports、Outports、Parameters等标签页中修改的存储类优先级高于“Data Default”标签页。

新建的模型会对模型中信号、参数、状态等的存储类赋默认值(Default)。但默认值不是固定的,不同的模块、以及不同配置情况下都可能有所不同。

在《第2期》中介绍的模型数据和生成代码之间的联系,就是基于默认存储类展开的,如果修改了存储类,这种联系将会改变

2.2 常见存储类

这里将会介绍如下存储类:

Import From FileExport To FileLocalizable

作为展示,把“Data Default”中Inports存储类修改为“Import From File”,把Outports修改为“Export To File”,把Signals、states等设置为“Localizable”,如下所示:

修改“Data Default”存储类

此时生成的Step函数如下所示:

/* Model step function */ void autoMBD_example_PI_StoreClasses_step(void) { real_T rtb_Err; /* Sum: '/Sum2' incorporates: * Inport: '/Feedback' * Inport: '/Req_Ctrl' */ rtb_Err = autoMBD_example_PI_Sto_Req_Ctrl - autoMBD_example_PI_Sto_Feedback; /* Outport: '/PI_Ctrl' incorporates: * DiscreteIntegrator: '/Discrete-Time Integrator' * Gain: '/Kp' * Sum: '/Sum1' */ autoMBD_example_PI_Stor_PI_Ctrl = 2.0 * rtb_Err + a_DiscreteTimeIntegrator_DSTATE; /* Update for DiscreteIntegrator: '/Discrete-Time Integrator' incorporates: * Gain: '/Ki' */ a_DiscreteTimeIntegrator_DSTATE = 3.0 * rtb_Err * 0.001 + a_DiscreteTimeIntegrator_DSTATE; }

可以看到,输入变量、输出变量和离散状态变量的名称已经不具备在第2期中介绍的特征(即Model_U、model_Y和model_DW),它们已经变成的普通变量(非结构体)。

根据我们的设置,输入变量设置为来自外部文件(存储类“Import From File”),因此生成的代码中不再包含任何的输入变量定义,只在Model_private.h中有一个外部声明。所以要想使用输入变量,需要加入一个定义了输入变量的外部源文件。

它的输入变量声明如下:

/* 文件:autoMBD_example_PI_StoreClasses_private.h */ /* Declaration for custom storage class: ImportFromFile */ extern real_T autoMBD_example_PI_Sto_Feedback;/* '/Feedback' */ extern real_T autoMBD_example_PI_Sto_Req_Ctrl;/* '/Req_Ctrl' */

输出变量设置为导出到外部供其他文件使用(存储类“Export To File”),因此生成的代码中对其有变量定义,而且还在Model.h头文件中进行了外部声明。只要外部文件包含了该头文件,即可使用输出变量,到达了导出变量的效果。

具体输出变量定义代码如下:

/* 文件:autoMBD_example_PI_StoreClasses.c */ /* Definition for custom storage class: ExportToFile */ real_T autoMBD_example_PI_Stor_PI_Ctrl;/* '/PI_Ctrl' */

头文件外部声明如下:

/* 文件:autoMBD_example_PI_StoreClasses.h */ /* Declaration for custom storage class: ExportToFile */ extern real_T autoMBD_example_PI_Stor_PI_Ctrl;/* '/PI_Ctrl' */

而状态变量设置为尽可能本地化(存储类“Localizable”),即:仅模型代码自己能使用,其他外部代码不能使用。这是通过将状态变量定义为static变量实现的,如下所示:

/* 文件:autoMBD_example_PI_StoreClasses.c */ /* Definition for custom storage class: Localizable */ static real_T a_DiscreteTimeIntegrator_DSTATE;/* '/Discrete-Time Integrator' */Tips:最终的模型可以在autoMBD资源库的“临时资源分享”文件夹中找到该模型,资源序号tA32。

简单总结一下:“Import From File”存储类共享外部定义的变量,“Export To File”存储类可以将模型变量共享给外部代码使用,“Localizable”存储类是私域变量,仅自己可用。

可以看到,仅仅只是将存储类进行了简单修改,但生成的代码的使用方法发生了本质的变化,此时的代码与外部代码产生了联系,需要外部代码的相关定义才能正常使用。

虽然这样的修改使得代码使用和集成变得更加复杂了一些,但如能合理使用这些方法,可以避免重复定义,可以提高代码的执行效率,节省更多空间。这在嵌入式系统中是有意义的。

此外,预设存储类“Exported Global”与“Export To File”具有相同的效果;而存储类“Imported External”、“Imported External Pointer”也与“Import From File”具有相同的效果,只不过后者使用的是指针;存储类“File Scope”也具有和“Localizable”一样的效果。

Tips:不太清楚为什么会出现3组功能相似的存储类,但查看Embedded Coder Dictionary可以知道,Exported Global、Imported External和Imported External Pointer的来源是Buit-in,而其他的来源是Simulink package。可能Buit-in是MATLAB中的存储类,与Simulink的存储类有所重复。

结合第7期中介绍的“GetSet”存储类,一共介绍了8个常用的存储类和它们的用法。剩下未提及的存储类交由读者自行去验证和体会。

存储类还可以和数据对象、数据字典结合使用,这部分内容就在后续相关文章中再做介绍,欢迎持续关注autoMBD。

3 Memory Section的使用

仔细查看Simulink预设的十几种存储类,可以发现大部分的存储类都没有Memory Section。因为编译器可以通过关键字(例如static、const、extern等)的方式,确定变量应该的存储位置

Memory Section是另一种通过编译器指令的方式控制变量或函数的存储位置、对齐方式等,给开发者更多的代码控制权

这里列举一些GCC编译器部分常用的编译器指令:

#pragma GCC section text ".name"__attribute__((section("name")))__attribute__(aligned(8)))

前两个指令用于给代码或者变量指定存储位置,区别是前者单条指令对其后的所有变量或函数起作用,而后者单条指令只能对其后的一个变量或函数起作用;第三个用于指定变量、函数起始位置的对齐要求

假设这样一个场景,在系统中有一片特殊的内存区域,它的执行代码的速度比默认代码段存放区域更快。现在希望将Step函数放在这片区域内运行,以达到更快的效率。

在Simulink中如何实现呢?这就要用到Memory Section的功能了。以初始的PI控制器示例模型tA22为例,经过后文修改后的模型为tA33

首先,新建两个自定义的Memory Section,如下图所示:

新建两个Memory Section#pragma Memory Sectionattribute Memory Section

新增的Memory Section中,假设特殊的内存区域被划分为“.fasttextsect”,这需要链接文件真正具有该区域存在,不过链接文件不由Simulink负责。

#pragma和attribute有一点区别,前者需要前声明(Pre Statement)和后声明(Post Statement),分别表示开始和结束;而后者只需要前声明。

然后,在“Function Customization Templates”中新建一个自己的函数模板,如下所示:

新建函数模板

新建的函数模板中,Memory Section选择前面新建的内存区域之一,这里选择的是attribute Memory Secction。

函数模板还能定义函数名的格式控制符(关于格式控制符在《第8期(续)》中有介绍),这里也做了修改。

最后,在“Code Mappings - C”窗口为Step函数选定函数模板,如下所示:

为Step函数选择模板

这样,生成的Step函数就会包含attribute编译器指令,代码如下:

/* 文件:autoMBD_example_PI_MemSect.c */ /* Model step function */ /* Memory Section defined by autoMBD, using attribute method. */ __attribute__((section(".fasttextsect"))) void ctrlPI_step(void) { real_T rtb_Err; /* Sum: '/Sum2' incorporates: * Inport: '/Feedback' * Inport: '/Req_Ctrl' */ rtb_Err = autoMBD_example_PI_MemSect_U.Req_Ctrl - autoMBD_example_PI_MemSect_U.Feedback; ...

编译器在编译时,识别到该指令后,就会将该函数放在“.fasttextsect”区域中,链接时就会将Step函数放在执行速度更快的内存区域中。

读者可以自行将函数模板的内存区域修改为#pragma Memory Secction,查看它的效果。

Tips:最终的模型可以在autoMBD资源库的“临时资源分享”文件夹中找到该模型,资源序号tA33。

上述过程是针对自定义函数模板的,对自定义存储类应用自己的Memory Section也是可以的,过程也和上述是差不多的,读者可以自行验证、体会。有任何疑问可以和作者交流。

其他相关文章:



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3