中国汽车工程师之家--聚集了汽车行业80%专业人士 

论坛口号:知无不言,言无不尽!QQ:542334618 

本站手机访问:直接在浏览器中输入本站域名即可 

  • 136查看
  • 0回复

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

[复制链接]

该用户从未签到

发表于 2-3-2024 09:42:32 | 显示全部楼层 |阅读模式

汽车零部件采购、销售通信录       填写你的培训需求,我们帮你找      招募汽车专业培训老师


全文约3500字,你将看到以下内容:


    存储类基本介绍

    存储类的使用

    Memory Section的使用

autoMBD最近发布了《autoMBD原创技术文章合集》
合集包含156页丰富的MBD入门基础和MBDT硬件支持包的使用还包含基于MBD的电机控制算法开源项目——AMBD-MC合集配备了丰富的视频讲解
和大量的模型、文档和软件资源

如何获取请参考@所有读者:autoMBD发布《autoMBD原创技术文章合集》。

在《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”菜单下找到,如下所示:

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

Embedded Coder Dictionary入口 - From autoMBD

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

Embedded Coder Dictionary窗口 - From autoMBD

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

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


    Function Customization Templates

    Memory Section


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

Memory Section - From autoMBD

“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”菜单打开该窗口:

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

“Code Mappings - C”窗口 - From autoMBD

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

存储类设置页面 - From autoMBD

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

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

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

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

2.2 常见存储类

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


    Import From File

    Export To File

    Localizable

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

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

修改“Data Default”存储类 - From autoMBD

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

/* Model step function */void autoMBD_example_PI_StoreClasses_step(void){  real_T rtb_Err;
  /* Sum: '<Root>/Sum2' incorporates:   *  Inport: '<Root>/Feedback'   *  Inport: '<Root>/Req_Ctrl'   */  rtb_Err = autoMBD_example_PI_Sto_Req_Ctrl - autoMBD_example_PI_Sto_Feedback;
  /* Outport: '<Root>/PI_Ctrl' incorporates:   *  DiscreteIntegrator: '<Root>/Discrete-Time Integrator'   *  Gain: '<Root>/Kp'   *  Sum: '<Root>/Sum1'   */  autoMBD_example_PI_Stor_PI_Ctrl = 2.0 * rtb_Err +    a_DiscreteTimeIntegrator_DSTATE;
  /* Update for DiscreteIntegrator: '<Root>/Discrete-Time Integrator' incorporates:   *  Gain: '<Root>/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;/* '<Root>/Feedback' */extern real_T autoMBD_example_PI_Sto_Req_Ctrl;/* '<Root>/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;/* '<Root>/PI_Ctrl' */头文件外部声明如下:
/* 文件:autoMBD_example_PI_StoreClasses.h *//* Declaration for custom storage class: ExportToFile */extern real_T autoMBD_example_PI_Stor_PI_Ctrl;/* '<Root>/PI_Ctrl' */而状态变量设置为尽可能本地化(存储类“Localizable”),即:仅模型代码自己能使用,其他外部代码不能使用。这是通过将状态变量定义为static变量实现的,如下所示:/* 文件:autoMBD_example_PI_StoreClasses.c *//* Definition for custom storage class: Localizable */static real_T a_DiscreteTimeIntegrator_DSTATE;/* '<Root>/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,如下图所示:
MBD的Simulink使用技巧⑩:数据存储类的使用方法w7.jpg

新建两个Memory Section - From autoMBD

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

#pragma Memory Section - From autoMBD

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

attribute Memory Section - From autoMBD
新增的Memory Section中,假设特殊的内存区域被划分为“.fasttextsect”,这需要链接文件真正具有该区域存在,不过链接文件不由Simulink负责。#pragma和attribute有一点区别,前者需要前声明(Pre Statement)和后声明(Post Statement),分别表示开始和结束;而后者只需要前声明。
然后,在“Function Customization Templates”中新建一个自己的函数模板,如下所示:

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

新建函数模板 - From autoMBD
新建的函数模板中,Memory Section选择前面新建的内存区域之一,这里选择的是attribute Memory Secction。函数模板还能定义函数名的格式控制符(关于格式控制符在《第8期(续)》中有介绍),这里也做了修改。最后,在“Code Mappings - C”窗口为Step函数选定函数模板,如下所示:
MBD的Simulink使用技巧⑩:数据存储类的使用方法w11.jpg

为Step函数选择模板 - From autoMBD
这样,生成的Step函数就会包含attribute编译器指令,代码如下:
/* 文件:autoMBD_example_PI_MemSect.c *//* Model step function *//* Memory Section defined by autoMBD, using attribute method. */__attribute__((section(".fasttextsect")))voidctrlPI_step(void){  real_T rtb_Err;
/* Sum: '<Root>/Sum2' incorporates:   *  Inport: '<Root>/Feedback'   *  Inport: '<Root>/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也是可以的,过程也和上述是差不多的,读者可以自行验证、体会。有任何疑问可以和作者交流。

快速发帖

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|手机版|小黑屋|Archiver|汽车工程师之家 ( 渝ICP备18012993号-1 )

GMT+8, 1-5-2024 13:22 , Processed in 0.438504 second(s), 30 queries .

Powered by Discuz! X3.5

© 2001-2013 Comsenz Inc.