赞
踩
本文讲解用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_M_清零,然后将结束时间设置为10.0,步长为0.2,这2个数据都可以在模型中设置。下面注释了很多内容,推测是把仿真数据输出到文件。
首先是个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的情况,如果强行指定一个步长,那么生成的代码只能按照指定的步长来设置。生成代码时会检查模型中的所有采样时间必须为固定步长的整数倍,若不通过检查,则报错。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。