一条简单可行的套路,simulink支持纯C代码仿真
开关电源仿真与实用设计 2022-09-26

前言:很久以前就听说过simulink支持纯C代码仿真,但是一直没有去摸索使用。今天尝试了一天后终于摸索出了一条简单可行的套路。


 可见套路:

    S1 把Sfunction模块放到空白工程里面,然后在matlab的工作目录里面新建一个xxx.c的文件,复制文件名粘贴到 sfunction name里面。


S2 安装matlab 里面运行的C语言编译器 MinGW64 Compiler,可以去官网下载:https://jmeubank.github.io/tdm-gcc/   


  根据操作系统选择,这里我下载64位编译器,然后安装到C盘根目录里面。强调:一定要安装在C盘根目录,如:C:\TDM-GCC-64。然后在我的电脑,高级系统设置里面设置系统变量:


S3 然后在matlab Command Window 里面输入: mex -setup,如果你安装编译器正确,就可以看到C语言使用'MinGW64 Compiler (C)'编译器,然后在输入:mex -setup C++,即可完成Matlab里面编译器的配置。



S4 编辑c代码

static void mdlInitializeSizes(SimStruct *S),这个函数里面是用来配置仿真的输入和输出参数,这里做一个简单的介绍即可,具体可以看help。


    if (!ssSetNumInputPorts(S, 1)) return;

    ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);

    ssSetInputPortDirectFeedThrough(S, 0, 1);

   

  这里用来设置输入信息,使用DYNAMICALLY_SIZED,是说明输入使用的动态维度,matlab会自动处理。ssSetInputPortDirectFeedThrough

是设置有直接馈通的信息,只要你的输出函数中使用到了输入,这里就要设置为ssSetInputPortDirectFeedThrough(S, 0, 1);

 

    ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);

     ssSetNumSampleTimes(S, 1);

  这里是设置输出信息,同上面一样输出信号宽度自动处理。ssSetNumSampleTimes(S, 1),是设置采样时间,一般就设置为1个采样时间即可,其它可以暂时不处理。


static void mdlInitializeSampleTimes(SimStruct *S)

{

    ssSetSampleTime(S, 0, Ts); /* INHERITED_SAMPLE_TIME*/ 

    ssSetOffsetTime(S, 0, 0.0);

    ssSetModelReferenceSampleTimeDefaultInheritance(S); 

}   用来设置采样时间,这里Ts是仿真的采样时间。

 

  用户定义函数是放在这里运行,具体可以看文末的代码。

static void mdlOutputs(SimStruct *S, int_T tid)

{

    // 这个函数是返回输入信号的指针

    InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); 

    

    // 这里是返回输出信号的指针

    real_T            *y    = ssGetOutputPortRealSignal(S,0);  

    

    //返回输出信号的宽

    int_T             width = ssGetOutputPortWidth(S,0); 

    real_T            *rwork_point  = (real_T*) ssGetRWork(S);

    

    //下面则是用户的正常的C语言函数操作


    double lpf_out = iir_2order_func(*uPtrs[0], &lpf_2order_func_data, &lpf_test_coeff);    

    *y =  1.0 * lpf_out;

 }


S5 编译代码:

  在matlab Command Window 里面输入: mex lpf_test_cmex1.c,如果代码有错误就会报错,解决错误后会提示:


S6 运行仿真,可以看到LPF功能已经实现。


    唯一需要说明的是sfunction的数据存储处理,使用C语言编程时,可以把你需要的数据和结构定义文件前面,然后直接使用satic 关键字声明即可,其用法和普通C语言操作一致。我早上刚开始接触时,把数据的存储考虑的过于复杂,使用了DWORK来存储离散数据。经过查阅资料后,发现DWORK的使用是为防止在一个工程中出现多个同一C文件的仿真时数据的重入问题,所可以使用DWROK来将数据保存在每个sfunction独立的内存区域,防止重入导致的数据错误,如果是单C文件仿真就无需考虑这些。具体请看文末代码,回复关键字可以获得仿真文件。


回复:

lpf_test_cmex1   获得文中使用的模型文件

更多精彩内容:

  1. VMC移相全桥的环路仿真与设计 视频版

  2. 移相全桥PWM和功率级建模教程 视频版

  3. 数字控制系统的实现方法及其软件工作流 视频版

  4. 数字控制系统中ADC采样延迟的影响 视频版

  5. 数字控制系统中Z域被控对象的建模 视频版

  6. VMC和CMC的LLC控制器仿真对比 第五节 (完结篇)

  7. 数字控制器的设计方法基于matlab环境  视频版

  8. Z域传递函数和IIR滤波器第一部分 视频版

  9. 与Christophe Basso先生合影背后的故事

  10. IIR滤波器的具体实现第二部分  视频版

  11. Z域传递函数和IIR滤波器第一部分 视频版

  12. 数字积分器微分器与PID的实现 视频版



