1Matlab中基于C语言的S函数的编写与应用作者:ZhengSimin单位:WHU摘要:Matlab/Simulink中提供了S函数模块,可以实现和C语言和其它语言的程序混编,将C语言和Matlab的强大的仿真能力及数据可视化能力进行了有机结合,可以极大地方便一些以C语言为基础的工程应用的算法仿真和数学实验的进行。本文将对Matlab中基于C语言的S函数的应用及编写方法行相关介绍。关键字:Matlab;S函数;C语言1Matlab的S函数简介“S-函数是系统函数(SystemFunction)的简称,是指采用非图形化的方式(即计算机语言,区别于Simulink的系统模块)描述的一个功能块。Simulink没有单独的语言,但它提供了S函数规则。所谓的S函数可以是一个M文件、FORTRAN程序、C或C++语言程序等,通过特殊的语法规则使之能够被Simulink模型或模块调用。S函数使Simulink更加充实、完备,具有更强的处理能力。S-函数作为与其他语言相结合的接口,可以使用这个语言所提供的强大能力。例如,Matlab语言编写的S-函数可以充分利用MATLAB所提供的丰富资源,方便地调用各种工具箱函数和图形函数;使用C语言编写的S-函数可以实现对操作系统的访问,如实现与其他进程的通信和同步等。Simulink已经提供了大量的内置的系统模块,并且允许用户自定义模块,对于大多数动态系统仿真分析语言,使用Simulink提供的模块即可实现,而无需使用S-函数。但是,当需要开发一个新的通用的模块作为一个独立的功能单元时,使用S-函数实现则是一种相当简便的方法。另外,由于S-函数可以使用多种语言编写,因此可以将已有的代码结合进来,而不需要在Simulink中重新实现算法,从而在某种程度上实现了代码移植。此外,在S-函数中使用文本方式输入公式、方程,非常适合复杂动态系统的数学描述,并且在仿真过程中可以对仿真进行更精确的控制。”具体详细的一些介绍在此不再赘述,本文结合自身的项目经历,推荐一种S函数的用处:Matlab中嵌入其它代码,例如C代码,并参与算法的仿真,然后利用Matlab丰富的图形生成接口函数来实现数据的可视化。备注:本文在所涉及的Matlab版本:Matlab.R2010b2一个S函数仿真例子2在正式对S函数进行介绍前,先看一个例子,这个例子是对网络的随机延时环境进行算法模拟,能够对输入的正常的时钟信号产生0.2s到0.7s不等的随机延时。本小节采用“从整体到局部,从结果到原因”的方式对此示例进行介绍。用数字程序建立一个随机延时为0.3s到0.6s不等的网络随机延时环境,所涉及的Simulink仿真程序结构如图2-1所示。图2-1网络随机延时仿真这是一个比较基本的S函数的仿真应用结构图,采样周期设置为0.1s。整个仿真图的采样周期在解算器里面设置,单个仿真图的采样周期双击本模块,在如图2-2的界面中对“Sampletime”进行设置即可。图2-2设置模块采样周期在图2-1所示的网络随机延时仿真程序中,输入一个时钟模块,然后经过一个单位的延时(单位延时的大小等于采样周期),然后再经过网络随机延时的S函数模块(程序代码见附录一),然后再对S函数模块输入输出的两路信号合并在同一坐标系下输出。输出的模块有两种,“Scope”数字示波器模块和“ToWorkspace”数据导出模块。“Scope”可以直接将数据在坐标系下进行可视化显示,可以实现坐标轴不同级别的缩放,如图2-3所示;“ToWorkspace”则将数据以指定的变量名称存放在工作区间中,然后在工作区间中可以编写相应的程序直接对此数据进行引用。z1UnitDelaysimoutToWorkspaceScopeRandomTimeDelayS-Fun-TimeDelayClock3图2-3“Scope”整体图(左)和局部放大图(右)因为“Scope模块”默认的输出是黑底背景色,这些图片数据放置到论文中采用黑白打印时,完全是漆黑一片,为此可以采用一些处理方式:在Matlab的命令输入区域输入命令:“set(0,'ShowHiddenHandles','On');set(gcf,'menubar','figure')”然后“Scope图”就变成“Figure图”了,并配备了相应的修改菜单,通过修改“【Edit】—【AxesProperties】”然后就可以调出修改菜单,对坐标系统进行显示参数修改。在对“背景色、标尺颜色、曲线显示样式、坐标轴标记„„”进行修改后,图2-3就变成如图2-4的效果了(直接输出的是矢量图)。图2-4“Scope”虚拟示波器图形变换如果需要对输出数据进行程序引用,则可以使用“ToWorkspace”模块将数据引出到工作区间中,在仿真程序2-1运行结束后,在工作区间中会生成一个全局变量,如图2-5所示。Times(s)Delay(s)4图2-5导出仿真数据到Workspace中当数据导出到Workspace工作空间中后,就可以利用Matlab提供的一些函数接口来进行任意的程序设计并完成对数据的操作和引用了。例如,可以通过如表2-1所示的M代码对导出的数据simout(输出的两组数据)和tout(仿真的时间标记)进行作图。表2-1引用数据绘制曲线图的M程序figure1=figure;axes1=axes('Parent',figure1,'FontName','Consolas','FontSize',11);hold(axes1,'all');x=linspace(0,10,101);p=plot(x,simout(:,1),'c--',x,simout(:,2),'r:');%grid;set(p,'LineWidth',2,'Parent',axes1);xlabel('Time(s)','FontName','Consolas','FontSize',12);ylabel('Y-Value','FontName','Consolas','FontSize',12);title('TheRandomDelay','FontSize',12,'FontName','Consolas');legend('Clock','RandomDelayColck');运行表2-1所示的M文件后,就可以完成对工作区间的数据进行曲线绘制,绘制的结果如图2-6所示,当然如果对图形不满意,还可以通过菜单对显示图的坐标、背景色、线条样式、标记等等进行修改。数据导出到工作区间后,其意义并不仅仅局限于调用更强大的plot绘图函数来完成对数据的可视化,更重要意义在于这样做可以将本程序的实验数据实现通用性。这些数据从Simulink导出到Worksapce后,一方面它可以被任意的Matlab函数和其它的Simulink仿真程序所引用,另外一方面这些数据可以提供可以数据修改的可视化界面,可以复制到例如Excel等数据处理系统中,然后再供其它5程序进行引用。总之,“数据导出模块ToWorkspace”(及相应的“数据导入模块FromWorkspace”)为Simulink和外界数据交流提供了接口,实现了数据的共享。图2-6导出数据的Plot曲线图3S函数模块在上一节上对一个S函数的应用仿真程序例子的整体结构及功能进行了介绍,下面将对S函数的结构和编写方式(本文主要是基于C语言来实现)进行详细介绍。3.1S函数的结构关于S函数的学习资料,最齐全的就在Matlab自带的Help帮助文档中。在安装Malab之后,安装程序也顺便安装好了相关的学习文档。在【Simulink】-【User’sGuide】-【DevelopingS-Functions】分支里面就有详细的关于S函数的说明,如图3-1所示。在帮助文档里面有关于S函数的工作原理,运行流程及如何完成S函数的代码编写,在此不再赘述。本文只是简单的对自己除了这些文档外的一些学习经历进行一下总结。02468100246810Time(s)Y-ValueTheRandomDelayClockRandomDelayColck6图3-1S函数的相关学习文档3.2S函数的代码编写用C或C++语言编写实现的S函数被称为“MEXS-Functions”,MEXS函数由一系列的回调方法组成。用户只需要按照一定的结构对代码进行编写即可,具体的代码结构,在Matlab中会提供一个模板“sfuntmpl_doc.c”,这个文件存在于“D:\ProgramFiles\MATLAB\R2010b\simulink\src”中,用户只需要先复制一份此模板,然后再修改相应的名字,就可以往里面写好的回调函数的函数体中写入自己想实现的C代码即可。这里面主要包括:输入何输出端口设置、参数的提取、输出端口的设置等等。具体的编写方法也请参照帮助文档。3.3S函数的编译在编写好S函数的实现代码*.c文件后,还需要在Matlab的命令输出窗口中进行命令行输入,完成对源代码的编译,这样才能被S函数模块所调用。在命令窗口中输入命令:“mex-gRandomTimeDelay.c”M文件S-函数在MATLAB环境下可以通过解释器直接执行,而C文件或其它语言编写的C-函数,则需要先编译成可以在MATLAB内运行的二进制代码:动态连接库或静态连接库,然后才可以使用,这些经过编译的二进制文件就称作7MEX文件。用MEX命令来对*.c文件进行编译,然后会在相应的目录下生成对应的*.mexw32文件。然后就可以在simulink模块中引用这个s函数了。图3-2S函数的C代码编译3.4S函数的调用在Simulink模块中拖入S函数模块,然后双击模块,将模块和当前目录下的S函数的*.mexw32文件相对应起来,然后就可以实现函数的调用了。例如在第2节中对RandomTimeDelay函数调用时,双击S函数模块,在如图3-3所示的设置对话框中设置S函数的名称(在C代码中指定的)和输入参数[1,2,8](分别代表:输入信号宽度、最小随机时延、最大随机时延)。具体的还有些涉及到函数的传值,及S函数中一些参数的解析等等,都参考帮助文档。图3-3S函数模块参数设置3.5S函数的调试8程序规模比较小的时候,使用printf来将中间变量打印到Matlab的Workspace窗口中,然后看运行情况。程序规模比较大时,使用专门的C语言开发集成环境,例如VC6.0或者EclipseCDT等等来开发,调试成功后,再将代码直接复制到MEX-S函数的模板中,然后再在Matlab中使用MEX命令编译一次,就可以在Simulink中进行调用了。在Matlab的帮助文档中提供了一些使用VC++或者VS来调试C代码的方法,根据但要,好像可以一边使用Matlab仿真,一边在VS中进行断点调试,还可以查看中间变量值,但是自己一直都没有试成功,今后有时间有需求后再仔细研究下是什么问题吧。附上Matlab帮助文档中提供的“Matlab和外部工具现场联调”方法的目录,如图3-4所示。图3-4帮助文档中提供的CMEXS函数调试方法3.6MEX-S函数编写的辅助工具关于C语言的开发环境有很多,目前比较流行的有VC6.0和VS系列,但是因为“EclipseCDT”具有:免费、占用空间小、绿色免安装、代码导航效果好、用户自定义功能强大,本文就选择了“EclipseCDT+MinGW”的组合方式来进行C语言开发。具体的搭建C语言的开发环境搭建方法,在此不再赘述,下面将介绍如何在EclipseCDT中将Matlab/Simulink的S函数的库进行链接引用。S函数的C代码文件中的所有的变量函数可以在Matlab的帮助文档中检索出来,里面涉及的一些头文件(如,“tmwtypes.h”,“mex.h”,“matrix.h”)或例如int_T的数据类型可以在目录“D:\ProgramFiles\MATLAB\R2010b\extern\include”检索到。虽然EclipseCDT无法实现对C代码编译生成能够直接被Simulink引用的目标文件,但是却