当前位置:   article > 正文

解析Simulink Coder生成的C代码

simulink coder

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

架构

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

  1. #include <stdio.h>
  2. #include <float.h>
  3. #include <memory.h>
  4. using namespace std;
  5. #define rtmGetFinalTime(rtm) ((rtm)->Timing.tFinal)
  6. #define rtmGetTFinal(rtm) ((rtm)->Timing.tFinal)
  7. #define rtmSetTFinal(rtm, val) ((rtm)->Timing.tFinal = (val))
  8. typedef double time_T;
  9. typedef struct tag_RTM_SingleSubsystem_T RT_MODEL_SingleSubsystem_T;
  10. struct tag_RTM_SingleSubsystem_T
  11. {
  12. const char *errorStatus;
  13. //RTWLogInfo *rtwLogInfo;
  14. /*
  15. * Timing:
  16. * The following substructure contains information regarding
  17. * the timing information for the model.
  18. */
  19. struct {
  20. time_T taskTime0;
  21. unsigned int clockTick0;
  22. unsigned int clockTickH0;
  23. time_T stepSize0;
  24. time_T tFinal;
  25. bool stopRequestedFlag;
  26. } Timing;
  27. };
  28. static RT_MODEL_SingleSubsystem_T SingleSubsystem_M_;
  29. RT_MODEL_SingleSubsystem_T *const SingleSubsystem_M = &SingleSubsystem_M_;
  30. bool finish_flag;
  31. void SingleSubsystem_step(void)
  32. {
  33. /* signal main to stop simulation */
  34. { /* Sample time: [0.2s, 0.0s] */
  35. if ((rtmGetTFinal(SingleSubsystem_M)!=-1) &&
  36. !((rtmGetTFinal(SingleSubsystem_M)-SingleSubsystem_M->Timing.taskTime0) >
  37. SingleSubsystem_M->Timing.taskTime0 * (DBL_EPSILON)))
  38. {
  39. //rtmSetErrorStatus(SingleSubsystem_M, "Simulation finished");
  40. finish_flag = true;
  41. printf("finish\n");
  42. }
  43. }
  44. /* Update absolute time for base rate */
  45. /* The "clockTick0" counts the number of times the code of this task has
  46. * been executed. The absolute time is the multiplication of "clockTick0"
  47. * and "Timing.stepSize0". Size of "clockTick0" ensures timer will not
  48. * overflow during the application lifespan selected.
  49. * Timer of this task consists of two 32 bit unsigned integers.
  50. * The two integers represent the low bits Timing.clockTick0 and the high bits
  51. * Timing.clockTickH0. When the low bit overflows to 0, the high bits increment.
  52. */
  53. if (!(++SingleSubsystem_M->Timing.clockTick0))
  54. {
  55. ++SingleSubsystem_M->Timing.clockTickH0;
  56. }
  57. SingleSubsystem_M->Timing.taskTime0 = SingleSubsystem_M->Timing.clockTick0 *
  58. SingleSubsystem_M->Timing.stepSize0 + SingleSubsystem_M->Timing.clockTickH0 *
  59. SingleSubsystem_M->Timing.stepSize0 * 4294967296.0;
  60. }
  61. /* Model initialize function */
  62. void SingleSubsystem_initialize(void)
  63. {
  64. /* Registration code */
  65. /* initialize non-finites */
  66. //rt_InitInfAndNaN(sizeof(real_T));
  67. // initialize real-time model
  68. (void) memset((void *)SingleSubsystem_M, 0,
  69. sizeof(RT_MODEL_SingleSubsystem_T));
  70. rtmSetTFinal(SingleSubsystem_M, 10.0);
  71. SingleSubsystem_M->Timing.stepSize0 = 0.2;
  72. /* Setup for data logging
  73. {
  74. static RTWLogInfo rt_DataLoggingInfo;
  75. rt_DataLoggingInfo.loggingInterval = (NULL);
  76. SingleSubsystem_M->rtwLogInfo = &rt_DataLoggingInfo;
  77. }*/
  78. /* Setup for data logging
  79. {
  80. rtliSetLogXSignalInfo(SingleSubsystem_M->rtwLogInfo, (NULL));
  81. rtliSetLogXSignalPtrs(SingleSubsystem_M->rtwLogInfo, (NULL));
  82. rtliSetLogT(SingleSubsystem_M->rtwLogInfo, "tout");
  83. rtliSetLogX(SingleSubsystem_M->rtwLogInfo, "");
  84. rtliSetLogXFinal(SingleSubsystem_M->rtwLogInfo, "");
  85. rtliSetLogVarNameModifier(SingleSubsystem_M->rtwLogInfo, "rt_");
  86. rtliSetLogFormat(SingleSubsystem_M->rtwLogInfo, 4);
  87. rtliSetLogMaxRows(SingleSubsystem_M->rtwLogInfo, 0);
  88. rtliSetLogDecimation(SingleSubsystem_M->rtwLogInfo, 1);
  89. rtliSetLogY(SingleSubsystem_M->rtwLogInfo, "");
  90. rtliSetLogYSignalInfo(SingleSubsystem_M->rtwLogInfo, (NULL));
  91. rtliSetLogYSignalPtrs(SingleSubsystem_M->rtwLogInfo, (NULL));
  92. } */
  93. /* external inputs
  94. (void)memset(&SingleSubsystem_U, 0, sizeof(ExtU_SingleSubsystem_T));
  95. /* external outputs
  96. (void)memset(&SingleSubsystem_Y, 0, sizeof(ExtY_SingleSubsystem_T));
  97. */
  98. /* Matfile logging
  99. rt_StartDataLoggingWithStartTime(SingleSubsystem_M->rtwLogInfo, 0.0,
  100. rtmGetTFinal(SingleSubsystem_M), SingleSubsystem_M->Timing.stepSize0,
  101. (&rtmGetErrorStatus(SingleSubsystem_M))); */
  102. }
  103. int main()
  104. {
  105. finish_flag = false;
  106. SingleSubsystem_initialize();
  107. while( !finish_flag )
  108. {
  109. SingleSubsystem_step();
  110. printf( "%d %f\n", SingleSubsystem_M->Timing.clockTick0, SingleSubsystem_M->Timing.taskTime0);
  111. }
  112. return 0;
  113. }

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

SingleSubsystem_initialize

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

SingleSubsystem_step

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

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

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

接下来是

  1. if (!(++SingleSubsystem_M->Timing.clockTick0))
  2. {
  3. ++SingleSubsystem_M->Timing.clockTickH0;
  4. }

这个写法不太好,其实这个语句大部分时候只执行++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函数中会增加如下代码

  1. SingleSubsystem_Y.Out1 = (SingleSubsystem_U.In2 && SingleSubsystem_U.In3);
  2. SingleSubsystem_Y.Out2 = (SingleSubsystem_U.In4 || SingleSubsystem_U.In5);

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

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

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

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

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/735321
推荐阅读
相关标签
  

闽ICP备14008679号