主要内容

使用自定义存储类设计器创建存储类

要控制生成代码中数据的外观,您可以使用内置存储类,如 ExportToFile(请参阅使用 Struct 存储类将参数数据组织为结构体)。如果内置存储类不能满足您的需求,您可以创建自己的存储类。请参阅Define Service Interfaces, Storage Classes, Memory Sections, and Function Templates for Software Architecture

要定义可以通过代码映射编辑器在模型中应用的存储类,请使用 Embedded Coder 字典。或者,在 Simulink® 数据类包中创建存储类,并从 Embedded Coder 字典中引用该包。

数据类包包含可以映射到模型接口元素的存储类和内存段定义。数据类包还可以定义 SignalParameter 数据类,您可以从这些数据类创建数据对象,以指定信号、状态和模块参数的值和其他特性。使用数据对象,您可以通过仅更改对象的值来对信号、状态和参数特性进行模型范围的更改。您从包中创建的数据对象可以使用该包定义的存储类。有关详细信息,请参阅数据对象

您可以使用内置在 Simulink 中的包,例如 Simulinkmpt,您也可以使用自定义存储类设计器创建自己的包。

有关使用自定义存储类设计器创建内存段的信息,请参阅Control Data and Function Placement in Memory by Inserting Pragmas

创建并应用在用户定义的包中定义的存储类

此示例说明如何通过在用户定义的数据类包中定义一个存储类并为数据对象应用该存储类来控制从模型生成的代码。

浏览模型示例

打开模型 CustomStorageClasses

open_system('CustomStorageClasses')

在此示例中,您将模型中多个信号和参数的声明与定义导出到一个声明头文件和一个定义文件中。

创建数据类包

要在数据类包中定义存储类,请在该包的 MATLAB 命名空间文件夹中创建一个数据类。

  1. +myPackage 文件夹内导航到文件 Signal.m

  2. 打开 Signal.m 并编辑 Signal 类的定义。取消注释定义方法 setupCoderInfo 的方法部分。在对函数 useLocalCustomeStorageClasses 的调用中,用 'myPackage' 替换 'packName'。函数 useLocalCustomeStorageClasses 允许您将 myPackage 定义的存储类应用于从 myPackage 创建的数据对象。

    methods
        function setupCoderInfo(h)
          % Use custom storage classes from this package
          useLocalCustomStorageClasses(h, 'myPackage');
        end
      end % methods
    
  3. 保存并关闭 Signal.m

  4. +myPackage 文件夹内导航到文件 Parameter.m

  5. 打开 Parameter.m 并进行与步骤 4 中对文件 Signal.m 所做的相同编辑。

  6. 保存并关闭 Parameter.m

有关如何创建一个数据类的详细信息,请参阅定义数据类

在数据类包中创建存储类

使用自定义存储类设计器创建或编辑数据类包定义的存储类。

  1. 检查当前文件夹是否列出 MATLAB 命名空间文件夹 +myPackage

  2. 通过输入 cscdesigner 命令并指定要打开的包的名称来打开自定义存储类设计器。对于此示例,指定 'myPackage'。在 cscdesigner 命令中指定包的命名空间文件夹时,省略 + 字符。

    cscdesigner('myPackage')
    
  3. 在自定义存储类设计器中,在“自定义存储类定义”下,选择存储类 ExportToFile

  4. 对于名称,指定 ExportToGlobal

  5. 对于头文件,选择指定。在出现的新字段中,指定头文件名 global.h

  6. 对于定义文件,选择指定。在出现的新字段中,指定定义文件名 global.c

  7. 点击确定。点击以保存对包 myPackage 的更改。

将存储类应用于数据对象

