自己摸索+参考了一部分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模块参数:

更多推荐

使用S-function实现基于C语言的电力电子控制器