S-Function使用(C MEX S-函数)

S-Function使用(C MEX S-函数)

编写 C MEX S-函数 (C MEX S-function) 是在 Simulink 中实现自定义行为的一种强大方式。C MEX S-函数是一种可以通过 C 语言编写的动态链接库,它允许你在 Simulink 模型中使用自定义代码实现的算法。下面是如何编写一个简单的 C MEX S-函数的步骤:

1 S-函数的主要结构

一个 C MEX S-函数通常由以下几个核心函数组成:

  1. mdlInitializeSizes:设置 S-函数的输入、输出、参数等信息。

  2. mdlInitializeSampleTimes:设置采样时间。

  3. mdlOutputs:计算输出。

  4. mdlTerminate:执行清理操作。
    也包含其他非核心的函数,例如mdlDerivaticesmdlUpdatemdlRTW等。
    在这里插入图片描述

C MEX S函数的特点:

  1. 高运行速度和效率:C MEX S函数通过使用C语言编写,并在运行前将.c文件编译成可执行的.mexw32.mexw64文件。相比于M S函数,它不需要在仿真过程中反复调用MATLAB解释器,因此在运行速度和效率上有明显提升,尤其适用于计算密集型应用。
  2. 编程灵活性:C语言本身具有强大的编程灵活性,C MEX S函数可以利用C语言的丰富语法结构和库函数实现复杂的算法和逻辑控制。这使得C MEX S函数能够处理更多自定义和复杂的任务。
  3. 硬件外设支持:C MEX S函数能够支持硬件外设驱动,允许开发者通过S函数与外部硬件进行交互,并构建自定义的算法库或驱动块库,满足实际工程中嵌入式系统和外设控制的需求。
  4. 资源重用:C MEX S函数可以直接调用已有的C代码。这种资源重用特性使得已有的成熟C代码可以轻松地集成到Simulink模型中,降低了开发成本并提高了代码复用性。

C Mex S函数的构成:

宏定义部分
每个C MEX S函数的开头都需要定义两个宏:

  • S函数名:定义为 S_FUNCTION_NAME,指定当前S函数的名称。
  • S函数等级:通过 S_FUNCTION_LEVEL 2 指定S函数的版本。
#define S_FUNCTION_NAME  my_s_function   // S-函数名称
#define S_FUNCTION_LEVEL 2               // S-函数版本等级

头文件包含部分
头文件是C MEX S函数中不可或缺的一部分,其中 simstruc.h 是必须包含的头文件,它定义了Simulink与S函数进行交互的 SimStruct 结构体和API。

其他C标准库文件(如 stdio.hmath.h 等)可以根据具体的功能需求包含。

#include "simstruc.h"  // Simulink核心数据结构
#include <stdio.h>     // 标准输入输出库(根据需要)
#include <math.h>      // 数学库(根据需要)

参数对话框访问宏函数定义
Simulink与S函数之间交互依赖于 SimStruct 数据结构和一系列宏函数。这些宏函数用于:

  • 获取输入、输出端口的信号、参数;
  • 设置输入/输出端口的属性,如宽度、数据类型、直接传递等;
  • 设置采样时间及偏移时间。

通过这些宏函数,用户可以灵活配置参数。

ssSetNumSFcnParams(S, numParams);    // 设置参数数量
ssSetNumInputPorts(S, numPorts);     // 设置输入端口数量
ssSetInputPortWidth(S, port, width); // 设置输入端口宽度
ssSetOutputPortWidth(S, port, width);// 设置输出端口宽度

S函数子方法的定义
C MEX S函数必须实现几个回调函数(子方法),以定义系统的初始化、计算逻辑、终止等行为。每个子方法实现具体的功能:

  • mdlInitializeSizes:定义输入、输出端口的数量、数据类型和宽度,设置采样时间。
  • mdlInitializeSampleTimes:设置采样时间和偏移时间。
  • mdlOutputs:计算输出信号。
  • mdlTerminate:进行资源清理等工作。

示例:

static void mdlInitializeSizes(SimStruct *S)
{
    // 设置输入、输出端口和相关属性
    ssSetNumInputPorts(S, 1);
    ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
    ssSetInputPortDataType(S, 0, SS_DOUBLE);
    ssSetInputPortDirectFeedThrough(S, 0, 1);

    ssSetNumOutputPorts(S, 1);
    ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
    ssSetOutputPortDataType(S, 0, SS_DOUBLE);

    ssSetNumSampleTimes(S, 1);
}

static void mdlOutputs(SimStruct *S, int_T tid)
{
    real_T *u = (real_T *)ssGetInputPortSignal(S, 0);
    real_T *y = ssGetOutputPortRealSignal(S, 0);
    y[0] = u[0] + 1.0;  // 简单加法计算输出
}

SimStruct的连接与Simulink集成
C MEX S函数与Simulink和Simulink Coder的连接,通过 simulink.ccg_sfun.h 头文件实现。MEX编译后的文件可以直接集成到Simulink模型中进行仿真。

#ifdef  MATLAB_MEX_FILE
#include "simulink.c"  // MATLAB仿真接口
#else
#include "cg_sfun.h"   // 生成代码接口
#endif

