Main Content

本页的翻译已过时。点击此处可查看最新英文版本。

生成的代码如何存储内部信号、状态和参数数据

为了根据输入计算输出,生成的代码会在全局内存中存储一些内部数据。未连接到根级别输入或输出(InportOutport 模块)的信号是内部数据。

内部数据还可以包括:

  • 模块状态,例如 Unit Delay 模块的状态。算法必须保留执行周期之间的状态值,因此生成的代码通常将状态存储在全局内存中(例如,作为全局变量或全局结构体变量的一个字段)。

  • 模块参数,例如 Gain 模块的 Gain 参数,代码生成器不能在代码中内联该参数的值。例如,代码生成器不能内联非标量参数的值。

  • 条件执行子系统(例如使能子系统)的状态指示符。

要获得更高效的代码,您可以配置优化,例如 Configuration Parameters > Default parameter behaviorConfiguration Parameters > Signal storage reuse,这些优化会尝试消除内部数据的存储。但是,优化不能消除某些数据的存储,这会占用生成的代码的内存使用量。

当您了解生成的代码存储内部数据的默认格式时,您可以:

  • 默认情况下,使信号可访问且参数可调。然后,您可以在执行期间与代码进行交互并监视代码。

  • 消除内部数据存储,并根据您的硬件和编译工具链来控制优化不能消除的数据在内存中的位置,从而生成高效的生产代码。

  • 将内部数据段提升到模型接口,以便其他组件和系统可以访问这些数据。

有关生成的代码如何通过接口与调用方环境交换数据的信息,请参阅How Generated Code Exchanges Data with an Environment

生成的代码中的内部数据

此示例说明生成的代码如何存储模块状态等内部数据。

浏览模型示例

打开模型示例 rtwdemo_roll

open_system('rtwdemo_roll')

该模型包含不连接到根级别 Inport 或 Outport 模块的内部信号。某些信号具有名称,例如 phiCmd 信号。

该模型还包含一些维护状态数据的模块。例如,在 BasicRollMode 子系统中,标记为 Integrator 的 Discrete-Time Integrator 模块用于维护状态。

在模型中,将 Configuration Parameters > Code Generation > System target file 设置为 grt.tlc

set_param('rtwdemo_roll','SystemTargetFile','grt.tlc')

检查 Configuration Parameters > Code Generation > Interface > Code interface packaging 的设置。设置 Nonreusable function 表示生成的代码不可重用(可重入)。

对于此示例,通过清除 Configuration Parameters > Code Generation > Interface > Advanced parameters > Mat-file logging 生成更简单的代码。

set_param('rtwdemo_roll','MatFileLogging','off')

生成不可重用的代码

设置以下配置参数:

  • Default parameter behavior 设置为 Tunable

  • 清除 Signal storage reuse

set_param('rtwdemo_roll','DefaultParameterBehavior','Tunable',...
    'OptimizeBlockIOStorage','off')

从模型中生成代码。

slbuild('rtwdemo_roll')
### Starting build procedure for: rtwdemo_roll
### Successful completion of build procedure for: rtwdemo_roll

Build Summary

Top model targets built:

Model         Action                       Rebuild Reason                                    
=============================================================================================
rtwdemo_roll  Code generated and compiled  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 10.748s

文件 rtwdemo_roll.h 定义了几种表示内部数据的结构体类型。例如,模块输入和输出结构体为模型中的每个内部信号定义一个字段。每个字段名称都派生自生成该信号的模块的名称,或者派生自该信号的名称(如果您指定了信号名称)。

file = fullfile('rtwdemo_roll_grt_rtw','rtwdemo_roll.h');
rtwdemodbtype(file,...
    '/* Block signals (default storage) */','} B_rtwdemo_roll_T;',1,1)
/* Block signals (default storage) */
typedef struct {
  real32_T phiCmd;                     /* '<Root>/ModeSwitch' */
  real32_T hdgError;                   /* '<S2>/Sum' */
  real32_T DispGain;                   /* '<S2>/DispGain' */
  real32_T Product;                    /* '<S2>/Product' */
  real32_T Abs;                        /* '<S3>/Abs' */
  real32_T FixPtUnitDelay1;            /* '<S4>/FixPt Unit Delay1' */
  real32_T Xnew;                       /* '<S4>/Enable' */
  real32_T TKSwitch;                   /* '<S3>/TKSwitch' */
  real32_T RefSwitch;                  /* '<S3>/RefSwitch' */
  real32_T Integrator;                 /* '<S1>/Integrator' */
  real32_T DispLimit;                  /* '<S1>/DispLimit' */
  real32_T Sum;                        /* '<S1>/Sum' */
  real32_T DispGain_f;                 /* '<S1>/DispGain' */
  real32_T RateLimit;                  /* '<S1>/RateLimit' */
  real32_T Sum1;                       /* '<S1>/Sum1' */
  real32_T RateGain;                   /* '<S1>/RateGain' */
  real32_T Sum2;                       /* '<S1>/Sum2' */
  real32_T CmdLimit;                   /* '<S1>/CmdLimit' */
  real32_T IntGain;                    /* '<S1>/IntGain' */
  boolean_T NotEngaged;                /* '<S3>/NotEngaged' */
  boolean_T TKThreshold;               /* '<S3>/TKThreshold' */
  boolean_T RefThreshold2;             /* '<S3>/RefThreshold2' */
  boolean_T RefThreshold1;             /* '<S3>/RefThreshold1' */
  boolean_T Or;                        /* '<S3>/Or' */
  boolean_T NotEngaged_e;              /* '<S1>/NotEngaged' */
} B_rtwdemo_roll_T;

该文件定义一种 DWork 结构体类型,用于表示模块状态,例如 Discrete-Time Integrator 模块的状态。

rtwdemodbtype(file,...
    '/* Block states (default storage) for system','} DW_rtwdemo_roll_T;',1,1)