要将在数据类包中定义的存储类应用于数据对象,请从包中创建数据对象并配置对象以使用存储类。

  1. 创建数据对象以表示示例模型中的一些信号和参数。使用包 myPackage 创建数据对象。

    % Signals
    tempalarm = myPackage.Signal;
    pressurealarm = myPackage.Signal;
    O2alarm = myPackage.Signal;
    rpmalarm = myPackage.Signal;
    
    % Parameters
    templimit = myPackage.Parameter(202);
    pressurelimit = myPackage.Parameter(45.2);
    O2limit = myPackage.Parameter(0.96);
    rpmlimit = myPackage.Parameter(7400);
    
  2. 将每个对象的存储类设置为 ExportToGlobal

    % Signals 
    tempalarm.CoderInfo.StorageClass = 'Custom';
    tempalarm.CoderInfo.CustomStorageClass = 'ExportToGlobal';
    pressurealarm.CoderInfo.StorageClass = 'Custom';
    pressurealarm.CoderInfo.CustomStorageClass = 'ExportToGlobal';
    O2alarm.CoderInfo.StorageClass = 'Custom';
    O2alarm.CoderInfo.CustomStorageClass = 'ExportToGlobal';
    rpmalarm.CoderInfo.StorageClass = 'Custom';
    rpmalarm.CoderInfo.CustomStorageClass = 'ExportToGlobal';
    
    % Parameters
    templimit.CoderInfo.StorageClass = 'Custom';
    templimit.CoderInfo.CustomStorageClass = 'ExportToGlobal';
    pressurelimit.CoderInfo.StorageClass = 'Custom';
    pressurelimit.CoderInfo.CustomStorageClass = 'ExportToGlobal';
    O2limit.CoderInfo.StorageClass = 'Custom';
    O2limit.CoderInfo.CustomStorageClass = 'ExportToGlobal';
    rpmlimit.CoderInfo.StorageClass = 'Custom';
    rpmlimit.CoderInfo.CustomStorageClass = 'ExportToGlobal';
    
  3. 对于您为其创建对象并指定 ExportToGlobal 存储类的每个信号,选择属性信号名称必须解析为 Simulink 信号对象

    % Signal tempalarm
    portHandles = get_param('CustomStorageClasses/RelOp1','PortHandles');
    outputPortHandle = portHandles.Outport;
    set_param(outputPortHandle,'MustResolveToSignalObject','on')
    
    % Signal pressurealarm
    portHandles = get_param('CustomStorageClasses/RelOp2','PortHandles');
    outputPortHandle = portHandles.Outport;
    set_param(outputPortHandle,'MustResolveToSignalObject','on')
    
    % Signal O2alarm
    portHandles = get_param('CustomStorageClasses/RelOp3','PortHandles');
    outputPortHandle = portHandles.Outport;
    set_param(outputPortHandle,'MustResolveToSignalObject','on')
    
    % Signal rpmalarm
    portHandles = get_param('CustomStorageClasses/RelOp4','PortHandles');
    outputPortHandle = portHandles.Outport;
    set_param(outputPortHandle,'MustResolveToSignalObject','on')
  4. 执行更新图以设置您为其创建对象并指定 ExportToGlobal 存储类的参数的值。

生成和检查代码

为示例模型生成代码。

  1. 在模型窗口中,打开 Embedded Coder。

  2. 为示例模型生成代码。

    slbuild('CustomStorageClasses')
    
  3. 在代码视图中,查看生成的头文件 global.h。该文件包含配置为使用 ExportToGlobal 存储类的模型信号和参数的 extern 声明。

    /* Declaration for custom storage class: ExportToGlobal */
    extern boolean_T O2alarm;
    extern real_T O2limit;
    extern boolean_T pressurealarm;
    extern real_T pressurelimit;
    extern boolean_T rpmalarm;
    extern real_T rpmlimit;
    extern boolean_T tempalarm;
    extern real_T templimit;
    
  4. 查看生成的文件 global.c。该文件包含配置为使用 ExportToGlobal 存储类的模型信号和参数的定义。

    /* Definition for custome storage class: ExportToGlobal */
    boolean_T O2alarm;
    real_T O2limit = 0.96;
    boolean_T pressurealarm;
    real_T pressurelimit = 45.2;
    boolean_T rpmalarm;
    real_T rpmlimit = 7400.0;
    boolean_T tempalarm;
    real_T templimit = 202.0;
    

修改内置存储类

您无法直接修改内置存储类,例如 Simulink 包中的 ExportToFile,但您可以创建一个副本,然后修改该副本。如果您创建一个没有 csc_registration.m 文件的新包,然后首次打开自定义存储类设计器,Simulink 会将内置存储类的定义复制到该包中。然后,您可以在自定义存储类设计器中修改复制的定义。或者,为了保持内置存储类在包中可用,请通过点击复制复制其中一个内置存储类,然后修改生成的副本。

通过配置存储类属性控制数据表示

自定义存储类设计器是用于创建和管理存储类和内存段的工具。要为特定包(例如 mypkg)打开自定义存储类设计器,请在命令提示符下使用 cscdesigner 函数:

cscdesigner('mypkg')

包含包的 MATLAB® 命名空间文件夹以 + 开头。当您使用函数 cscdesigner 时,请在包名称中省略 +

要控制某存储类对模型中数据的影响,请在自定义存储类设计器中指定该存储类的属性。要确定如何在生成代码中生成特定类型的数据,请使用下表中的信息。

生成代码中的数据类型方法
结构体类型设置为 FlatStructure。请参阅生成结构化数据
数据初始化设置为并清除对于信号。请参阅生成宏
指针数据访问设置为指针并将数据作用域设置为自动已导入特定于实例。您的外部代码必须定义指针变量。
static 数据(作用域为文件的数据)数据作用域设置为文件
constvolatile 数据内存段设置为指定 constvolatile 或同时指定为两者的内存段。例如,使用内置内存段 MemConstMemVolatileMemConstVolatile 之一。要创建您自己的内存段,请参阅Control Data and Function Placement in Memory by Inserting Pragmas
调用外部函数来读取和写入数据请参阅调用自定义访问器函数或宏而不是读写变量

