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-函数通常由以下几个核心函数组成:
-
mdlInitializeSizes:设置 S-函数的输入、输出、参数等信息。
-
mdlInitializeSampleTimes:设置采样时间。
-
mdlOutputs:计算输出。
-
mdlTerminate:执行清理操作。
也包含其他非核心的函数,例如mdlDerivatices、mdlUpdate、mdlRTW等。
C MEX S函数的特点:
- 高运行速度和效率:C MEX S函数通过使用C语言编写,并在运行前将
.c
文件编译成可执行的.mexw32
或.mexw64
文件。相比于M S函数,它不需要在仿真过程中反复调用MATLAB解释器,因此在运行速度和效率上有明显提升,尤其适用于计算密集型应用。 - 编程灵活性:C语言本身具有强大的编程灵活性,C MEX S函数可以利用C语言的丰富语法结构和库函数实现复杂的算法和逻辑控制。这使得C MEX S函数能够处理更多自定义和复杂的任务。
- 硬件外设支持:C MEX S函数能够支持硬件外设驱动,允许开发者通过S函数与外部硬件进行交互,并构建自定义的算法库或驱动块库,满足实际工程中嵌入式系统和外设控制的需求。
- 资源重用: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.h
、math.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.c
和 cg_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