/* Block states (default storage) for system '<Root>' */
typedef struct {
  real32_T FixPtUnitDelay1_DSTATE;     /* '<S4>/FixPt Unit Delay1' */
  real32_T Integrator_DSTATE;          /* '<S1>/Integrator' */
  int8_T Integrator_PrevResetState;    /* '<S1>/Integrator' */
} DW_rtwdemo_roll_T;

该文件定义一种表示参数数据的结构体类型。模型中的每个可调模块参数(例如 Gain 模块的 Gain 参数)显示为此结构体的一个字段。如果模块参数从 MATLAB 变量或 Simulink.Parameter 对象获取其参数值,则该变量或对象显示为字段,而不是模块参数。

该文件还定义一种结构体类型,即实时模型数据结构体,其单个字段表示一种运行时指示,用于指示生成的代码在执行期间是否遇到错误。

rtwdemodbtype(file,'/* Real-time Model Data Structure */',...
    '/* Block parameters (default storage) */',1,0)
/* Real-time Model Data Structure */
struct tag_RTM_rtwdemo_roll_T {
  const char_T *errorStatus;
};

对于表示实时模型数据结构体的结构体类型,文件 rtwdemo_roll_types.h 会创建一个别名,生成的代码稍后将使用该别名为结构体分配内存。

file = fullfile('rtwdemo_roll_grt_rtw','rtwdemo_roll_types.h');
rtwdemodbtype(file,'/* Forward declaration for rtModel */',...
    'RT_MODEL_rtwdemo_roll_T;',1,1)
/* Forward declaration for rtModel */
typedef struct tag_RTM_rtwdemo_roll_T RT_MODEL_rtwdemo_roll_T;

rtwdemo_roll.c 使用这些结构体类型来定义用于为生成的算法存储内部数据的全局结构体变量(为其分配内存)。该文件还定义表示实时模型数据结构体的变量和指向该结构体的指针。

file = fullfile('rtwdemo_roll_grt_rtw','rtwdemo_roll.c');
rtwdemodbtype(file,'/* Block signals (default storage) */',...
    '= &rtwdemo_roll_M_;',1,1)
/* Block signals (default storage) */
B_rtwdemo_roll_T rtwdemo_roll_B;

/* Block states (default storage) */
DW_rtwdemo_roll_T rtwdemo_roll_DW;

/* External inputs (root inport signals with default storage) */
ExtU_rtwdemo_roll_T rtwdemo_roll_U;

/* External outputs (root outports fed by signals with default storage) */
ExtY_rtwdemo_roll_T rtwdemo_roll_Y;

/* Real-time model */
static RT_MODEL_rtwdemo_roll_T rtwdemo_roll_M_;
RT_MODEL_rtwdemo_roll_T *const rtwdemo_roll_M = &rtwdemo_roll_M_;

模型 step 函数(表示主模型算法)使用 void void 接口(不带参数)。

rtwdemodbtype(file,...
    '/* Model step function */','void rtwdemo_roll_step(void)',1,1)
/* Model step function */
void rtwdemo_roll_step(void)

在函数定义中,算法通过直接访问全局变量来执行计算并将中间结果存储在信号和状态结构体中。该算法还从对应的全局变量中读取参数数据。例如,在 BasicRollMode 子系统中,为 Integrator 模块生成的代码在结构体中读取和写入信号、状态和参数数据。

