主要内容

本页采用了机器翻译。点击此处可查看最新英文版本。

创建并实现基本的 C MEX S-Function

本主题介绍如何创建基本的 C MEX S-Function(系统函数)、C MEX S-Function如何与 Simulink® 交互,以及创建 C MEX S-Function 并在 Simulink 模型中实现它的简单示例。

S-Function 为扩展 Simulink 环境的功能提供了强大的机制。S-Function 是以 MATLAB®、C、C++ 或 Fortran 语言编写的 Simulink 模块的计算机语言描述。对于用 C、C++ 或 Fortran 编写的模块,请使用 C Mex S-Function。C、C++ 和 Fortran® S-Function 使用 mex 实用工具编译为 MEX 文件(参阅 编译 C MEX 函数)。与其他 MEX 文件一样,S-Function 是动态链接的子例程,MATLAB 执行引擎可以自动加载和执行它们。

如何创建 C MEX S-Function

您可以使用以下任一方法创建 C MEX S-Function:

  • 手写 S-Function - 您可以从头开始编写 C MEX S-Function。(创建并实现基本的 C MEX S-Function 提供了分步示例。)请参阅 Templates for C S-Functions 了解 C MEX S-Function 的完整骨架实现,您可以将其用作创建自己的 S-Function 的起点。尽管手写 S-Function 支持最广泛的功能,但它们可能很难编写。

  • S-Function Builder - 该模块集成了 C/C++ 代码,并根据您使用图形用户界面提供的规范和代码片段构建 C MEX S-Function。这样您就无需从头开始编写 S-Function。S-Function Builder 模块简化了编写 C MEX S-Function 的任务,但支持的功能较少。有关 S-Function Builder 模块的更多信息,请参阅 Use a Bus with S-Function Builder to Create an S-Function

  • Simulink Coder™ - Simulink Coder 产品提供了一种从图形子系统生成 C MEX S-Function 的方法。如果您是编写 C MEX S-Function 的新手,您可以在 Simulink 子系统中构建应用程序的各个部分,然后使用 S-Function 目标将其转换为 S-Function。生成的文件提供了有关如何在 S-Function 中实现特定模块的见解。有关使用 S-Function 目标的详细信息和限制,请参阅 将 S-Function 目标用于模型或子系统 (Simulink Coder)。您可以开发一个 S-Function 来使用与 Simulink 引擎交互的 API 来表示外部代码。将此 S-Function 与代码生成器一起使用来生成代码。有关代码生成中不同类型的 S-Function 的详细信息,请参阅 S-Function 和代码生成 (Simulink Coder)

有关编写 C MEX S-Function 的每种方法的特性和局限性的更多信息,请参阅 Available S-Function Implementations

C MEX S-Function 如何与 Simulink 交互

AC MEX S-Function 必须在仿真期间向 Simulink 引擎提供有关该函数的信息。随着仿真的进行,引擎、ODE 求解器和 C MEX S-Function 相互作用以执行特定的任务。这些任务包括定义初始条件和模块特征,以及计算导数、离散状态和输出。

与 MATLAB S-function 一样,Simulink 引擎通过调用 S-Function 实现的回调方法与 C MEX S-Function 交互。每种方法都执行一项预定义的任务,例如计算模块输出,以仿真 S-Function 所定义功能的模块。但是,S-Function 可以根据其 S-Function 的功能自由地在每种方法中执行任务。例如,mdlOutputs 方法必须计算当前仿真时间的模块输出。但是,S-Function 可以按照适合该函数的任何方式计算这些输出。这个基于回调的 API 允许您创建具有任何所需功能的 S-Function,从而创建自定义模块。

C MEX S-Function 可以实现的回调方法集比 MATLAB S-Function 可用的回调方法集更大。C MEX S-Function 仅需要实现 S-FunctionAPI 中的一小部分回调方法。如果您的模块没有实现特定功能(例如矩阵信号),您可以自由省略实现功能所需的回调方法。这使您可以非常快速地创建简单的模块。

C MEX S-Function 的一般格式如下所示:

#define S_FUNCTION_NAME  /* your_sfunction_name_here */
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"

static void mdlInitializeSizes(SimStruct *S)
{
}

/* <additional S-function routines/code> */

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

mdlInitializeSizes 是 Simulink 引擎与 S-Function 交互时调用的第一个例程。引擎随后调用其他 S-Function 方法(均以 mdl 开头)。在仿真结束时,引擎调用 mdlTerminate

介绍基本 C MEX S-Function 的示例

本节介绍一个 C MEX S-Function 的示例,您可以将其用作创建简单 C S-Function 的模型。示例 S-Functiontimestwo.c 的输出为其输入的两倍。

以下模型使用 timestwo S-Function 将正弦波的振幅加倍并在示波器中绘制它。

S-Function 的模块对话框指定 timestwo 作为 S-Function 名称;参数字段为空。

timestwo S-Function 包含此图中所示的 S-Function 回调方法。在 S-Function 的末尾,包含 Simulink/Simulink Coder 接口 中所述的代码片段。

timestwo.c 的内容如下所示。示例之后提供了代码的描述。

#define S_FUNCTION_NAME timestwo 
#define S_FUNCTION_LEVEL 2

