解析Simulink Coder生成的C代码

本文讲解用Simulink Coder生成的C代码

架构

首先撇开模型内容,看生成的C代码的框架。为了方便读者测试,我把关键的2个函数组装到了1个程序中,代码如下:


#include <stdio.h>
#include <float.h>
#include <memory.h>
using namespace std;

#define rtmGetFinalTime(rtm)           ((rtm)->Timing.tFinal)
#define rtmGetTFinal(rtm)              ((rtm)->Timing.tFinal)
#define rtmSetTFinal(rtm, val)         ((rtm)->Timing.tFinal = (val))

typedef double time_T;
typedef struct tag_RTM_SingleSubsystem_T RT_MODEL_SingleSubsystem_T;

struct tag_RTM_SingleSubsystem_T 
{
  const char *errorStatus;
  //RTWLogInfo *rtwLogInfo;

  /*
   * Timing:
   * The following substructure contains information regarding
   * the timing information for the model.
   */
  struct {
    time_T taskTime0;
    unsigned int clockTick0;
    unsigned int clockTickH0;
    time_T stepSize0;
    time_T tFinal;
    bool stopRequestedFlag;
  } Timing;
};

static RT_MODEL_SingleSubsystem_T SingleSubsystem_M_;
RT_MODEL_SingleSubsystem_T *const SingleSubsystem_M = &SingleSubsystem_M_;

bool finish_flag;


void SingleSubsystem_step(void)
{

  /* signal main to stop simulation */
  {                                    /* Sample time: [0.2s, 0.0s] */
    if ((rtmGetTFinal(SingleSubsystem_M)!=-1) &&
        !((rtmGetTFinal(SingleSubsystem_M)-SingleSubsystem_M->Timing.taskTime0) >
          SingleSubsystem_M->Timing.taskTime0 * (DBL_EPSILON))) 
	{
      //rtmSetErrorStatus(SingleSubsystem_M, "Simulation finished");
	  finish_flag = true;
	  printf("finish\n");
    }
  }

  /* Update absolute time for base rate */
  /* The "clockTick0" counts the number of times the code of this task has
   * been executed. The absolute time is the multiplication of "clockTick0"
   * and "Timing.stepSize0". Size of "clockTick0" ensures timer will not
   * overflow during the application lifespan selected.
   * Timer of this task consists of two 32 bit unsigned integers.
   * The two integers represent the low bits Timing.clockTick0 and the high bits
   * Timing.clockTickH0. When the low bit overflows to 0, the high bits increment.
   */
  if (!(++SingleSubsystem_M->Timing.clockTick0)) 
  {
    ++SingleSubsystem_M->Timing.clockTickH0;
  }

  SingleSubsystem_M->Timing.taskTime0 = SingleSubsystem_M->Timing.clockTick0 *
    SingleSubsystem_M->Timing.stepSize0 + SingleSubsystem_M->Timing.clockTickH0 *
    SingleSubsystem_M->Timing.stepSize0 * 4294967296.0;
    
}

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

  /* initialize non-finites */
  //rt_InitInfAndNaN(sizeof(real_T));

  // initialize real-time model 
  (void) memset((void *)SingleSubsystem_M, 0,
                sizeof(RT_MODEL_SingleSubsystem_T));

  rtmSetTFinal(SingleSubsystem_M, 10.0);
  SingleSubsystem_M->Timing.stepSize0 = 0.2;

  /* Setup for data logging 
  {
    static RTWLogInfo rt_DataLoggingInfo;
    rt_DataLoggingInfo.loggingInterval = (NULL);
    SingleSubsystem_M->rtwLogInfo = &rt_DataLoggingInfo;
  }*/

  /* Setup for data logging 
  {
    rtliSetLogXSignalInfo(SingleSubsystem_M->rtwLogInfo, (NULL));
    rtliSetLogXSignalPtrs(SingleSubsystem_M->rtwLogInfo, (NULL));
    rtliSetLogT(SingleSubsystem_M->rtwLogInfo, "tout");
    rtliSetLogX(SingleSubsystem_M->rtwLogInfo, "");
    rtliSetLogXFinal(SingleSubsystem_M->rtwLogInfo, "");
    rtliSetLogVarNameModifier(SingleSubsystem_M->rtwLogInfo, "rt_");
    rtliSetLogFormat(SingleSubsystem_M->rtwLogInfo, 4);
    rtliSetLogMaxRows(SingleSubsystem_M->rtwLogInfo, 0);
    rtliSetLogDecimation(SingleSubsystem_M->rtwLogInfo, 1);
    rtliSetLogY(SingleSubsystem_M->rtwLogInfo, "");
    rtliSetLogYSignalInfo(SingleSubsystem_M->rtwLogInfo, (NULL));
    rtliSetLogYSignalPtrs(SingleSubsystem_M->rtwLogInfo, (NULL));
  } */

  /* external inputs
  (void)memset(&SingleSubsystem_U, 0, sizeof(ExtU_SingleSubsystem_T));

  /* external outputs 
  (void)memset(&SingleSubsystem_Y, 0, sizeof(ExtY_SingleSubsystem_T));
  */

  /* Matfile logging 
  rt_StartDataLoggingWithStartTime(SingleSubsystem_M->rtwLogInfo, 0.0,
    rtmGetTFinal(SingleSubsystem_M), SingleSubsystem_M->Timing.stepSize0,
    (&rtmGetErrorStatus(SingleSubsystem_M))); */
}

