见《开箱报告,Simulink Toolbox库模块使用指南(一)——powergui模块》
见《开箱报告,Simulink Toolbox库模块使用指南(二)——MATLAB Fuction模块》
见《开箱报告,Simulink Toolbox库模块使用指南(三)——Simscape 电路仿真模块》
见《开箱报告,Simulink Toolbox库模块使用指南(四)——S-Fuction模块》
见《开箱报告,Simulink Toolbox库模块使用指南(五)——S-Fuction模块(C MEX S-Function)》
见《开箱报告,Simulink Toolbox库模块使用指南(六)——S-Fuction模块(TLC)》
S-Fuction Builter模块,Mathworks官方Help对该部分内容的说明如下所示。
DFT算法的原理讲解和模块开发在前几篇文章中已经完成了,本文介绍如何使用S-Fuction Builter模块一步到位地自动开发DFT算法模块,包括建立C MEX S-Function文件、TLC文件和编译生成等工作。
第一步是模块配置,包括S-Fuction的名字、开发语言、输入输出端口、状态变量和固定参数等,如下图所示:
第二步是编写Update_wrapper()函数,沿用前期C MEX S-Function的代码,使用自动生成的函数接口,写出的Update_wrapper()函数如下:
- void DFT_CMexSfuncBuilder_Update_wrapper(const real_T *u0,
- real_T *y0,
- real_T *xD,
- const real_T *Freq, const int_T p_width0)
- {
- /* Update_BEGIN */
- static real_T Fs = 10e3;
- static real_T L = 5e3;
-
- real_T Sr_cos;
- real_T Sr_sin;
- real_T T;
-
- if(xD[0]<=L)
- {
- Sr_cos = cos(2*3.14*xD[4]*xD[1]);
- Sr_sin = sin(2*3.14*xD[4]*xD[1]);
- xD[2] = xD[2] + u0[0]*Sr_cos;
- xD[3] = xD[3] + u0[0]*Sr_sin;
-
- xD[0] = xD[0] + 1;
- T = 1/Fs;
- xD[1] = xD[1] + T;
- }
- /* Update_END */
- }
第三步是编写Outputs_wrapper()函数,沿用前期C MEX S-Function的代码,使用自动生成的函数接口,写出的Outputs_wrapper()函数如下:
- void DFT_CMexSfuncBuilder_Outputs_wrapper(const real_T *u0,
- real_T *y0,
- const real_T *xD,
- const real_T *Freq, const int_T p_width0)
- {
- /* Output_BEGIN */
- /* This sample sets the output equal to the input
- y0[0] = u0[0];
- For complex signals use: y0[0].re = u0[0].re;
- y0[0].im = u0[0].im;
- y1[0].re = u1[0].re;
- y1[0].im = u1[0].im;
- */
- static real_T L = 5e3;
- real_T real = 0;
- real_T imag = 0;
- real_T modl = 0;
-
- if(xD[0]==L+1)
- {
- real = xD[2]/L*2;
- imag = xD[3]/L*2;
- modl = sqrt(real*real + imag*imag);
- y0[0] = modl;
- }
- /* Output_END */
- }
上述配置和函数编写完成后,点击Build按钮,可以看到如下是信息:
其中,DFT_CMexSfuncBuilder.c文件就是自动生成的C MEX文件,DFT_CMexSfuncBuilder_wrapper.c文件是C MEX文件中调用的wrapper函数,DFT_CMexSfuncBuilder.tlc是自动生成的TLC文件。
与此同时,工程文件夹里边还会自动生成DFT_CMexSfuncBuilder.mexw64、rtwmakecfg.m和SFB__DFT_CMexSfuncBuilder__SFB.mat,都是S-Fuction的执行文件和代码生成和编译的相关文件。
将上述编写好的S-Fuction Buider模块,放入Simulink模型中进行验证,如下所示:
运行上述模型,得到的电流和电压如上图所示,与前期C MEX S-Function的结果一致。
至此,可以证明该S-Fuction Buider模块可以较好地实现,手动开发C MEX S-Function相同的功能。
点击代码生成按钮,可以看到如下过程提示:
点击打开报告按钮,可以看到如下生成报告:
点击左侧的sfucbuilder.c超链接,可以看到如下生成的代码,其中30行到117行是该模型主要功能的代码,47行到86行是与我们S-Fuction Buider模块直接相关的代码。
- File: sfucbuilder.c
- 1 /*
- 2 * sfucbuilder.c
- 3 *
- 4 * Code generation for model "sfucbuilder".
- 5 *
- 6 * Model version : 1.52
- 7 * Simulink Coder version : 9.4 (R2020b) 29-Jul-2020
- 8 * C source code generated on : Thu Oct 5 22:12:11 2023
- 9 *
- 10 * Target selection: grt.tlc
- 11 * Note: GRT includes extra infrastructure and instrumentation for prototyping
- 12 * Embedded hardware selection: Intel->x86-64 (Windows64)
- 13 * Code generation objectives: Unspecified
- 14 * Validation result: Not run
- 15 */
- 16
- 17 #include "sfucbuilder.h"
- 18 #include "sfucbuilder_private.h"
- 19
- 20 /* Block signals (default storage) */
- 21 B_sfucbuilder_T sfucbuilder_B;
- 22
- 23 /* Block states (default storage) */
- 24 DW_sfucbuilder_T sfucbuilder_DW;
- 25
- 26 /* Real-time model */
- 27 static RT_MODEL_sfucbuilder_T sfucbuilder_M_;
- 28 RT_MODEL_sfucbuilder_T *const sfucbuilder_M = &sfucbuilder_M_;
- 29
- 30 /* Model step function */
- 31 void sfucbuilder_step(void)
- 32 {
- 33 /* Selector: '
/Selector5' incorporates: - 34 * Constant: '
/Constant6' - 35 * UnitDelay: '
/Output' - 36 */
- 37 sfucbuilder_B.Selector5 =
- 38 sfucbuilder_ConstP.Constant6_Value[sfucbuilder_DW.Output_DSTATE];
- 39
- 40 /* Selector: '
/Selector4' incorporates: - 41 * Constant: '
/Constant5' - 42 * UnitDelay: '
/Output' - 43 */
- 44 sfucbuilder_B.Selector4 =
- 45 sfucbuilder_ConstP.Constant5_Value[sfucbuilder_DW.Output_DSTATE];
- 46
- 47 /* S-Function (DFT_CMexSfuncBuilder): '
/S-Function Builder' */ - 48 DFT_CMexSfuncBuilder_Outputs_wrapper(&sfucbuilder_B.Selector4,
- 49 &sfucbuilder_B.SFunctionBuilder, &sfucbuilder_DW.SFunctionBuilder_DSTATE[0],
- 50 &sfucbuilder_ConstP.pooled1, 1);
- 51
- 52 /* S-Function (DFT_CMexSfuncBuilder): '
/S-Function Builder1' */ - 53 DFT_CMexSfuncBuilder_Outputs_wrapper(&sfucbuilder_B.Selector5,
- 54 &sfucbuilder_B.SFunctionBuilder1, &sfucbuilder_DW.SFunctionBuilder1_DSTATE[0],
- 55 &sfucbuilder_ConstP.pooled1, 1);
- 56
- 57 /* Switch: '
/FixPt Switch' incorporates: - 58 * Constant: '
/FixPt Constant' - 59 * Sum: '
/FixPt Sum1' - 60 * UnitDelay: '
/Output' - 61 */
- 62 if ((uint16_T)(sfucbuilder_DW.Output_DSTATE + 1U) > 4999) {
- 63 /* Update for UnitDelay: '
/Output' incorporates: - 64 * Constant: '
/Constant' - 65 */
- 66 sfucbuilder_DW.Output_DSTATE = 0U;
- 67 } else {
- 68 /* Update for UnitDelay: '
/Output' */ - 69 sfucbuilder_DW.Output_DSTATE++;
- 70 }
- 71
- 72 /* End of Switch: '
/FixPt Switch' */ - 73
- 74 /* Update for S-Function (DFT_CMexSfuncBuilder): '
/S-Function Builder' */ - 75
- 76 /* S-Function "DFT_CMexSfuncBuilder_wrapper" Block:
/S-Function Builder */ - 77 DFT_CMexSfuncBuilder_Update_wrapper(&sfucbuilder_B.Selector4,
- 78 &sfucbuilder_B.SFunctionBuilder, &sfucbuilder_DW.SFunctionBuilder_DSTATE[0],
- 79 &sfucbuilder_ConstP.pooled1, 1);
- 80
- 81 /* Update for S-Function (DFT_CMexSfuncBuilder): '
/S-Function Builder1' */ - 82
- 83 /* S-Function "DFT_CMexSfuncBuilder_wrapper" Block:
/S-Function Builder1 */ - 84 DFT_CMexSfuncBuilder_Update_wrapper(&sfucbuilder_B.Selector5,
- 85 &sfucbuilder_B.SFunctionBuilder1, &sfucbuilder_DW.SFunctionBuilder1_DSTATE[0],
- 86 &sfucbuilder_ConstP.pooled1, 1);
- 87
- 88 /* Matfile logging */
- 89 rt_UpdateTXYLogVars(sfucbuilder_M->rtwLogInfo,
- 90 (&sfucbuilder_M->Timing.taskTime0));
- 91
- 92 /* signal main to stop simulation */
- 93 { /* Sample time: [0.001s, 0.0s] */
- 94 if ((rtmGetTFinal(sfucbuilder_M)!=-1) &&
- 95 !((rtmGetTFinal(sfucbuilder_M)-sfucbuilder_M->Timing.taskTime0) >
- 96 sfucbuilder_M->Timing.taskTime0 * (DBL_EPSILON))) {
- 97 rtmSetErrorStatus(sfucbuilder_M, "Simulation finished");
- 98 }
- 99 }
- 100
- 101 /* Update absolute time for base rate */
- 102 /* The "clockTick0" counts the number of times the code of this task has
- 103 * been executed. The absolute time is the multiplication of "clockTick0"
- 104 * and "Timing.stepSize0". Size of "clockTick0" ensures timer will not
- 105 * overflow during the application lifespan selected.
- 106 * Timer of this task consists of two 32 bit unsigned integers.
- 107 * The two integers represent the low bits Timing.clockTick0 and the high bits
- 108 * Timing.clockTickH0. When the low bit overflows to 0, the high bits increment.
- 109 */
- 110 if (!(++sfucbuilder_M->Timing.clockTick0)) {
- 111 ++sfucbuilder_M->Timing.clockTickH0;
- 112 }
- 113
- 114 sfucbuilder_M->Timing.taskTime0 = sfucbuilder_M->Timing.clockTick0 *
- 115 sfucbuilder_M->Timing.stepSize0 + sfucbuilder_M->Timing.clockTickH0 *
- 116 sfucbuilder_M->Timing.stepSize0 * 4294967296.0;
- 117 }
- 118
- 119 /* Model initialize function */
- 120 void sfucbuilder_initialize(void)
- 121 {
- 122 /* Registration code */
- 123
- 124 /* initialize non-finites */
- 125 rt_InitInfAndNaN(sizeof(real_T));
- 126
- 127 /* initialize real-time model */
- 128 (void) memset((void *)sfucbuilder_M, 0,
- 129 sizeof(RT_MODEL_sfucbuilder_T));
- 130 rtmSetTFinal(sfucbuilder_M, 10.0);
- 131 sfucbuilder_M->Timing.stepSize0 = 0.001;
- 132
- 133 /* Setup for data logging */
- 134 {
- 135 static RTWLogInfo rt_DataLoggingInfo;
- 136 rt_DataLoggingInfo.loggingInterval = NULL;
- 137 sfucbuilder_M->rtwLogInfo = &rt_DataLoggingInfo;
- 138 }
- 139
- 140 /* Setup for data logging */
- 141 {
- 142 rtliSetLogXSignalInfo(sfucbuilder_M->rtwLogInfo, (NULL));
- 143 rtliSetLogXSignalPtrs(sfucbuilder_M->rtwLogInfo, (NULL));
- 144 rtliSetLogT(sfucbuilder_M->rtwLogInfo, "tout");
- 145 rtliSetLogX(sfucbuilder_M->rtwLogInfo, "");
- 146 rtliSetLogXFinal(sfucbuilder_M->rtwLogInfo, "");
- 147 rtliSetLogVarNameModifier(sfucbuilder_M->rtwLogInfo, "rt_");
- 148 rtliSetLogFormat(sfucbuilder_M->rtwLogInfo, 0);
- 149 rtliSetLogMaxRows(sfucbuilder_M->rtwLogInfo, 0);
- 150 rtliSetLogDecimation(sfucbuilder_M->rtwLogInfo, 1);
- 151 rtliSetLogY(sfucbuilder_M->rtwLogInfo, "");
- 152 rtliSetLogYSignalInfo(sfucbuilder_M->rtwLogInfo, (NULL));
- 153 rtliSetLogYSignalPtrs(sfucbuilder_M->rtwLogInfo, (NULL));
- 154 }
- 155
- 156 /* block I/O */
- 157 (void) memset(((void *) &sfucbuilder_B), 0,
- 158 sizeof(B_sfucbuilder_T));
- 159
- 160 /* states (dwork) */
- 161 (void) memset((void *)&sfucbuilder_DW, 0,
- 162 sizeof(DW_sfucbuilder_T));
- 163
- 164 /* Matfile logging */
- 165 rt_StartDataLoggingWithStartTime(sfucbuilder_M->rtwLogInfo, 0.0, rtmGetTFinal
- 166 (sfucbuilder_M), sfucbuilder_M->Timing.stepSize0, (&rtmGetErrorStatus
- 167 (sfucbuilder_M)));
- 168
- 169 /* InitializeConditions for UnitDelay: '
/Output' */ - 170 sfucbuilder_DW.Output_DSTATE = 0U;
- 171
- 172 /* InitializeConditions for S-Function (DFT_CMexSfuncBuilder): '
/S-Function Builder' */ - 173
- 174 /* S-Function Block:
/S-Function Builder */ - 175 {
- 176 real_T initVector[5] = { 1, 0, 0, 0, 50.0 };
- 177
- 178 {
- 179 int_T i1;
- 180 real_T *dw_DSTATE = &sfucbuilder_DW.SFunctionBuilder_DSTATE[0];
- 181 for (i1=0; i1 < 5; i1++) {
- 182 dw_DSTATE[i1] = initVector[i1];
- 183 }
- 184 }
- 185 }
- 186
- 187 /* InitializeConditions for S-Function (DFT_CMexSfuncBuilder): '
/S-Function Builder1' */ - 188
- 189 /* S-Function Block:
/S-Function Builder1 */ - 190 {
- 191 real_T initVector[5] = { 1, 0, 0, 0, 50.0 };
- 192
- 193 {
- 194 int_T i1;
- 195 real_T *dw_DSTATE = &sfucbuilder_DW.SFunctionBuilder1_DSTATE[0];
- 196 for (i1=0; i1 < 5; i1++) {
- 197 dw_DSTATE[i1] = initVector[i1];
- 198 }
- 199 }
- 200 }
- 201 }
- 202
- 203 /* Model terminate function */
- 204 void sfucbuilder_terminate(void)
- 205 {
- 206 /* (no terminate code required) */
- 207 }
- 208
人工检查上述自动生成的C代码,可以实现该Simulink模型设计的功能。
至此,可以证明该S-Fuction Buider模块可以较好地实现,手动开发TLC相同的功能。
本文沿用的例子--DFT算法S-Fuction开发,涉及到一个参数Freq。按照前期C MEX S-Function开发的经验,是可以通过函数直接引用的Freq = (real_T) *mxGetPr(ssGetSFcnParam(S,0)),但是在S-Fuction Buider里边尝试直接应用参数Freq时,却出现了报错。查阅相关资料后,发现有人是先把参数作为初始值赋给一个状态变量,然后再拿这个状态变量作为参数使用。本文中的第5个状态变量xD[4]就是充当Freq的中转桥,经验证这种方法确实可行。虽然这种方法稍微有点绕弯,但是也不失是一种解决问题的办法。
前面分析了那么多C MEX S-Function和TLC的特点和应用,本文S-Fuction Buider是集它们特点于一身的存在,并且把C MEX S-Function中复杂的接口函数和TLC中复杂的语法,都变成了Matlab后台自动执行的工作,开发者只需把注意力集中在C算法的移植上即可,大大提升了S-Function开发的工作效率。
以上就是本人在使用S-Fuction Buider时,一些个人理解和分析的总结,首先介绍了S-Fuction Buider的基本知识,然后展示它的使用方法,最后分析了该模块的特点和适用场景。
后续还会分享另外几个最近总结的Simulink Toolbox库模块,欢迎评论区留言、点赞、收藏和关注,这些鼓励和支持都将成文本人持续分享的动力。
另外,上述例程使用的Demo工程,可以到笔者的主页查找和下载。
版权声明,原创文章,转载和引用请注明出处和链接,侵权必究!