允许存储类的用户指定属性值

对于存储类的某些属性(例如数据作用域),您可以允许存储类的用户指定属性值,而不是在自定义存储类设计器中指定值。您可以创建一个灵活的存储类,而不是创建多个仅在少数属性值上有所不同的存储类。

例如,假设您创建一个在生成代码中生成全局变量的存储类。您可以配置存储类,以便用户对模型中的一个信号将数据作用域属性设置为已导出,而对另一个信号将该属性设置为已导入

在自定义存储类设计器中,对于要以这种方式配置的每个属性,请将属性值设置为特定于实例。然后,当用户将存储类应用于数据项时,该属性将作为该数据项的自定义特性显示给该用户。有关特定于实例的自定义属性的信息,请参阅Storage Class Properties

生成宏

要创建一个在生成代码中生成宏的存储类,请将数据初始化设置为并清除对于信号

当您将数据初始化设置为时,定义文件没有意义。为了防止存储类的用户指定无意义的定义文件,请在自定义存储类设计器中将定义文件设置为指定,并将文本框留空。

  • 要在头文件 (#define) 中定义宏,请使用头文件属性指定头文件的名称。

  • 要将宏定义作为编译器选项或编译器标志提供,请将数据访问设置为已导入,并将头文件设置为指定。将头文件文本框留空。然后,生成的代码不会定义宏,也不会包含 (#include) 头文件。

    要指定编译器选项或标志,请使用自定义代码模型配置参数定义。请参阅“代码生成”窗格:自定义代码:附加编译信息:定义

生成结构化数据

要创建一个将若干数据项聚合为一个扁平结构体的存储类(类似于内置存储类 Struct),请将类型设置为 FlatStructure结构体属性选项卡将出现。

  • 要控制在生成代码中显示的全局结构体变量的名称,请使用结构体名称属性和文本框。

  • 如果将结构体名称设置为特定于实例,则存储类的用户将指定结构体变量的名称。在这种情况下,代码生成器从变量名称派生结构体类型的名称。

    要更精确地控制结构体类型的名称和其他特性,请将结构体名称设置为指定。使用出现的其他属性来控制类型特性。

  • 要生成具有位字段的结构体(类似于内置存储类 BitField),请选择位压缩布尔值。布尔、定点或整数类型的数据项在生成代码中显示为结构体的位字段。

控制生成代码中的初始值赋值

  • 要使用默认代码生成器初始化策略,请将数据初始化设置为默认值自动

    • 生成的代码在 .c.cpp 源文件的顶部使用赋值语句静态初始化参数数据(在任何函数之外)。

    • 生成的代码将信号和状态数据动态初始化为模型初始化函数的一部分(默认命名为 model_initialize)。

  • 要静态初始化数据,请将数据初始化设置为静态

  • 要动态初始化数据,请将数据初始化设置为动态

  • 要防止生成的代码初始化数据,请将数据初始化设置为。当您的外部代码初始化数据时,请使用此设置以防止生成的代码覆盖您的初始化。此设置不应用于您在 Initialize Function 模块中指定的数据。

调用自定义访问器函数或宏而不是读写变量

使用内置存储类 GetSet,您可以生成调用外部自定义函数的代码,而不是直接与变量交互。有关 GetSet 的一般信息,请参阅Access Data Through Functions with Storage Class GetSet

要创建一个类似的存储类,请将类型设置为 AccessFunction

  • 指定外部函数的名称。

    • 要将相同的 getset 函数命名方案应用于使用存储类的数据项,请将 get 函数set 函数设置为指定。然后,在文本框中指定函数命名方案,例如 get_myData_$N。在命名方案中使用标记 $N 来表示每个数据项的名称。如果不使用标记,则每个数据项使用相同的指定 getset 函数名称,因此在生成代码时模型会生成错误。

    • 要为每个数据项指定不同 getset 函数名称,请将 get 函数set 函数设置为特定于实例。稍后,当您创建一个数据项并应用存储类时,通过配置数据项的自定义属性来指定函数名称。

  • 使用头文件属性指定声明 getset 函数的头文件的名称。在这种情况下,定义文件没有意义。为了防止存储类的用户指定无意义的定义文件,请将定义文件设置为指定,并将文本框留空。

  • 如果将标量或数组数据的 get 机制实现为宏而不是函数,您可以生成在读取该数据时省略圆括号的代码。在访问函数属性选项卡上,选择通过宏获取数据(省略圆括号)

生成使用外部代码中的数据的代码

当您的外部代码定义数据时,为了避免生成重复的定义,您必须配置生成的代码以读取和写入现有数据。在自定义存储类设计器中,将数据作用域设置为已导入。代码生成器不会生成全局变量或宏的定义。

  • 如果您的代码定义一个指针变量,要生成与该指针交互的代码,请将数据访问设置为指针

  • 如果通过将类型设置为 FlatStructure 生成结构化数据,则您的代码必须定义结构体类型和全局结构体变量。

  • 如果您的代码初始化状态或信号数据,您可以通过将数据初始化设置为来防止生成的代码覆盖您的初始化。请参阅Prevent Duplicate Initialization Code for Global Variables

生成与数据定义和声明代码一起显示的注释

要自定义在生成代码中显示的注释,请在注释选项卡上将注释规则设置为指定。要指定与每个数据项的声明一起显示的注释,请使用声明注释框。要指定与定义一起显示的注释,请使用定义注释框。

对于结构化数据(您将类型设置为 FlatStructure),要指定与结构体类型定义一起显示的注释,请使用类型注释框。

通过验证存储类配置避免在代码生成期间出现错误

在自定义存储类设计器中配置存储类时,要检查配置中可能存在的错误,请点击验证

例如,验证存储类会警告您是否在选择对于信号的情况下意外将数据初始化设置为。信号不能在生成代码中显示为宏。

使存储类在当前文件夹之外可用

要使用您创建的一个存储类,您必须使定义包可用。将当前文件夹设置为包含包的命名空间文件夹,或将包含包命名空间文件夹的文件夹添加到 MATLAB 路径(请参阅什么是 MATLAB 搜索路径?)。将包命名空间文件夹添加到 MATLAB 路径后,无论您如何设置当前文件夹,都可以使用该存储类。

使存储类在代码映射编辑器中可用

要在代码映射编辑器中使用基于包的存储类,请配置 Embedded Coder 字典以引用该包,如Refer to Code Generation Definitions in a Package中所述。

在包之间共享存储类

在创建包时,您可以引用在另一个包中定义的存储类,例如内置包 Simulink 或您创建的其他包。然后,定义包和引用包都可以使用该存储类。当您要更改存储类时,只需在定义包中进行一次更改。

要配置包以引用另一个包定义的存储类,请执行以下操作:

  1. 在自定义存储类设计器中,选择自定义存储类选项卡。

  2. 使用选择包以选择引用包。该包必须为可写的。

  3. 自定义存储类定义窗格中,选择要在其下插入引用的现有定义。

  4. 点击新建引用

    具有默认名称和属性的新引用将出现在先前选择的定义下方。新引用被选中,并出现引用选项卡,其中显示初始属性。

  5. 使用名称为新引用指定名称。名称在导入包中必须唯一,但可以与源包中的名称重复。名称不能为 TLC 关键字。

  6. 引用包中的自定义存储类设置为指定包含要引用的存储类的包。

  7. 要引用的自定义存储类设置为指定要引用的存储类。

  8. 点击确定应用。要永久保存更改,请点击保存

控制存储类下拉列表的外观

当您将一个存储类应用于数据项时,您从下拉列表中选择该存储类。

  • 要控制列表中自定义存储类的顺序,请在自定义存储类设计器中使用上移下移按钮。下拉列表中存储类的顺序与您在自定义存储类设计器中指定的顺序一致。

  • 要防止某存储类的用户将该存储类应用于参数数据或信号和状态数据,请使用对于参数对于信号复选框。当您清除这些属性之一时,对于对应的数据类型,存储类不会出现在下拉列表中。

    例如,如果您的存储类在生成代码中生成宏(因为您将数据初始化设置为),要防止用户将该存储类应用于信号数据,请清除对于信号

保护自定义存储类定义

当您在自定义存储类设计器中点击保存时,设计器会将内存段和存储类定义保存到包命名空间文件夹的 csc_registration.m 文件中。要确定此文件的位置,请在自定义存储类设计器中检查文件名的值。

您可以通过将 csc_registration.m 文件从 MATLAB 文件转换为 P 文件来防止对整个包的存储类定义进行更改。使用 pcode 函数。

最佳做法是将 csc_registration.mcsc_registration.p 保留在您的包命名空间文件夹中。这样,如果您使用设计器修改存储类,则可以删除 csc_registration.p,并在完成修改后重新生成它。由于文件的 P 编码版本优先,当包中存在这两个文件时,存储类受到保护,您无法在自定义存储类设计器中修改它们。

通过编写 TLC 代码进一步自定义生成的代码

如果通过操作自定义存储类设计器中的属性创建您自己的存储类无法满足您的需求,您可以通过为存储类编写 TLC 代码来精细控制生成的代码。使用自定义存储类设计器的高级模式,并将存储类的类型设置为其他。请参阅Finely Control Data Representation by Writing TLC Code for a Storage Class

另请参阅

主题