S 函数(system function)是模块的核心,是完成功能实现的关键。S 函数的
编写可以使用多种程序语言,其中 M 语言是最常用的,同时也是最简单的。在
运用 M 语言进行 s 函数编写的时候,可以调用 MATLAB 提供的函数,简化了开
发过程。但是如果要与其他进程通讯或驱动外部硬件接口,则要调用 API 函数,
这样就需要用 C 语言来开发 S 函数。较 M 语言的开发,C 语言开发 S 函数更具有
灵活性,但是相对复杂一些。
C 语言写 S 函数,顾名思义,运用 C 语言语法,依照 S 函数格式要求,最后
在 MATLAB 中 MEX 命令编译,编译成功既得函数。
S 函数格式可简单看成:初始化、采样时间设定、系统输出、结束四个部分。
对应的函数分别为 mdlInitializeSizes()、mdlInitializeSampleTimes()、mdlOutputs()、
mdlTerminate()。这四个函数是一个 S 函数必不可少的,缺少任何一个在编译的
时候都无法通过,输出信息会提示哪个函数没有写。
一个最基本的 C 语言 S 函数模版如下:
#define S_FUNCTION_NAME name
#define S_FUNCTION_LEVEL 2
#include“simstruc.h”
Static void mdlInitializeSizes(SimStruct *S){}
Static void mdlInitializeSampleTimes(SimStruct *S){}
Static void mdlOutputs(SimStruct *S,int_T tid){}
Static void mdlTerminate(SimStruct *S){}
#ifdef MATLAB_MEX_FILE
#include“Simulink.c”
#else
#include“cg_sfun.h”
#endif
S 函数的运行依托于 Simulink,Simulink 的运行是采用循环方式,计算各采样
时间点的系统状态得到的,由此可理解 S 函数,在初始化之后,S 函数也通过循
环完成输出状态计算。
结合上述格式,首先自定义 S 函数名称,然后定义 S 函数级别,这里写 2,1
级是老版本 Simulink 使用的,现已经不是用,之所以保留 1 级是为了兼容原有的
老程序,现在写的 S 函数都是 2 级的。接下来将需要的头文件包含进来,这里必
须包含 simstruc.h 文件,这里的 SimStruc 是 Simulink 提供的数据结构,S 函数中
的输入输出等信息都包含在这个结构体中,同时,在编写 S 函数的时候也要把使
用到的 C 语言库中的头文件包含进来,所有的 C 语言库文件在这里都可以使用。
接下来即可按照格式顺序编写代码。最后要注意,如果用于仿真则添加 Simulink.c
文件,如果用于 RTW 代码生成,则添加 cg_sfun.h 头文件。这里的 RTW 代码生
成是指非内嵌的 S 函数,如果要做一个内嵌的 S 函数则需要在 S 函数中添加
mdlRTW()函数,并额外编写 TLC 文件。其中,TLC 文件用于优化的 C 代码生成,
mdlRTW()函数则把模块参数传递到生成的代码当中。具体 TLC 文件的编写方法这
里不再赘述。
除了上述必需的函数外,系统提供了其他可选用的函数,功能各异,例如
mdlStart()等。
只要理解了 Simulink 运行方式就可以理解文件的开发过程了,其中,系统函数和
特定的变量类型都可以在 SimStruct 数据结构中找到。至此,基本的 S 函数都可
以编写了。
在编写结束后,将 S 函数源文件存储在 MATLAB 路径下,打开 MATLAB 命令
行窗口,选择当前路径为存储路径,运用 MEX 命令编译 C 源文件,如果成功则
在当前路径下生成一个后缀名为 mexw32 的文件(后缀名随系统环境不同而不
同,32 指 32 位系统,如果是 64 位系统则不同),如果使用的是 MATLAB 早期
版本,则生成的文件后缀名为 dll,即动态链接库。两者等价,这里可以用 dll 来
理解 mexw32 文件的作用。
当 S 函数编译写好之后,还不能在 Simulink 中直接调用,因为缺少一个可视
化的模块。这时候打开 Simulink,在用户自定义模块库中找到名称为 S_function
的模块,并将它拖拽到模型文件中。可以把它看成一个 S 函数的通用的容器,下
面介绍如何把编写好的 S 函数放入这个容器,即模块封装。
下面讲解 S 函数的模块封装方法:
右键点击 S 函数模块,选择 MASK 选项,弹出封装编辑框,在这里有四个栏,
分别为图标、参数、初始化、文档。其中参数最为重要。图标即模块上显示的图
形,可以编辑自己需要的文字,也可以用图片包装模块。初始化可以对模块参数
进行默认设置,文档中可以编写模块说明和帮助链接。而最重要的参数栏中,要
把模块对应的 S 函数的参数列出来,每一个参数有三种形式:编辑、下拉框、复
选框。如果是下拉框形式,要编辑对应的选项。如果 S 函数需要响应不同操作,
可以对每一个参数选择编写对应的回调函数。值得一提的是,可以用 set_param()
函数直接对封装模块进行操作,可以实现更加灵活的封装。
封装编辑好后,右键点击 S 函数模块选择 LOOK UNDER MASK 选项,在弹出
的对话框中,首先填写原文件,把编译过的文件(mexw32)放置在当前目录下,
在对话框中正确填写文件名(不写后缀),然后再把封装时定义的参数变量按顺
序写在参数框中,每个参数用逗号隔开,注意变量名必须与封装填写的名称一致,
且数目相同。最有一个框不用填写。点击 OK。则完成了对 S 函数的模块封装。
完成上述两个步骤之后,即完成了自定义的 Simulink 驱动模块,这时要做的
工作是对该模块经行测试。若测试通过,可以将该模块添加进 Simulink 模块库,
和 Simulink 提供的模块并没有任何的区别,方便以后工作中的重复利用。若测试
未通过,则返回 S 函数编写工作中,重新编写 S 函数,重复上述步骤。由于封装
只是一个将 S 函数图形化的过程,当测试出现问题的时候,可以确定是 S 函数的
工作出现了问题,而不是因为封装引发的。
由于水平有限,这里讲解不那么易于理解,可以在学习过程中,参照现有程序和 MATLAB
帮助文档,从自己动手编写简单的 S 函数入手,一旦完成一次,以后就很好理解了。
4.1 C MEX-file S-function 简介
定义了 S-function 模块的 C MEX-file 必须在仿真过程中向 Simulink 提供模型信
息。在仿真中 Simulink、ODE 求解器、MEX-file 协作完成指定任务。这些任务
包括:定义初始条件和模块特性,计算微分、离散状态和输出。
Simulink 与 C MEX-file S-function 模块的交互仍是通过 S-function 的回调方法。
每个回调方法执行一个预定义的,实现仿真所需功能的任务。S-function 可以执
行任何其实现的任务。一系列 C MEX-file S-function 实现的回调方法,都远大于
M-file S-function 中的。与 M-file S-function 不同的是,C MEX-file 可以访问并
修改 Simulink 内部用来存储 S-function 信息的数据结构。更多的回调方法和对
Simulink 内部数据结构的访问能力,使得 C MEX-file S-function 可以实现更丰富
的模块特性,如处理矩阵信号和多种数据类型。
C MEX-file S-function 只需实现 Simulink 定义的回调方法的一个小子集即可。如
果不实现某个回调方法,相应的功能将被省略掉。这有利于快速开发简单的模块。
通常 C MEX-file S-function 的形式如下:
#define S_FUNCTION_NAME your_sfunction_name_here
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
static void mdlInitializeSizes(SimStruct *S)
{}
static void mdlTerminate(SimStruct *S)
{}
#ifdef MATLAB_MEX_FILE
#include "simulink.c"
/* Is this file being compiled as a MEX-file? */
/* MEX-file interface mechanism */
#else
#include "cg_sfun.h"
/* Code generation registration function */
#endif
mdlInitializeSizes 是 Simulink 与 S-function 交互时调用的第一个方法。随后
Simulink 将调用其他 S-function 方法(都以 mdl 开头)。仿真结束时,Simulink
调用 mdlTerminate。
注意:与 M-file S-function 不同,C MEX-file S-function 回调方法不是每个都具
有 flag 参数。这是因为,Simulink 仿真时直接在适当的时间调用每个回调方法。
4.2 自动建立 S-function 模块
S-Function Builder 是通过规范定义和用户提供的 C 代码建立 S-function 的
Simulink 模块。S-Function Builder 还用作普通的 S-function 在 Simulink 模型中
的包装。
通过 S-Function Builder 建立 S-function。
1. 将 MATLAB 当前目录设置到需要建立 S-function 的目录。
2. 创建新的 Simulink 模型。
3. 从 Simulink User-Defined Functions library 中将 S-Function Builder 拖入新
建的 ulink 模型。
4. 双击模块打开 S-Function Builder 对话框。
5. 输入所需信息和用户代码。(详见下节)
6. 如果还未设置 mex 编译器,用 mex –setup 在 MATLAB 命令行设置。
7. 点 Build 按钮,启动建立过程。Simulink 建立 MEX 文件实现指定的 S-function,
并存放在当前目录
8. 保存包含 S-Function Builder 模块的模型。
部署生成的 S-Function
要在其他模型中使用生成的 S-Function,首先必须检查生成的 S-Function 所在
的目录是否在 MATLAB 路径中。然后把 S-Function Builder 模块从创建它的模
型复制到目标模型并设置其参数。
S-Function Builder 如何建立 S-Function
4.3 S-Function Builder 对话框
4.4 基本的 C MEX-file S-function 实例
本节介绍一个 C MEX-file S-function 实例:timestwo,实现输出信号放大为输入
信号的 2 倍。以下是模型图:
以下是 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 will be reported by Simulink */
}
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);
ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_USE_TLC_WITH_ACCELERATOR);
}
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
static void mdlOutputs(SimStruct *S, int_T tid)
{
int_T
i;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T
int_T
*y
= ssGetOutputPortRealSignal(S,0);
width = ssGetOutputPortWidth(S,0);
for (i=0; i
这 个 实 例 包 括 3 部 分 : 宏 定 义 和 头 文 件 ; 回 调 方 法 的 实 现 ;Simulink ( 或
Real-Time Workshop)接口。
宏定义和头文件
示例程序中包含两个宏定义:
#define S_FUNCTION_NAME timestwo
#define S_FUNCTION_LEVEL 2
第一个指定 S-function 的名字,第二个指示该 S-function 是 Level-2 格式的。
然后是包含头文件"simstruc.h",在其中定义了 SimStruct 数据结构和 MATLAB
应用程序接口(API)函数。SimStruct 是 Simulink 用于保持 S-function 信息的数
据结构,"simstruc.h"中还有用于 MEX 文件设置或获取 SimStruct 属性值的宏定
义。
回调方法的实现
mdlInitializeSizes:
Simulink 调用 mdlInitializeSizes 方法查询 S-function 模块的输入输出端口数,
端口容量,以及 S-function 所需的其他对象(如,状态个数等)。
Timestwo 实现的 mdlInitializeSizes 方法指定了下面信息
● 零参数:
意味着 S-function 对话框的参数框必须为空。否则,Simulink 将报告参数不匹配。
● 一个输入和一个输出端口:
输入输出端口的宽度可以动态变化。Simulink 将把所有输入信号乘以 2 作为输出
信号的结果。注意,在这种情况下(一个输入一个输出端口),Simulink 对
S-function 宽度的默认处理是输入和输出宽度相等。
● 一个采样时间
例子 timestwo 在 mdlInitializeSampleTimes 中指定实际的采样时间。
● 代码无异常处理
指定无异常处理的代码可以加速 S-function 的执行。作这项指定必须谨慎。通常,
如果用户 S-function 与 MATLAB 没有交互,指定无异常处理的代码是安全的。
mdlInitializeSampleTimes:
Simulink 调 用 mdlInitializeSampleTimes 设 置 S-function 的 采 样 时 间 。 每 当
timestwo 模块的前驱模块执行一次,timestwo 模块就执行一次,所以 timestwo
模块采用继承的采样时间。
mdlOutputs
Simulink 在每个时间步调用 mdlOutputs 计算模块的输出。Timestwo 模块的
mdlOutputs 方法实现了将输入信号乘以 2,将结果写到输出信号。
Timestwo 模块的 mdlOutputs 方法使用了 SimStruct 中的宏:
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
来实现对输入信号的访问。这个宏返回一个指针向量,必须通过*uPtrs[i]来访问。
Timestwo 模块的 mdlOutputs 方法还使用了
real_T *y = ssGetOutputPortRealSignal(S,0);
来访问输出信号。这个宏返回一个包含模块输出的数组。
S-function 使用
int_T width = ssGetOutputPortWidth(S,0);
来得到通过该模块的信号宽度。最后 S-function 循环通过输入得到输出。
mdlTerminate
执行仿真结束最后的任务。这是一个强制的 S-function 过程。不过由于 timestwo
模块不需要任何终止操作,所以其函数为空。
Simulink/Real-Time Workshop 接口
在 S-function 的最后,下面的特定代码将该例子关联到 Simulink 或者 Real-Time
Workshop:
#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif
生成 timestwo 实例
在命令行键入:mex timestwo.c
mex 命令将把 timestwo.c 编译、链接为一个可由 Simulink 动态加载执行的模块。
这是一个动态链接库文件,在 Window 中,是个 dll 文件。
4.5 C MEX S-functions 模板
Simulink 提 供 了 C
S-functions 模 板 。
/simulink/src/sfuntmpl_basic.c 提供了 C MEX S-functions 的常用
功 能 方 法 。 /simulink/src/sfuntmpl_doc.c 则 描 述 了 C MEX
S-functions 的全部实现方法。
S-function 源代码文件的要求
用户 S-function 要访问 SimStruct 结构,则必须在文件头部具备下列宏定义和头
文件:
MEX
#define S_FUNCTION_NAME your_sfunction_name_here
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
这里 your_sfunction_name_here 指得是用户 S-function 的模块名。这些声明使
S-function 能访问 SimStruct 结构。
当被编译为 MEX 文件时,matlabroot/simulink/include/simstruc.h 将包括下面头
文件:
Header File
Description
matlabroot/extern/include/tmwtypes.h
General data types, e.g., real_T
matlabroot/extern/include/mex.h
MATLAB MEX-file API routines
matlabroot/extern/include/matrix.h
MATLAB MEX-file API routines
当被编译为 MEX 文件时,matlabroot/simulink/include/simstruc.h 将包括下面头文
件:
Header File
Description
matlabroot/extern/include/tmwtypes.h
General types, e.g., real_T
matlabroot/rtw/c/libsrc/rt_matrx.h
Macros for MATLAB API routines
用户 C MEX S-function 文件尾部必须包括下面声明:
#ifdef MATLAB_MEX_FILE /* Is this being compiled as MEX-file? */
#include "simulink.c"
/* MEX-file interface mechanism */
#else
#include "cg_sfun.h"
/* Code generation registration func */
#endif
这些声明确保针对用户具体应用选择适当的代码:
● 当被编译为 MEX 文件的时候,simulink.c 文件将被包含。
● 当被编译为关联了实时工具箱(Real-Time Workshop)的独立程序时,
cg_sfun.h 文件将被包含。
SimStruct 结构