代码:


/*

 * C - mex S function Test

 * By: maileyang

 * Data: 2020/04/06

 */


#define S_FUNCTION_NAME  lpf_test_cmex1

#define S_FUNCTION_LEVEL 2


#include "simstruc.h"

#include "math.h"


typedef int  int16_t;

typedef long int32_t;

typedef unsigned int uint16_t;

typedef unsigned long uint32_t;

typedef float float32_t;

typedef double float64_t;


#define Ts 1e-4

#define LPF_COEFF_B0    -0.0240184045397043f

#define LPF_COEFF_B1    -0.000742891126009384f

#define LPF_COEFF_B2     0.0232755134136949f

#define LPF_COEFF_A1    -1.88176520511650f

#define LPF_COEFF_A2     0.881765205116502f


typedef struct CTRL_LAW_2P2Z_DATA_TAG

{

    float32_t control_ref;

    float32_t control_sen;


    float32_t error_0;

    float32_t error_1;

    float32_t error_2;


    float32_t output_0;

    float32_t output_1;

    float32_t output_2;


    float32_t return_result;

}CTRL_LAW_2P2Z_DATA_DEF;


typedef struct IIR_2ORDER_TAG{

     double filter_out;

     double filter_W0;

     double filter_W1;

     double filter_W2;

}IIR_2ORDER_FUNC_DATA_DEF;

    

typedef struct IIR_2ORDER_COEFF_TAG{

    float32_t   coeff_b0;

    float32_t   coeff_b1;

    float32_t   coeff_b2;

    float32_t   coeff_a1;

    float32_t   coeff_a2;

}IIR_2ORDER_FUNC_COEFF_DEF;


static IIR_2ORDER_FUNC_DATA_DEF lpf_2order_func_data;


static IIR_2ORDER_FUNC_COEFF_DEF lpf_test_coeff = {

    LPF_COEFF_B0,   

    LPF_COEFF_B1,     

    LPF_COEFF_B2,     

    LPF_COEFF_A1,     

    LPF_COEFF_A2      

};


double iir_2order_func(double input_data, IIR_2ORDER_FUNC_DATA_DEF *obj_pointconst IIR_2ORDER_FUNC_COEFF_DEF *coeff)

{

  obj_point->filter_W0 = input_data - obj_point->filter_W1 * coeff->coeff_a1 \

                        - obj_point->filter_W2 * coeff->coeff_a2;


  double lpf_out = obj_point->filter_W0 * coeff->coeff_b0 \

                + obj_point->filter_W1 * coeff->coeff_b1 \

                + obj_point->filter_W2 * coeff->coeff_b2;   


  obj_point->filter_W2 = obj_point->filter_W1;

  obj_point->filter_W1 = obj_point->filter_W0;   


  return(lpf_out);                                                  

}


/* Function: mdlInitializeSizes ===============================================

 * Abstract:

 *   Setup sizes of the various vectors.

 */

static void mdlInitializeSizes(SimStruct *S)

{

    ssSetNumSFcnParams(S, 0);

    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {

        return/* Parameter mismatch will be reported by Simulink */

    }


    if (!ssSetNumInputPorts(S, 1)) return;

    ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);

    ssSetInputPortDirectFeedThrough(S, 01);


    if (!ssSetNumOutputPorts(S,1)) return;

    ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);


    ssSetNumSampleTimes(S, 1);

    

    ssSetNumRWork(S, 1);

    

    /* specify the sim state compliance to be same as a built-in block */

    ssSetSimStateCompliance(S, USE_DEFAULT_SIM_STATE);


    ssSetOptions(S,

                 SS_OPTION_WORKS_WITH_CODE_REUSE |

                 SS_OPTION_EXCEPTION_FREE_CODE |

                 SS_OPTION_USE_TLC_WITH_ACCELERATOR);

}


  /* Function: mdlInitializeConditions ========================================

    *

   */

#define MDL_INITIALIZE_CONDITIONS   /* Change to #undef to remove function */

  static void mdlInitializeConditions(SimStruct *S)

  {

    real_T *x = (real_T *) ssGetRWork(S);

    /*  Initialize the first RWork vector */

    x[0] = 0;      

    x[1] = 0;  

    x[2] = 0;  

  }


  

/* Function: mdlInitializeSampleTimes =========================================

 * Abstract:

 *    Specifiy that we inherit our sample time from the driving block.

 */

static void mdlInitializeSampleTimes(SimStruct *S)

{

    ssSetSampleTime(S, 0, Ts); /* INHERITED_SAMPLE_TIME*/ 

    ssSetOffsetTime(S, 00.0);

    ssSetModelReferenceSampleTimeDefaultInheritance(S); 

}


