创建并实现基本的 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 所需的任何其他信息(例如状态数)。
mdlInitializeSizes
的 timestwo
实现指定了以下尺寸信息:
零参数
因此,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
来计算模块输出。mdlOutputs
的 timestwo
实现将输入信号乘以 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
。