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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值