这世界上根本就不存在真正的感同身受,拒绝内耗,好好说话!!!

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):

  1. #include "simstruc.h"//定义S函数的接口
  2. #include "Gen4PlateSet.h"//C2000相关的一些接口
  3. #include "LoopDef.h"//环路控制相关的宏定义
  4. #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):

  1. #define S_FUNCTION_NAME LoopControl
  2. #define S_FUNCTION_LEVEL 2
  3. #include "simstruc.h"
  4. #include "Gen4PlateSet.h"
  5. #include "LoopDef.h"
  6. #include "mex.h"
  7. #define N_INPUT 8
  8. #define N_OUTPUT 5
  9. #define SAMPLE_TS (0.00001)
  10. static void DataInput(const real_T* adc)
  11. {
  12. InputData.XXX= adc[0];
  13. ......
  14. InputData.XXX = adc[7];
  15. }
  16. static void DataOutput(real_T* out)
  17. {
  18. out[0] = OutputData.YYY;
  19. ......
  20. out[4] = OutputData.YYY;
  21. }
  22. /**********控制代码Start**********/
  23. 包含控制算法LoopCtrl()、初始化函数LoopCtrlInit()
  24. 建议将宏定义和内联函数打包放在LoopDef.h
  25. /**********控制代码end**********/
  26. static void mdlInitializeSizes(SimStruct *S)
  27. {
  28. // 设置输入端口个数
  29. if (!ssSetNumInputPorts(S, 1)) return;
  30. ssSetInputPortWidth(S, 0, N_INPUT);
  31. ssSetInputPortDirectFeedThrough(S, 0, 1);
  32. // 启用直接穿透,也就是输入更新完毕输出会马上更新
  33. ssSetInputPortRequiredContiguous(S, 0, true);
  34. // 设置连续存储,优化性能
  35. // 设置输出端口个数
  36. if (!ssSetNumOutputPorts(S, 1)) return;
  37. ssSetOutputPortWidth(S, 0, N_OUTPUT);
  38. ssSetNumSampleTimes(S, 1);//采样时间的个数
  39. ssSetNumRWork(S, 0);//设置实数型工作向量的大小为0
  40. ssSetNumIWork(S, 0);//设置整数型工作向量的大小为0
  41. ssSetNumPWork(S, 0);//设置指针型工作向量的大小为0
  42. ssSetNumModes(S, 0);//定义状态向量中模式的数量为0
  43. ssSetNumNonsampledZCs(S, 0);//无非采样零点
  44. ssSetOptions(S, 0);//将选项设置为默认值
  45. }
  46. static void mdlInitializeSampleTimes(SimStruct *S)
  47. {
  48. ssSetSampleTime(S, 0, SAMPLE_TS);
  49. ssSetOffsetTime(S, 0, 0);
  50. }
  51. static void mdlInitializeConditions(SimStruct *S)//只在仿真之前初始化一次,初始化代码放这里
  52. {
  53. LoopCtrlInit();
  54. }
  55. static void mdlOutputs(SimStruct *S, int_T tid)//在每个采样周期结束后立即执行的
  56. {
  57. const real_T *u = (const real_T*) ssGetInputPortSignal(S,0);//定义只读的输入指针变量u
  58. real_T *y = ssGetOutputPortSignal(S,0);//定义输出指针变量y
  59. DataInput(u);
  60. LoopCtrl();
  61. DataOutput(y);
  62. }
  63. #ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
  64. #include "simulink.c" /* MEX-file interface mechanism */
  65. #else
  66. #include "cg_sfun.h" /* Code generation registration function */
  67. #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模块参数:

posted @ 2023-12-20 15:48  你丫闭嘴20250318  阅读(417)  评论(0)    收藏  举报