/* Function: mdlOutputs =======================================================

 * Abstract:

 *  LPF TEST

 */

static void mdlOutputs(SimStruct *S, int_T tid)

{

    InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

    real_T            *y    = ssGetOutputPortRealSignal(S,0);

    int_T             width = ssGetOutputPortWidth(S,0);

    real_T            *rwork_point  = (real_T*) ssGetRWork(S);


    double lpf_out = iir_2order_func(*uPtrs[0], &lpf_2order_func_data, &lpf_test_coeff);

        

    *y =  1.0 * lpf_out;

}



/* Function: mdlTerminate =====================================================

 * Abstract:

 *    No termination needed, but we are required to have this routine.

 */

static void mdlTerminate(SimStruct *S)

{

}


#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


// end~~~


本文源自微信公众号:开关电源仿真与实用设计,不代表用户或本站观点,如有侵权,请联系nick.zong@aspencore.com 删除!

声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 相关技术文库
  • C语言
  • 编程
  • 软件开发
  • 程序
  • STM32如何移植uCGUI,看这里!

    [导读]第一部分:在UCGUI移植之前在移植之前,首先要了解在网上下的UCGUI 3.98源码的文件结构是。UCGUI 3.98源码中有三个文件夹:1)“too

    11-22
  • 嵌入式汇编语言学习经验

    [导读]最近参与了一个项目,需要用嵌入式汇编写测试程序。汇编程序写过,C程序也写过,但是将它们混合起来写还是第一次,完全没有概念。只能上网找资料,自己慢慢摸索。

    11-22
  • 数字示波器在高频信号采集中的应用

    1 高频信号的采集 当要对一个高频信号(比如高达100MHz的雷达波形)进行采集和处理的时候。通常会设计一个高速或者超高速硬件采集电路,包括放大部分、滤波部分;A/D和D/A转换部分等,这种电路的要求非常高,要求边采集边存储,电路速度高,而且要考虑各种辐射干扰等,同时,目前市场上成品价格很难承受。并且根据采样定理,一个最高频率为/的连续信号,完全可以用时间上相隔了=1/2f的一系列离散采样值来表示...

    11-22
  • 单片机C语言static属性和数码管显示程序

    简介:static从英文上翻译是静态的意思,在C语言中static所起的作用也正是静态。对于局部变量而言,其作用域是局部的如某一子函数体,程序在每次执行时调用该

    11-21
  • LEMO产品进入EPLAN Data Portal绘图平台部件数据库

      EPLANDataPortal是一个基于web网络的平台,可以让工程师即时访问众多部件制造商的电气原理图,从现在开始,LEMO的连接器也加入了EPLANDa

    11-21
  • DAC0832三种波形的C语言程序

    波形发生器作为一种常用的应用电子仪器设备,传统的波形发生器可以完全用硬件电路搭建,如应用555 振荡电路可以产生正弦波,三三角波,方波等波形,传统的波形发生器多

    11-17
  • 虚拟仪器发展趋势及其对军用测试技术的影响

    1 引言从1986年NI公司提出VI概念到现在,经过十几年的发展,不仅VI技术本身的内涵不断丰富,外延不断扩展,在军事和民用领域均得到了广泛的应用,而且对现代测控技术产生了深远的影响。例如,VI原来最核心的思想是利用计算机的强大资源使本来需要硬件实现的技术软件化,以便最大限度地降低系统成本,增强系统功能与灵活性。由IT产业特征决定了VI技术也必须走标准化、开放性这条技术路线,目前VI已发展成具有G...

    11-16
  • 漫画:什么是 “跳表” ?

    —————  第二天  ————— 如何进行二分查找呢? 首先根据数组下标,定位到数组的中间元素: 由于要查找的元素20,大于中间元素12,再次定位到数组右半部分的中间元素: 这一次定位到的元素正好是20,查找成功。 如果数组的长度

    11-03
  • 总结4个Python数据读取的常见问题

    read_csv()是python数据分析包pandas里面使用频次较高的函数之一。它包括的参数差不多20个,可能一开始未必需要完整知道每个参数作用。不过,随着

    10-26
  • AWTK能为现代GUI编程带来何种改变?

    AWTK是一个伸缩性极强的嵌入式图形框架,它的诞生会给GUI编程研发工程师带来哪些改变?AWTK是一个伸缩性极强的嵌入式图形框架,可在Cortex-M3这样低端

    10-26
  • 可编程中断控制器8259其内部结构及引脚图

    [导读] 中断系统的使用极大的提高了CPU的利用率。中断是一种机制,这种机制实现的过程可分为请求-->响应-->服务-->返回。可编程中断控制器8259A是In

    10-24
下载排行榜
更多
广告