前言
S-Fuction Builder
模块配置
编写Update_wrapper()
编写Outputs_wrapper()
Build S-Fuction
仿真验证
生成代码
Tips
分析和应用
总结
见《开箱报告,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工程,可以到笔者的主页查找和下载。
版权声明,原创文章,转载和引用请注明出处和链接,侵权必究!