#include "simstruc.h"
static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, 0);
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
        return; /* Parameter mismatch reported by the Simulink engine*/
    }

    if (!ssSetNumInputPorts(S, 1)) return;
    ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
    ssSetInputPortDirectFeedThrough(S, 0, 1);

    if (!ssSetNumOutputPorts(S,1)) return;
    ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);

    ssSetNumSampleTimes(S, 1);

    /* Take care when specifying exception free code - see sfuntmpl.doc */
    ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
    }
static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
    ssSetOffsetTime(S, 0, 0.0);
}
static void mdlOutputs(SimStruct *S, int_T tid)
{
    int_T i;
    InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
    real_T *y = ssGetOutputPortRealSignal(S,0);
    int_T width = ssGetOutputPortWidth(S,0);

    for (i=0; i<width; i++) {
        *y++ = 2.0 *(*uPtrs[i]);
    }
}
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

这个示例有三个部分:

  • 定义和包括

  • 回调方法实现

  • Simulink(或 Simulink Coder)产品接口

定义和包括

该示例以以下 define 语句开始。

#define S_FUNCTION_NAME  timestwo
#define S_FUNCTION_LEVEL 2

第一个 define 语句指定 S-Function 的名称 (timestwo)。第二个 define 语句指定 S-Function 采用 Level 2 格式(有关 Level 1 和 Level 2 S-function 的更多信息,请参阅 Convert Level-1 C MEX S-Functions)。

定义这两项之后,示例包含 simstruc.h,这是一个头文件,可以访问 SimStruct 数据结构和 MATLAB 应用程序接口 (API) 函数。

#define S_FUNCTION_NAME  timestwo
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"

simstruc.h 文件定义了一个名为 SimStruct 的数据结构,Simulink 引擎使用该数据结构来维护有关 S-Function 的信息。simstruc.h 文件还定义了宏,使您的 MEX 文件能够从 SimStruct(参阅 About SimStruct Functions)设置值和获取值(例如模块的输入和输出信号)。

回调方法实现

timestwo S-Function 的下一部分包含所需回调方法的实现。

mdlInitializeSizes

Simulink 引擎调用 mdlInitializeSizes 来查询输入和输出端口的数量、端口的大小以及 S-Function 所需的任何其他信息(例如状态数)。

mdlInitializeSizestimestwo 实现指定了以下尺寸信息:

  • 零参数

    因此,S-Function 模块对话框的 S-Function 字段必须为空。如果包含任何参数,引擎会报告参数不匹配。

  • 一个输入端口和一个输出端口

    输入和输出端口的宽度是动态大小的。这告诉引擎 S-Function 可以接受任何宽度的输入信号。默认情况下,当 S-Function 只有一个输入和输出端口时,动态大小的输入和输出端口的宽度相等。

  • 一个采样时间

    mdlInitializeSampleTimes 回调方法指定采样时间的实际值。

  • 无异常代码

    指定无异常代码可加快 S-Function 的执行速度。指定此选项时必须小心。一般来说,如果您的 S-Function 没有与 MATLAB 环境交互,您可以安全地指定此选项。有关详细信息,请参阅Simulink Engine Interaction with C S-Functions

mdlInitializeSampleTimes

Simulink 引擎调用 mdlInitializeSampleTimes 来设置 S-Function 的采样时间。每当驱动模块执行时,timestwo 模块就会执行。因此,它有一个单一的继承采样时间,INHERITED_SAMPLE_TIME

mdlOutputs

引擎在每个时间步骤调用 mdlOutputs 来计算模块输出。mdlOutputstimestwo 实现将输入信号乘以 2,并将答案写入输出。

代码行:

InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);

访问输入信号。ssGetInputPortRealSignalPtrs 宏返回一个指针向量,您必须使用以下方式访问该向量

*uPtrs[i]

有关访问输入信号的更多详细信息,请参阅Accessing Signals Using Pointers

代码行:

real_T *y = ssGetOutputPortRealSignal(S,0);

访问输出信号。ssGetOutputPortRealSignal 宏返回一个指向包含模块输出的数组的指针。

代码行:

int_T width = ssGetOutputPortWidth(S,0);

获取通过模块的信号的宽度。S-Function 循环遍历输入来计算输出。

mdlTerminate

引擎调用 mdlTerminate 为 S-Function 提供在仿真结束时执行任务的机会。这是一个强制性的 S-Function 例程。timestwo S-Function 不执行任何终止操作,并且此例程为空。

Simulink/Simulink Coder 接口

在 S-Function 的末尾,包含以下代码以将您的 S-Function 附加到 Simulink 或 Simulink Coder 产品。

#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif

每个 S-Function 的末尾都需要这个尾部。如果省略它,则任何编译 S-Function 的尝试都将中止,并显示 failure during build of exports file 错误消息。

构建 Timestwo 示例

要编译此 S-Function,请输入

mex timestwo.c

(在命令行中)。mex 命令使用默认编译器编译并链接 timestwo.c 文件。mex 命令创建一个动态可加载的可执行文件供 Simulink 软件使用。如果您有多个支持 MATLAB 的编译器,则可以使用 mex -setup 命令更改默认值。请参阅 更改默认编译器支持的编译器列表。

生成的可执行文件称为 MEX S-Function,其中 MEX 代表“ MATLAB 可执行文件”。MEX 文件扩展名因平台而异。例如,在 32 位 Microsoft® Windows® 系统上,MEX 文件扩展名是 .mexw32