rtwdemodbtype(file,'/* DiscreteIntegrator: ''<S1>/Integrator'' *',...
    '/* End of DiscreteIntegrator: ''<S1>/Integrator'' */',1,1)
  /* DiscreteIntegrator: '<S1>/Integrator' */
  if (rtwdemo_roll_B.NotEngaged_e || (rtwdemo_roll_DW.Integrator_PrevResetState
       != 0)) {
    rtwdemo_roll_DW.Integrator_DSTATE = rtwdemo_roll_P.Integrator_IC;
  }

  if (rtwdemo_roll_DW.Integrator_DSTATE >= rtwdemo_roll_P.intLim) {
    rtwdemo_roll_DW.Integrator_DSTATE = rtwdemo_roll_P.intLim;
  } else if (rtwdemo_roll_DW.Integrator_DSTATE <=
             rtwdemo_roll_P.Integrator_LowerSat) {
    rtwdemo_roll_DW.Integrator_DSTATE = rtwdemo_roll_P.Integrator_LowerSat;
  }

  /* DiscreteIntegrator: '<S1>/Integrator' */
  rtwdemo_roll_B.Integrator = rtwdemo_roll_DW.Integrator_DSTATE;

  /* Saturate: '<S1>/DispLimit' */
  u0 = rtwdemo_roll_B.phiCmd;
  u1 = rtwdemo_roll_P.DispLimit_LowerSat;
  u2 = rtwdemo_roll_P.dispLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/DispLimit' */
    rtwdemo_roll_B.DispLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/DispLimit' */
    rtwdemo_roll_B.DispLimit = u1;
  } else {
    /* Saturate: '<S1>/DispLimit' */
    rtwdemo_roll_B.DispLimit = u0;
  }

  /* End of Saturate: '<S1>/DispLimit' */

  /* Sum: '<S1>/Sum' incorporates:
   *  Inport: '<Root>/Phi'
   */
  rtwdemo_roll_B.Sum = rtwdemo_roll_B.DispLimit - rtwdemo_roll_U.Phi;

  /* Gain: '<S1>/DispGain' */
  rtwdemo_roll_B.DispGain_f = rtwdemo_roll_P.dispGain * rtwdemo_roll_B.Sum;

  /* Saturate: '<S1>/RateLimit' */
  u0 = rtwdemo_roll_B.DispGain_f;
  u1 = rtwdemo_roll_P.RateLimit_LowerSat;
  u2 = rtwdemo_roll_P.rateLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/RateLimit' */
    rtwdemo_roll_B.RateLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/RateLimit' */
    rtwdemo_roll_B.RateLimit = u1;
  } else {
    /* Saturate: '<S1>/RateLimit' */
    rtwdemo_roll_B.RateLimit = u0;
  }

  /* End of Saturate: '<S1>/RateLimit' */

  /* Sum: '<S1>/Sum1' incorporates:
   *  Inport: '<Root>/Rate_FB'
   */
  rtwdemo_roll_B.Sum1 = rtwdemo_roll_B.RateLimit - rtwdemo_roll_U.Rate_FB;

  /* Gain: '<S1>/RateGain' */
  rtwdemo_roll_B.RateGain = rtwdemo_roll_P.rateGain * rtwdemo_roll_B.Sum1;

  /* Sum: '<S1>/Sum2' */
  rtwdemo_roll_B.Sum2 = rtwdemo_roll_B.Integrator + rtwdemo_roll_B.RateGain;

  /* Saturate: '<S1>/CmdLimit' */
  u0 = rtwdemo_roll_B.Sum2;
  u1 = rtwdemo_roll_P.CmdLimit_LowerSat;
  u2 = rtwdemo_roll_P.cmdLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/CmdLimit' */
    rtwdemo_roll_B.CmdLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/CmdLimit' */
    rtwdemo_roll_B.CmdLimit = u1;
  } else {
    /* Saturate: '<S1>/CmdLimit' */
    rtwdemo_roll_B.CmdLimit = u0;
  }

  /* End of Saturate: '<S1>/CmdLimit' */

  /* Gain: '<S1>/IntGain' */
  rtwdemo_roll_B.IntGain = rtwdemo_roll_P.intGain * rtwdemo_roll_B.Sum1;

  /* Update for DiscreteIntegrator: '<S1>/Integrator' */
  rtwdemo_roll_DW.Integrator_DSTATE += rtwdemo_roll_P.Integrator_gainval *
    rtwdemo_roll_B.IntGain;
  if (rtwdemo_roll_DW.Integrator_DSTATE >= rtwdemo_roll_P.intLim) {
    rtwdemo_roll_DW.Integrator_DSTATE = rtwdemo_roll_P.intLim;
  } else if (rtwdemo_roll_DW.Integrator_DSTATE <=
             rtwdemo_roll_P.Integrator_LowerSat) {
    rtwdemo_roll_DW.Integrator_DSTATE = rtwdemo_roll_P.Integrator_LowerSat;
  }

  rtwdemo_roll_DW.Integrator_PrevResetState = (int8_T)
    rtwdemo_roll_B.NotEngaged_e;

  /* End of Update for DiscreteIntegrator: '<S1>/Integrator' */
  /* End of Outputs for SubSystem: '<Root>/BasicRollMode' */

  /* Switch: '<Root>/EngSwitch' incorporates:
   *  Inport: '<Root>/AP_Eng'
   */
  if (rtwdemo_roll_U.AP_Eng) {
    /* Outport: '<Root>/Ail_Cmd' */
    rtwdemo_roll_Y.Ail_Cmd = rtwdemo_roll_B.CmdLimit;
  } else {
    /* Outport: '<Root>/Ail_Cmd' incorporates:
     *  Constant: '<Root>/Zero'
     */
    rtwdemo_roll_Y.Ail_Cmd = rtwdemo_roll_P.Zero_Value_c;
  }

  /* End of Switch: '<Root>/EngSwitch' */
}

/* Model initialize function */
void rtwdemo_roll_initialize(void)
{
  /* Registration code */

  /* initialize error status */
  rtmSetErrorStatus(rtwdemo_roll_M, (NULL));

  /* block I/O */
  (void) memset(((void *) &rtwdemo_roll_B), 0,
                sizeof(B_rtwdemo_roll_T));

  /* states (dwork) */
  (void) memset((void *)&rtwdemo_roll_DW, 0,
                sizeof(DW_rtwdemo_roll_T));

  /* external inputs */
  (void)memset(&rtwdemo_roll_U, 0, sizeof(ExtU_rtwdemo_roll_T));

  /* external outputs */
  rtwdemo_roll_Y.Ail_Cmd = 0.0F;

  /* SystemInitialize for Atomic SubSystem: '<Root>/RollAngleReference' */
  /* InitializeConditions for UnitDelay: '<S4>/FixPt Unit Delay1' */
  rtwdemo_roll_DW.FixPtUnitDelay1_DSTATE = rtwdemo_roll_P.LatchPhi_vinit;

  /* End of SystemInitialize for SubSystem: '<Root>/RollAngleReference' */

  /* SystemInitialize for Atomic SubSystem: '<Root>/BasicRollMode' */
  /* InitializeConditions for DiscreteIntegrator: '<S1>/Integrator' */
  rtwdemo_roll_DW.Integrator_DSTATE = rtwdemo_roll_P.Integrator_IC;
  rtwdemo_roll_DW.Integrator_PrevResetState = 0;

  /* End of SystemInitialize for SubSystem: '<Root>/BasicRollMode' */
}

/* Model terminate function */
void rtwdemo_roll_terminate(void)
{
  /* (no terminate code required) */
}

由于存在 void void 接口和直接数据访问,该函数不可重入。如果在一个应用程序中多次调用该函数,则每次调用都会将数据写入全局结构体变量,后续调用可以读取该数据,从而导致各次调用之间出现意外的干扰。

模型初始化函数 rtwdemo_roll_initialize 将所有内部数据初始化为零。该函数还通过调用专用宏函数对错误状态进行初始化。初始化函数直接访问全局变量,这意味着该函数不可重入。

rtwdemodbtype(file,'/* Model initialize function */',...
    'sizeof(DW_rtwdemo_roll_T));',1,1)
