C文件的s-function模板
自己摸索+参考了一部分ChatGPT的回答,也通过搭建电路验证了控制的有效性。
意义:能直接将CCS工程的控制代码copy到s-function模块,在simulink中只需要搭建好PWM模块和主功率电路即可,好处是能节省时间,且对实际系统的还原度更高,能验证一些复杂的控制策略的效果。
事前准备:需要为MATLAB安装MinGW64 Compiler编译器,配置环境,否则无法编译。参考:
Matlab mex -setup 找不到编译器:为MATLAB安装MinGW64 Compiler编译器_matlab安装mex_booksyhay的博客-CSDN博客
在Library中搜索S-Function Examples,按照以下顺序双击打开:


就能得到一个level-2的S-function模板sfuntmpl_basic.c。(level-1的已经基本不再使用)
将sfuntmpl_basic.c文件复制出来到与仿真文件的同一文件夹下,再进行修改名称,添加头文件,声明变量等。建议将宏定义和内联函数等定义都放在头文件中;因此我的头文件包含(如果你喜欢的话也可以将代码全部放在.c里,这样就只需要引用simstruc.h和mex.h):
- #include "simstruc.h"//定义S函数的接口
- #include "Gen4PlateSet.h"//C2000相关的一些接口
- #include "LoopDef.h"//环路控制相关的宏定义
- #include "mex.h"//MATLAB C/C++接口中的一个头文件
将这个.c文件重命名为LoopControl.c,那么它的整体架构是这样的:
/*Part1
头文件、宏定义等。
/*Part2
控制相关代码:Copy来自CCS的控制相关代码。
/*Part3
mdl函数:至少要包含mdlInitializeSizes、mdlInitializeSampleTimes、mdlInitializeConditions、mdlOutputs和mdlTerminate。
对于完成以上配置的S-function,它的生命周期是先在仿真开始时执行mdlInitializeConditions,然后每过一次采样周期,完成采样后就会执行一次mdlOutputs,仿真结束执行mdlTerminate。
因此,我们需要将控制算法放在mdlOutputs中保证其周期性执行,初始化代码放在mdlInitializeConditions中。
mdlInitializeSizes和mdlInitializeSampleTimes主要用于配置输入输出个数、采样时间等。
mdlTerminate里什么都不需要写,但是没有它会编译错误。
对于电力电子控制器相关S-function函数的配置,我配置的模板如下(8个输入,5个输出,采样频率为100kHz):
- #define S_FUNCTION_NAME LoopControl
- #define S_FUNCTION_LEVEL 2
-
- #include "simstruc.h"
- #include "Gen4PlateSet.h"
- #include "LoopDef.h"
- #include "mex.h"
-
- #define N_INPUT 8
- #define N_OUTPUT 5
- #define SAMPLE_TS (0.00001)
-
- static void DataInput(const real_T* adc)
- {
- InputData.XXX= adc[0];
- ......
- InputData.XXX = adc[7];
- }
-
- static void DataOutput(real_T* out)
- {
- out[0] = OutputData.YYY;
- ......
- out[4] = OutputData.YYY;
- }
-
- /**********控制代码Start**********/
-
- 包含控制算法LoopCtrl()、初始化函数LoopCtrlInit()
- 建议将宏定义和内联函数打包放在LoopDef.h中
-
- /**********控制代码end**********/
-
- static void mdlInitializeSizes(SimStruct *S)
- {
- // 设置输入端口个数
- if (!ssSetNumInputPorts(S, 1)) return;
- ssSetInputPortWidth(S, 0, N_INPUT);
- ssSetInputPortDirectFeedThrough(S, 0, 1);
- // 启用直接穿透,也就是输入更新完毕输出会马上更新
- ssSetInputPortRequiredContiguous(S, 0, true);
- // 设置连续存储,优化性能
-
- // 设置输出端口个数
- if (!ssSetNumOutputPorts(S, 1)) return;
- ssSetOutputPortWidth(S, 0, N_OUTPUT);
-
- ssSetNumSampleTimes(S, 1);//采样时间的个数
- ssSetNumRWork(S, 0);//设置实数型工作向量的大小为0
- ssSetNumIWork(S, 0);//设置整数型工作向量的大小为0
- ssSetNumPWork(S, 0);//设置指针型工作向量的大小为0
- ssSetNumModes(S, 0);//定义状态向量中模式的数量为0
- ssSetNumNonsampledZCs(S, 0);//无非采样零点
- ssSetOptions(S, 0);//将选项设置为默认值
-
- }
-
- static void mdlInitializeSampleTimes(SimStruct *S)
- {
- ssSetSampleTime(S, 0, SAMPLE_TS);
- ssSetOffsetTime(S, 0, 0);
- }
-
- static void mdlInitializeConditions(SimStruct *S)//只在仿真之前初始化一次,初始化代码放这里
- {
- LoopCtrlInit();
- }
-
- static void mdlOutputs(SimStruct *S, int_T tid)//在每个采样周期结束后立即执行的
- {
- const real_T *u = (const real_T*) ssGetInputPortSignal(S,0);//定义只读的输入指针变量u
- real_T *y = ssGetOutputPortSignal(S,0);//定义输出指针变量y
-
- DataInput(u);
- LoopCtrl();
- DataOutput(y);
-
- }
-
- #ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
- #include "simulink.c" /* MEX-file interface mechanism */
- #else
- #include "cg_sfun.h" /* Code generation registration function */
- #endif
说明:
1、InputData和OutputData结构体需要自行配置,LoopCtrl()为你的环路控制函数, LoopCtrlInit()为你的初始化函数,需要你自行配(fu)置(zhi)。
2、注意此处的采样时间的个数应设置为1,如果你设置了ssSetNumSampleTimes(S, 2),那么你就需要配置两个采样时间ts1和ts2,系统就会在m*ts1+n*ts2的时间点进行采样。
3、对于输入、输出而言,我只定义了一个输入端口和输出端口,但是它们的维度分别是8和5,因此你需要在仿真中使用mux和demux。
配置完毕后,还需要执行以下matlab代码来完成C语言的编译,才能在仿真中使用你的控制器:
setenv('MW_MINGW64_LOC', 'C:\TDM-GCC-64');
mex -setup C++;
mex LoopControl.c;
如果你的代码缺少什么,编译器会如实地告诉你,你对代码中缺少的部分进行添加。
最后当然是不要忘记设置你的S-Function模块参数:


浙公网安备 33010602011771号