int main()
{
	finish_flag = false;
  SingleSubsystem_initialize();
	while( !finish_flag )
	{
		SingleSubsystem_step();
    printf( "%d %f\n", SingleSubsystem_M->Timing.clockTick0, SingleSubsystem_M->Timing.taskTime0);
	}
	return 0;
}

关键的2个函数是SingleSubsystem_step, SingleSubsystem_initialize. 前者是仿真的每一步要执行的函数,后者是初始化函数。以上代码列出并不是直接从Simulink生成的代码中复制过来的,有一点点修改。首先SingleSubsystem_M_ 全局变量,保存仿真过程中的模型整体数据,其实就是为了控制模型的仿真步数。

SingleSubsystem_initialize

    这个函数先把SingleSubsystem_M_清零,然后将结束时间设置为10.0,步长为0.2,这2个数据都可以在模型中设置。下面注释了很多内容,推测是把仿真数据输出到文件。

SingleSubsystem_step

    首先是个if语句,条件rtmGetTFinal(SingleSubsystem_M)!=-1显得多余。条件

!((rtmGetTFinal(SingleSubsystem_M)-SingleSubsystem_M->Timing.taskTime0) >
          SingleSubsystem_M->Timing.taskTime0 * (DBL_EPSILON))

判断仿真过程目前时间taskTime0是否超过结束时间tFinal. 里面有DBL_EPSILON, 是1个很接近0的浮点数,数量级为10^(-16). if语句条件为真时就应该结束仿真。

接下来是

if (!(++SingleSubsystem_M->Timing.clockTick0)) 
  {
    ++SingleSubsystem_M->Timing.clockTickH0;
  }

这个写法不太好,其实这个语句大部分时候只执行++SingleSubsystem_M->Timing.clockTick0. 但是如果让clockTick0为-1或者说unsigned int的最大值时 ,就会执行if中的语句。个人推测这是为了防止执行的步数超过unsigned int的最大值。注意clockTickH0和clockTick0是不同变量。clockTickH0几乎用不到,但是它只要加1,几乎就意味着仿真结束。原因看Step函数中最后的语句,clockTickH0将乘以4294967296.0,1个极大的权重。

这个样例结束时间为10,步长0.2,所以程序会运行51次Step函数(最后1次Step函数中判断仿真时间超过tFinal). 

模型内容

根据模型内容,生成代码除了上述架构,还会在Step函数和Initialize函数中增加一些代码。下面是一个模型例子

在我的例子中只有1个或门、内部是与门的子系统和相应的Inport, Outport. 所以在Step函数中会增加如下代码

 SingleSubsystem_Y.Out1 = (SingleSubsystem_U.In2 && SingleSubsystem_U.In3);

 SingleSubsystem_Y.Out2 = (SingleSubsystem_U.In4 || SingleSubsystem_U.In5);

直到现在模型里面并没有设置采样时间,一切都是用默认参数。那么如果我们修改采样时间,看看生成的代码有何变化。在子系统中

 设置In1的采样时间为0.2, Out1的采样时间为0.8,则生成代码会多出一个函数


static void rate_scheduler(void)
{
  /* Compute which subrates run during the next base time step.  Subrates
   * are an integer multiple of the base rate counter.  Therefore, the subtask
   * counter is reset when it reaches its limit (zero means run).
   */
  (SingleSubsystem_M->Timing.TaskCounters.TID[1])++;
  if ((SingleSubsystem_M->Timing.TaskCounters.TID[1]) > 3) {/* Sample time: [0.8s, 0.0s] */
    SingleSubsystem_M->Timing.TaskCounters.TID[1] = 0;
  }
}

这个if语句的3其实就是0.8/0.2 - 1 =3,也就是TID[1]的变化范围为{0,1,2,3}共4个数。rate_scheduler函数会在step函数的末尾执行。TID可以理解为TaskID,如果多增加一些复杂系统,则会增加TID数组的size。而在step函数的开头计算各个模块的值的部分也有所改变,此时不能看成1个简单的与门运算,代码为

SingleSubsystem_B.and = (SingleSubsystem_U.In1 && SingleSubsystem_U.In2);
  if (SingleSubsystem_M->Timing.TaskCounters.TID[1] == 0) 
{
    /* Outport: '<Root>/Out1' */
    SingleSubsystem_Y.Out1 = SingleSubsystem_B.and;
}

这里SingleSubsystem_B存储与Subsystem模块相关的信息。由于输出、输入的采样时间不一致,所以与门的值需要在TID[1]==0时赋予输出端。

如果我们把输出端采样时间改为0.3,那么0.3/0.2不是整数,会发生什么?此时生成代码会把步长设置为0.1,相当于取了2, 3的公因数1. 这是在模型参数配置->求解器->定步长中设置auto的情况,如果强行指定一个步长,那么生成的代码只能按照指定的步长来设置。生成代码时会检查模型中的所有采样时间必须为固定步长的整数倍,若不通过检查,则报错。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值