/* Model initialize function */
void rtwdemo_roll_initialize(void)
{
  /* Registration code */

  /* initialize error status */
  rtmSetErrorStatus(rtwdemo_roll_M, (NULL));

  /* block I/O */
  (void) memset(((void *) &rtwdemo_roll_B), 0,
                sizeof(B_rtwdemo_roll_T));

  /* states (dwork) */
  (void) memset((void *)&rtwdemo_roll_DW, 0,
                sizeof(DW_rtwdemo_roll_T));

然后,该函数将 DWork 结构体中的模块状态初始化为模型中的模块参数指定的初始值。模型的三个状态中的两个具有可调初始值,因此代码通过从参数结构体中读取数据来初始化它们。

rtwdemodbtype(file,...
    '/* SystemInitialize for Atomic SubSystem: ''<Root>/RollAngleReference'' */',...
    '/* Model terminate function */',1,0)
  /* SystemInitialize for Atomic SubSystem: '<Root>/RollAngleReference' */
  /* InitializeConditions for UnitDelay: '<S4>/FixPt Unit Delay1' */
  rtwdemo_roll_DW.FixPtUnitDelay1_DSTATE = rtwdemo_roll_P.LatchPhi_vinit;

  /* End of SystemInitialize for SubSystem: '<Root>/RollAngleReference' */

  /* SystemInitialize for Atomic SubSystem: '<Root>/BasicRollMode' */
  /* InitializeConditions for DiscreteIntegrator: '<S1>/Integrator' */
  rtwdemo_roll_DW.Integrator_DSTATE = rtwdemo_roll_P.Integrator_IC;
  rtwdemo_roll_DW.Integrator_PrevResetState = 0;

  /* End of SystemInitialize for SubSystem: '<Root>/BasicRollMode' */
}

生成可重用的代码

您可以将生成的代码配置为可重入代码,这意味着您可以在一个应用程序中多次调用入口函数。使用此配置时,入口函数并不直接访问全局变量,而是通过形参(指针参数)接受内部数据。通过使用这些指针参数,每次调用都可以在一组单独的全局变量中维护内部数据,从而防止各次调用之间出现意外的交互。

在模型中,将 Configuration Parameters > Code Generation > Interface > Code interface packaging 设置为 Reusable function

set_param('rtwdemo_roll','CodeInterfacePackaging','Reusable function')

从模型中生成代码。

slbuild('rtwdemo_roll')
### Starting build procedure for: rtwdemo_roll
### Successful completion of build procedure for: rtwdemo_roll

Build Summary

Top model targets built:

Model         Action                       Rebuild Reason                   
============================================================================
rtwdemo_roll  Code generated and compiled  Generated code was out of date.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 9.203s

现在,在 rtwdemo_roll.h 中,实时模型数据结构体包含指向错误指示的指针、内部数据以及 ExtUExtY 子结构体形式的主输入和输出数据(其字段表示模型根级别的 Inport 和 Outport 模块)。

file = fullfile('rtwdemo_roll_grt_rtw','rtwdemo_roll.h');
rtwdemodbtype(file,'/* Real-time Model Data Structure */',...
    '/* External data declarations for dependent source files */',1,0)
/* Real-time Model Data Structure */
struct tag_RTM_rtwdemo_roll_T {
  const char_T *errorStatus;
  B_rtwdemo_roll_T *blockIO;
  ExtU_rtwdemo_roll_T *inputs;
  ExtY_rtwdemo_roll_T *outputs;
  DW_rtwdemo_roll_T *dwork;
};

/* Block parameters (default storage) */
extern P_rtwdemo_roll_T rtwdemo_roll_P;

要在一个应用程序中多次调用生成的代码,您的代码必须在每次调用时为实时模型数据结构体分配内存。文件 rtwdemo_roll.c 定义一个专用函数,它为新实时模型数据结构体分配内存并返回指向该结构体的指针。该函数还为模型数据结构体中的字段指向的子结构体(例如 DWork 结构体)分配内存。

file = fullfile('rtwdemo_roll_grt_rtw','rtwdemo_roll.c');
rtwdemodbtype(file,'/* Model data allocation function */',...
    'RT_MODEL_rtwdemo_roll_T *rtwdemo_roll(void)',1,1)
/* Model data allocation function */
RT_MODEL_rtwdemo_roll_T *rtwdemo_roll(void)

模型 step 函数接受表示实时模型数据结构体的参数。

rtwdemodbtype(file,'/* Model step function */','void rtwdemo_roll_step',1,1)
/* Model step function */
void rtwdemo_roll_step(RT_MODEL_rtwdemo_roll_T *const rtwdemo_roll_M)

在函数定义中,算法首先将每个指针从实时模型数据结构体中提取到局部变量中。

rtwdemodbtype(file,'*rtwdemo_roll_B =','rtwdemo_roll_M->outputs;',1,1)
  B_rtwdemo_roll_T *rtwdemo_roll_B = rtwdemo_roll_M->blockIO;
  DW_rtwdemo_roll_T *rtwdemo_roll_DW = rtwdemo_roll_M->dwork;
  ExtU_rtwdemo_roll_T *rtwdemo_roll_U = (ExtU_rtwdemo_roll_T *)
    rtwdemo_roll_M->inputs;
  ExtY_rtwdemo_roll_T *rtwdemo_roll_Y = (ExtY_rtwdemo_roll_T *)
    rtwdemo_roll_M->outputs;

然后,为了访问存储在全局内存中的内部数据,该算法与这些局部变量交互。