2 示例

#define S_FUNCTION_LEVEL 2 // 宏定义了 S-函数的版本级别
#define S_FUNCTION_NAME  s_function_add // 定义了 S-函数的名称

#include "simstruc.h"
#include <stdio.h>  // 包含 printf 函数的头文件,主要进行打印查看

// Function: mdlInitializeSizes ===============================================
// 设置输入和输出的大小
static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, 0); // 设置 S-函数的参数数量;为0则没有额外的参数

    // 设置两个输入端口
    if (!ssSetNumInputPorts(S, 2)) return; // 设置输入端口的数量为 2
    ssSetInputPortWidth(S, 0, 1); // 第一个输入端口宽度为 1
    ssSetInputPortWidth(S, 1, 1); // 第二个输入端口宽度为 1
    ssSetInputPortDataType(S, 0, SS_UINT8); // 设置第一个输入数据类型为 uint8
    ssSetInputPortDataType(S, 1, SS_UINT8); // 设置第二个输入数据类型为 uint8
    ssSetInputPortDirectFeedThrough(S, 0, 1); // 输入数据直接传递到输出
    ssSetInputPortDirectFeedThrough(S, 1, 1); // 输入数据直接传递到输出
    
    // 设置一个输出端口
    if (!ssSetNumOutputPorts(S, 1)) return; // 设置输出端口的数量
    ssSetOutputPortWidth(S, 0, 1); // 输出端口宽度为 1
    ssSetOutputPortDataType(S, 0, SS_DOUBLE); // 设置输出数据类型为 double
    
    ssSetNumSampleTimes(S, 1); // 一个采样时间
}

// Function: mdlInitializeSampleTimes =========================================
// 设置采样时间
static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); // 设置 S-函数的采样时间
    ssSetOffsetTime(S, 0, 0.0); // 设置 S-函数的偏移时间
}

// Function: mdlOutputs =======================================================
// 计算输出
static void mdlOutputs(SimStruct *S, int_T tid)
{
    // 获取两个输入信号
    InputUInt8PtrsType u0 = (InputUInt8PtrsType)ssGetInputPortSignalPtrs(S, 0); // 获取第一个输入信号
    InputUInt8PtrsType u1 = (InputUInt8PtrsType)ssGetInputPortSignalPtrs(S, 1); // 获取第二个输入信号
    
    // 获取输出信号
    real_T *y = ssGetOutputPortRealSignal(S, 0);
    
    // 将两个 uint8 的输入值相加,并将结果存储为 double
    uint8_T input1 = *u0[0];
    uint8_T input2 = *u1[0];
    
    double result = (double)(input1 + input2);
    
    // 打印输入和输出值
    printf("Input 1 value: %u\n", input1);
    printf("Input 2 value: %u\n", input2);
    printf("Output value (sum): %f\n", result);
    
    // 将结果赋值给输出
    y[0] = result;
}

// Function: mdlTerminate =====================================================
// 清理工作
static void mdlTerminate(SimStruct *S)
{
    // 在这里进行任何清理工作(如动态分配的内存)
}

/*=============================*

Required S-function trailer *
*=============================*/
#ifdef  MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif
The function `mdlInitializeSizes` is part of the Simulink S-function API and is called during the initialization phase of a custom S-function block. It is responsible for specifying the input and output port sizes, sample times, initial conditions, and other block attributes. Here is an example implementation of `mdlInitializeSizes`: ```matlab static void mdlInitializeSizes(SimStruct *S) { // Set input port properties if (!ssSetNumInputPorts(S, 1)) return; ssSetInputPortWidth(S, 0, 1); ssSetInputPortDirectFeedThrough(S, 0, 1); // Set output port properties if (!ssSetNumOutputPorts(S, 1)) return; ssSetOutputPortWidth(S, 0, 1); // Set sample time ssSetNumContStates(S, 0); ssSetNumDiscStates(S, 0); ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE); ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK); ssSetSimStateCompliance(S, USE_CUSTOM_SIM_STATE); ssSetModelReferenceSimTargetDiagnostics(S, rtM->errorStatus); ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK); ssSetModelReferenceSimTargetDiagnostics(S, rtM->errorStatus); ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK); ssSetModelReferenceSimTargetDiagnostics(S, rtM->errorStatus); ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK); ssSetModelReferenceSimTargetDiagnostics(S, rtM->errorStatus); ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK); ssSetModelReferenceSimTargetDiagnostics(S, rtM->errorStatus); ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK); ssSetModelReferenceSimTargetDiagnostics(S, rtM->errorStatus); ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK); ssSetModelReferenceSimTargetDiagnostics(S, rtM->errorStatus); ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK); ssSetModelReferenceSimTargetDiagnostics(S, rtM->errorStatus); ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK); ssSetModelReferenceSimTargetDiagnostics(S, rtM->errorStatus); } ``` This implementation specifies a single input port with a width of 1, a direct feedthrough flag of 1, and a single output port with a width of 1. It also sets the sample time, number of continuous and discrete states, and block options. Note that this is just an example and the actual implementation may vary depending on the specific requirements of the S-function.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值