rtwdemodbtype(file,'/* DiscreteIntegrator: ''<S1>/Integrator'' */',...
    '/* End of DiscreteIntegrator: ''<S1>/Integrator'' */',1,1)
  /* DiscreteIntegrator: '<S1>/Integrator' */
  if (rtwdemo_roll_B->NotEngaged_e ||
      (rtwdemo_roll_DW->Integrator_PrevResetState != 0)) {
    rtwdemo_roll_DW->Integrator_DSTATE = rtwdemo_roll_P.Integrator_IC;
  }

  if (rtwdemo_roll_DW->Integrator_DSTATE >= rtwdemo_roll_P.intLim) {
    rtwdemo_roll_DW->Integrator_DSTATE = rtwdemo_roll_P.intLim;
  } else if (rtwdemo_roll_DW->Integrator_DSTATE <=
             rtwdemo_roll_P.Integrator_LowerSat) {
    rtwdemo_roll_DW->Integrator_DSTATE = rtwdemo_roll_P.Integrator_LowerSat;
  }

  /* DiscreteIntegrator: '<S1>/Integrator' */
  rtwdemo_roll_B->Integrator = rtwdemo_roll_DW->Integrator_DSTATE;

  /* Saturate: '<S1>/DispLimit' */
  u0 = rtwdemo_roll_B->phiCmd;
  u1 = rtwdemo_roll_P.DispLimit_LowerSat;
  u2 = rtwdemo_roll_P.dispLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/DispLimit' */
    rtwdemo_roll_B->DispLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/DispLimit' */
    rtwdemo_roll_B->DispLimit = u1;
  } else {
    /* Saturate: '<S1>/DispLimit' */
    rtwdemo_roll_B->DispLimit = u0;
  }

  /* End of Saturate: '<S1>/DispLimit' */

  /* Sum: '<S1>/Sum' incorporates:
   *  Inport: '<Root>/Phi'
   */
  rtwdemo_roll_B->Sum = rtwdemo_roll_B->DispLimit - rtwdemo_roll_U->Phi;

  /* Gain: '<S1>/DispGain' */
  rtwdemo_roll_B->DispGain_f = rtwdemo_roll_P.dispGain * rtwdemo_roll_B->Sum;

  /* Saturate: '<S1>/RateLimit' */
  u0 = rtwdemo_roll_B->DispGain_f;
  u1 = rtwdemo_roll_P.RateLimit_LowerSat;
  u2 = rtwdemo_roll_P.rateLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/RateLimit' */
    rtwdemo_roll_B->RateLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/RateLimit' */
    rtwdemo_roll_B->RateLimit = u1;
  } else {
    /* Saturate: '<S1>/RateLimit' */
    rtwdemo_roll_B->RateLimit = u0;
  }

  /* End of Saturate: '<S1>/RateLimit' */

  /* Sum: '<S1>/Sum1' incorporates:
   *  Inport: '<Root>/Rate_FB'
   */
  rtwdemo_roll_B->Sum1 = rtwdemo_roll_B->RateLimit - rtwdemo_roll_U->Rate_FB;

  /* Gain: '<S1>/RateGain' */
  rtwdemo_roll_B->RateGain = rtwdemo_roll_P.rateGain * rtwdemo_roll_B->Sum1;

  /* Sum: '<S1>/Sum2' */
  rtwdemo_roll_B->Sum2 = rtwdemo_roll_B->Integrator + rtwdemo_roll_B->RateGain;

  /* Saturate: '<S1>/CmdLimit' */
  u0 = rtwdemo_roll_B->Sum2;
  u1 = rtwdemo_roll_P.CmdLimit_LowerSat;
  u2 = rtwdemo_roll_P.cmdLim;
  if (u0 > u2) {
    /* Saturate: '<S1>/CmdLimit' */
    rtwdemo_roll_B->CmdLimit = u2;
  } else if (u0 < u1) {
    /* Saturate: '<S1>/CmdLimit' */
    rtwdemo_roll_B->CmdLimit = u1;
  } else {
    /* Saturate: '<S1>/CmdLimit' */
    rtwdemo_roll_B->CmdLimit = u0;
  }

  /* End of Saturate: '<S1>/CmdLimit' */

  /* Gain: '<S1>/IntGain' */
  rtwdemo_roll_B->IntGain = rtwdemo_roll_P.intGain * rtwdemo_roll_B->Sum1;

  /* Update for DiscreteIntegrator: '<S1>/Integrator' */
  rtwdemo_roll_DW->Integrator_DSTATE += rtwdemo_roll_P.Integrator_gainval *
    rtwdemo_roll_B->IntGain;
  if (rtwdemo_roll_DW->Integrator_DSTATE >= rtwdemo_roll_P.intLim) {
    rtwdemo_roll_DW->Integrator_DSTATE = rtwdemo_roll_P.intLim;
  } else if (rtwdemo_roll_DW->Integrator_DSTATE <=
             rtwdemo_roll_P.Integrator_LowerSat) {
    rtwdemo_roll_DW->Integrator_DSTATE = rtwdemo_roll_P.Integrator_LowerSat;
  }

  rtwdemo_roll_DW->Integrator_PrevResetState = (int8_T)
    rtwdemo_roll_B->NotEngaged_e;

  /* End of Update for DiscreteIntegrator: '<S1>/Integrator' */
  /* End of Outputs for SubSystem: '<Root>/BasicRollMode' */

  /* Switch: '<Root>/EngSwitch' incorporates:
   *  Inport: '<Root>/AP_Eng'
   */
  if (rtwdemo_roll_U->AP_Eng) {
    /* Outport: '<Root>/Ail_Cmd' */
    rtwdemo_roll_Y->Ail_Cmd = rtwdemo_roll_B->CmdLimit;
  } else {
    /* Outport: '<Root>/Ail_Cmd' incorporates:
     *  Constant: '<Root>/Zero'
     */
    rtwdemo_roll_Y->Ail_Cmd = rtwdemo_roll_P.Zero_Value_c;
  }

  /* End of Switch: '<Root>/EngSwitch' */
}

/* Model initialize function */
void rtwdemo_roll_initialize(RT_MODEL_rtwdemo_roll_T *const rtwdemo_roll_M)
{
  DW_rtwdemo_roll_T *rtwdemo_roll_DW = rtwdemo_roll_M->dwork;

  /* SystemInitialize for Atomic SubSystem: '<Root>/RollAngleReference' */
  /* InitializeConditions for UnitDelay: '<S4>/FixPt Unit Delay1' */
  rtwdemo_roll_DW->FixPtUnitDelay1_DSTATE = rtwdemo_roll_P.LatchPhi_vinit;

  /* End of SystemInitialize for SubSystem: '<Root>/RollAngleReference' */

  /* SystemInitialize for Atomic SubSystem: '<Root>/BasicRollMode' */
  /* InitializeConditions for DiscreteIntegrator: '<S1>/Integrator' */
  rtwdemo_roll_DW->Integrator_DSTATE = rtwdemo_roll_P.Integrator_IC;
  rtwdemo_roll_DW->Integrator_PrevResetState = 0;

  /* End of SystemInitialize for SubSystem: '<Root>/BasicRollMode' */
}

/* Model terminate function */
void rtwdemo_roll_terminate(RT_MODEL_rtwdemo_roll_T * rtwdemo_roll_M)
{
  /* model code */
  rt_FREE(rtwdemo_roll_M->blockIO);
  rt_FREE(rtwdemo_roll_M->inputs);
  rt_FREE(rtwdemo_roll_M->outputs);
  rt_FREE(rtwdemo_roll_M->dwork);
  rt_FREE(rtwdemo_roll_M);
}

/* Model data allocation function */
RT_MODEL_rtwdemo_roll_T *rtwdemo_roll(void)
{
  RT_MODEL_rtwdemo_roll_T *rtwdemo_roll_M;
  rtwdemo_roll_M = (RT_MODEL_rtwdemo_roll_T *) malloc(sizeof
    (RT_MODEL_rtwdemo_roll_T));
  if (rtwdemo_roll_M == NULL) {
    return NULL;
  }

  (void) memset((char *)rtwdemo_roll_M, 0,
                sizeof(RT_MODEL_rtwdemo_roll_T));

  /* block I/O */
  {
    B_rtwdemo_roll_T *b = (B_rtwdemo_roll_T *) malloc(sizeof(B_rtwdemo_roll_T));
    rt_VALIDATE_MEMORY(rtwdemo_roll_M,b);
    rtwdemo_roll_M->blockIO = (b);
  }

  /* states (dwork) */
  {
    DW_rtwdemo_roll_T *dwork = (DW_rtwdemo_roll_T *) malloc(sizeof
      (DW_rtwdemo_roll_T));
    rt_VALIDATE_MEMORY(rtwdemo_roll_M,dwork);
    rtwdemo_roll_M->dwork = (dwork);
  }

  /* external inputs */
  {
    ExtU_rtwdemo_roll_T *rtwdemo_roll_U = (ExtU_rtwdemo_roll_T *) malloc(sizeof
      (ExtU_rtwdemo_roll_T));
    rt_VALIDATE_MEMORY(rtwdemo_roll_M,rtwdemo_roll_U);
    rtwdemo_roll_M->inputs = (((ExtU_rtwdemo_roll_T *) rtwdemo_roll_U));
  }

  /* external outputs */
  {
    ExtY_rtwdemo_roll_T *rtwdemo_roll_Y = (ExtY_rtwdemo_roll_T *) malloc(sizeof
      (ExtY_rtwdemo_roll_T));
    rt_VALIDATE_MEMORY(rtwdemo_roll_M,rtwdemo_roll_Y);
    rtwdemo_roll_M->outputs = (rtwdemo_roll_Y);
  }

  {
    B_rtwdemo_roll_T *rtwdemo_roll_B = rtwdemo_roll_M->blockIO;
    DW_rtwdemo_roll_T *rtwdemo_roll_DW = rtwdemo_roll_M->dwork;
    ExtU_rtwdemo_roll_T *rtwdemo_roll_U = (ExtU_rtwdemo_roll_T *)
      rtwdemo_roll_M->inputs;
    ExtY_rtwdemo_roll_T *rtwdemo_roll_Y = (ExtY_rtwdemo_roll_T *)
      rtwdemo_roll_M->outputs;

    /* block I/O */
    (void) memset(((void *) rtwdemo_roll_B), 0,
                  sizeof(B_rtwdemo_roll_T));

    /* states (dwork) */
    (void) memset((void *)rtwdemo_roll_DW, 0,
                  sizeof(DW_rtwdemo_roll_T));

    /* external inputs */
    (void)memset(rtwdemo_roll_U, 0, sizeof(ExtU_rtwdemo_roll_T));

    /* external outputs */
    rtwdemo_roll_Y->Ail_Cmd = 0.0F;
  }

  return rtwdemo_roll_M;
}

同样,模型初始化函数接受实时模型数据结构体作为参数。

rtwdemodbtype(file,...
    '/* Model initialize function */','void rtwdemo_roll_initialize',1,1)
/* Model initialize function */
void rtwdemo_roll_initialize(RT_MODEL_rtwdemo_roll_T *const rtwdemo_roll_M)

由于您对入口函数的每次调用都与一个单独的实时模型数据结构体交互,因此可以避免各次调用之间发生意外交互。

使用代码生成优化消除内部数据

为了获得消耗更少内存的更高效代码,请选择优化,例如您在前面清除了的 Default parameter behavior

set_param('rtwdemo_roll','DefaultParameterBehavior','Inlined',...
    'OptimizeBlockIOStorage','on',...
    'LocalBlockOutputs','on')

在此示例中,为了获得更简单的代码,请将 Code interface packaging 设置为 Nonreusable function

set_param('rtwdemo_roll','CodeInterfacePackaging','Nonreusable function')

从模型中生成代码。

slbuild('rtwdemo_roll')
### Starting build procedure for: rtwdemo_roll
### Successful completion of build procedure for: rtwdemo_roll

Build Summary

Top model targets built:

Model         Action                       Rebuild Reason                   
============================================================================
rtwdemo_roll  Code generated and compiled  Generated code was out of date.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 8.078s

现在,rtwdemo_roll.h 没有定义用于模块输入和输出的结构体。对于模型中的所有内部信号,优化要么已消除了存储,要么创建了局部函数变量而不是全局结构体字段。

优化未能消除三种模块状态的存储,因此文件继续定义 DWork 结构体类型。

file = fullfile('rtwdemo_roll_grt_rtw','rtwdemo_roll.h');
rtwdemodbtype(file,...
    '/* Block states (default storage) for system','} DW_rtwdemo_roll_T;',1,1)
/* Block states (default storage) for system '<Root>' */
typedef struct {
  real32_T FixPtUnitDelay1_DSTATE;     /* '<S4>/FixPt Unit Delay1' */
  real32_T Integrator_DSTATE;          /* '<S1>/Integrator' */
  int8_T Integrator_PrevResetState;    /* '<S1>/Integrator' */
} DW_rtwdemo_roll_T;

现在,为 Discrete-Time Integrator 模块生成的代码仅在 DWork 结构体中存储状态和输出数据。

file = fullfile('rtwdemo_roll_grt_rtw','rtwdemo_roll.c');
rtwdemodbtype(file,'/* Update for DiscreteIntegrator: ''<S1>/Integrator''',...
    '/* End of Update for DiscreteIntegrator: ''<S1>/Integrator'' */',1,1)
  /* Update for DiscreteIntegrator: '<S1>/Integrator' incorporates:
   *  Gain: '<S1>/IntGain'
   */
  rtwdemo_roll_DW.Integrator_DSTATE += 0.5F * rtb_TKSwitch * 0.025F;
  if (rtwdemo_roll_DW.Integrator_DSTATE >= 5.0F) {
    rtwdemo_roll_DW.Integrator_DSTATE = 5.0F;
  } else if (rtwdemo_roll_DW.Integrator_DSTATE <= -5.0F) {
    rtwdemo_roll_DW.Integrator_DSTATE = -5.0F;
  }

  rtwdemo_roll_DW.Integrator_PrevResetState = (int8_T)rtb_NotEngaged_f;

优化还消除了模型中模块参数的存储。例如,在 Discrete-Time Integrator 模块中,Upper saturation limitLower saturation limit 参数设置为 intLim-intLimintLimSimulink.Parameter 对象,用于存储值 5。在为 Discrete-Time Integrator 生成的代码中,这些模块参数和 intLim 显示为内联字面数字 5.0F-5.0F

如果模型包含代码生成器不能直接内联的参数(例如数组参数),则代码会定义表示该数据的结构体类型。此常量参数结构体使用 const 存储类型限定符,因此某些编译工具链可以进一步优化程序集代码。

生成的代码中的局部变量

当您选择 Configuration Parameters > Enable local block outputs 优化时,代码生成器会尝试通过将内部信号表示为局部变量(而不是全局结构体的字段)来生成更高效的代码。如果局部变量消耗的内存有超过目标硬件上的可用堆栈空间的风险,请考虑通过设置 Configuration Parameters > Maximum stack size (bytes) 来指示最大堆栈大小。有关详细信息,请参阅Maximum stack size (bytes)

生成的代码中测试点的外观

测试点是存储在唯一内存位置的信号。有关在模型中包含测试点的信息,请参阅将信号配置为测试点

为包含测试点的模型生成代码时,编译过程会为每个测试点分配单独的内存缓冲区。默认情况下,测试点存储为标准数据结构体的成员,例如 model_B

如果您有 Embedded Coder®

  • 通过在 Code Mapping Editor 中为 Internal data 类别的数据指定代码生成设置,您可以控制测试点的默认表示形式(请参阅Configure Default Code Generation for Data (Embedded Coder))。

  • 您可以使用 Ignore test point signals (Embedded Coder) 参数指定编译过程忽略模型中的测试点,从而允许最佳缓冲区分配。忽略测试点可加快从原型构建到部署的过程,并避免由于工作流的产物而导致生成的代码出现意外的性能降级。请参阅Ignore test point signals (Embedded Coder)

虚拟总线不显示在生成的代码中,即使与测试点相关联时也是如此。要在生成的代码中显示总线,请使用非虚拟总线或使用通过 Signal Conversion 模块转换为非虚拟总线的虚拟总线。

生成的代码中工作区变量的外观

您可以使用工作区变量来指定模型中的模块参数值。工作区变量包括您存储在工作区(例如基础工作区)或数据字典中的 MATLAB® 数值变量和 Simulink.Parameter 对象。

Default parameter behavior 设置为 “Tunable” 时,默认情况下,工作区变量在生成的代码中显示为全局参数结构体的可调字段。如果使用一个这样的变量指定多个模块参数值,则该变量显示为全局参数结构体的单个字段。代码不会创建多个字段来表示模块参数。因此,在代码执行期间调整字段值会更改模型的数学行为,就像在仿真期间调整 MATLAB 变量或参数对象的值一样。

如果您有 Embedded Coder,则可以通过在 Code Mapping Editor 中指定参数数据类别的代码生成设置来控制工作区变量的默认表示形式(请参阅Configure Default Code Generation for Data (Embedded Coder))。

  • Model parameters 类别适用于存储在模型工作区中的变量。

  • External parameters 类别适用于存储在基础工作区或数据字典中的变量。

将内部数据提升到接口

默认情况下,代码生成器假定应用程序中的其他系统和组件不需要访问内部数据。例如,代码生成器会对内部数据进行优化,以从生成的代码中消除它们。对于原型构建和测试目的,您可以通过清除优化或配置测试点并应用存储类来访问内部数据(请参阅Preserve Variables in Generated Code)。对于优化的生产代码,将单个数据项配置为作为模型接口的一部分显示在生成的代码中。

您可以提升的数据

根据生成的代码的重入性,即您为 Code interface packaging 选择的设置,您可以通过将模型中的每个数据项配置为在代码中显示为下列实体之一来参与接口:

  • 全局符号,例如全局变量或对专用函数的调用

  • 生成的入口函数的形参(参数)

下表显示了每个数据类别参与接口时可使用的机制。

数据类别显示为全局符号显示为入口函数的参数
根级别 InportOutport 模块仅适用于非重入模型。是。
连接两个模块的信号仅适用于非重入模型。

仅适用于可重入模型,且仅作为结构体字段。

或者,将信号连接到根级别 Outport 模块。

模块状态仅适用于非重入模型。仅适用于可重入模型,且仅作为结构体字段。
数据存储,例如 Data Store Memory 模块是。仅适用于可重入模型,且仅作为结构体字段。
模块参数或参数对象,例如 Simulink.Parameter是。仅作为结构体的字段。

单实例算法

对于单实例算法(将 Code interface packaging 设置为 “Nonreusable function”),使用模型数据编辑器或 Property Inspector 将存储类直接应用于单个数据项。使用直接应用的存储类时,数据项在代码中显示为全局符号,例如全局变量。存储类还可以防止优化消除数据项的存储。

您可以将存储类应用于信号、模块状态和模块参数。(对于模块参数,可以通过 Simulink.Parameter 等参数对象间接应用存储类)。但是,对于信号,请考虑将信号连接到模型根级别的 Outport 模块。然后,您可以选择将存储类应用于模块。在模块图中,Outport 模块显示信号表示系统输出。

有关存储类的详细信息,请参阅模型接口元素的 C 代码生成配置

可重入算法

对于可重入算法(将 Code interface packaging 设置为 “Reusable function”),可使用不同方法将数据项配置为在代码中体现为生成的入口函数的形参。

  • 对于内部信号,可直接应用存储类 Model default(请参阅模型接口元素的 C 代码生成配置)。如果您有 Embedded Coder,在 Code Mappings 编辑器中,对于 Internal data 类别,请将默认存储类设置为 “Default” 或者设置为您在 Embedded Coder 字典中定义的结构化存储类(请参阅Create Code Definitions for Use in the Code Mappings Editor (Embedded Coder))。或者,将信号配置为测试点(请参阅生成的代码中测试点的外观)。默认情况下,信号显示为标准数据结构体之一的字段(请参阅生成的代码如何存储内部信号、状态和参数数据)。如果您不希望信号显示在生产代码中,请使用测试点,以便稍后选择模型配置参数 Ignore test point signals

    或者,将信号连接到模型根级别的 Outport 模块。将信号连接到根级别 Outport 模块可防止优化从代码中消除信号。为了帮助在大型模型中进行信号路由,请使用 GotoFrom 模块。

  • 对于模块参数,请创建一个参数对象,例如 Simulink.Parameter,并直接将 Auto 之外的存储类应用于该对象。存储类可防止优化在代码中内联参数值。

    使用存储类时,会在模型的实例之间共享该参数,这些实例是对入口函数的调用。这些函数以全局符号而不是实参形式直接访问参数数据。您不能将参数配置为在代码中体现为实参,因此您无法使模型的每个实例都使用不同的参数值。

    有关应用存储类的信息,请参阅模型接口元素的 C 代码生成配置

控制内部数据的默认表示形式 (Embedded Coder)

默认情况下,代码生成器会将优化无法消除的内部数据(如大多数状态数据)聚合到 DWork 结构体等标准结构体中。使用 Embedded Coder,您可以控制生成的代码存储这些数据的方式。

通过插入 pragma 指令控制数据在内存中的位置

使用 Code Mapping Editor 为每个类别的数据(例如属于内部数据的状态和信号数据)指定默认内存段。在生成的代码中,您的自定义 pragma 指令或其他装饰元素位于数据定义和声明的前后位置。

您还可以根据模型中的原子子系统对结构体进行分区,以便为子例程和其他算法子组件的数据指定不同的默认内存段。

有关详细信息,请参阅Control Data and Function Placement in Memory by Inserting Pragmas (Embedded Coder)

控制标准数据结构体的类型、字段和全局变量的名称

您可以控制标准数据结构体的某些特性。有关详细信息,请参阅Control Data Type Names in Generated Code (Embedded Coder)

要对结构体特性施加更多控制,例如在生成的代码文件中的位置,请使用 Embedded Coder Dictionary 创建自己的结构化存储类。然后,使用 Code Mapping Editor 将存储类应用于数据类别。存储类会删除标准结构体中的数据,从而创建您可以施加更多控制的其他结构体。有关将默认存储类应用于数据类别的详细信息,请参阅Configure Default Code Generation for Data (Embedded Coder)。有关创建存储类的详细信息,请参阅Define Storage Classes, Memory Sections, and Function Templates for Software Architecture (Embedded Coder)

根据子组件将数据组织到结构体中

  • 在标准数据结构体中,要创建包含单实例(非重入)子例程或子组件的数据的子结构体,请使用原子子系统来封装对应的模块。在子系统参数中,将 Function packaging 设置为 “Reusable function”。有关详细信息,请参阅为非虚拟子系统生成模块化函数代码 (Embedded Coder)

    或者,将模块封装在模型中并使用 Model 模块。在引用模型中,将 Configuration Parameters > Model Referencing > Total number of instances allowed per top model 设置为 “Multiple”。有关详细信息,请参阅Generate Code for Model Reference Hierarchy

  • 要在模型中创建包含多实例(可重入)子例程或子组件的数据的单独独立结构体,请使用原子子系统来封装对应的模块。在子系统参数中,将 Function packaging 设置为 “Nonreusable function”,然后选择 Function with separate data。有关详细信息,请参阅为非虚拟子系统生成模块化函数代码 (Embedded Coder)

    或者,将模块封装在模型中并使用 Model 模块。在引用模型中,选择以下方法之一:

将信号和参数数据组织成有意义的自定义结构体和子结构体

要将任意信号和参数组织成自定义结构体和子结构体,请创建非虚拟总线信号和参数结构体。(可选)要防止优化消除代码中的数据,请将总线信号或参数结构体的存储类设置为 “Auto”(默认设置)以外的值。

在向模型添加模块时,必须将每个新信号和参数显式放入总线或结构体中。

有关详细信息,请参阅Organize Data into Structures in Generated Code

创建单独的全局变量而不是结构体字段

要使内部数据的类别在生成的代码中显示为单独的非结构化全局变量而不是标准数据结构体的字段,请使用 Code Mapping Editor 将非结构化存储类应用于数据类别。例如,应用存储类 ExportedGlobal。但是,如果通过将配置参数 Code interface packaging 设置为 “Nonreusable function” 以外的值来生成多实例可重入代码,则不能将此方法用于某些数据类别(请参阅Storage Classes and Reentrant, Multi-Instance Models and Components)。

要使用 Code Mapping Editor 将默认存储类应用于数据类别,请参阅Configure Default Code Generation for Data (Embedded Coder)。要选择存储类,请参阅Choose Storage Class for Controlling Data Representation in Generated Code

相关主题