配色: 字号:
C-mex S函数
2015-10-18 | 阅:  转:  |  分享 
  
if(ssGetNumSFcnParams(S)!=ssGetSFcnParamsCount(S)){retur
n;/ParametermismatchwillbereportedbySimulink/}?
ssSetNumContStates(S,2);/系统有两个连续状态/ssSetNumDiscS
tates(S,0);/系统没有离散状态/if(!ssSetNumInputPorts(S,1))retu
rn;/如果设置输入端口数为1失败,返回//S-functions块只有一个输入端口,当需要多个输入时,使用mux
模块把需要输入的信号集中/成一个向量/ssSetInputPortWidth(S,0,2); /输入信号宽度为2
/ssSetInputPortDirectFeedThrough(S,0,1); /设置馈通标志为1/
if(!ssSetNumOutputPorts(S,1))return;ssSetOutputPortWidth
(S,0,2); /输出信号宽度为2/ssSetNumSampleTimes(S,1); /
1个采样时间/ssSetNumRWork(S,0);? /未使用工作向量/ss
SetNumIWork(S,0);ssSetNumPWork(S,0);ssSetNumModes(S,
0);ssSetNumNonsampledZCs(S,0);ssSetOptions(S,SS_OPTIO
N_EXCEPTION_FREE_CODE);}staticvoidmdlInitializeSampleTimes(S
imStructS){ssSetSampleTime(S,0,CONTINUOUS_SAMPLE_TIME);/
连续系统设采样时间为0,等同于ssSetSampleTime(S,0,0);/ssSetOffsetTime(
S,0,0.0);/偏移量为0/}?#defineMDL_INITIALIZE_CONDITIONSstati
cvoidmdlInitializeConditions(SimStructS){real_Tx0=ssG
etContStates(S);/获得指向连续状态的指针/int_Tlp;for(lp=0;
lp<2;lp++){x0++=0.0;/各状态初始化为0/
}/创建共享内存/hfilemap=OpenFileMapping(
FILE_MAP_WRITE ,false
,"sharedmem"
);/获得指向共享内存的指针/psharedmem=(double)MapViewOfFile(hfi
lemap,FILE_MAP_WRITE,0,0,2sizeof(double));}?staticvoidmdlOu
tputs(SimStructS,int_Ttid){/获得指向输出向量、连续状态向量和输入端口的的指针/
real_Ty=ssGetOutputPortRealSignal(S,0);
real_Tx=ssGetContStates(S);InputRealPtrsT
ypeuPtrs=ssGetInputPortRealSignalPtrs(S,0);UNUSED_ARG(ti
d);/y=Cx+Du//输出方程/y[0]=C[0][0]x[0]+C[0][1]x[
1]+D[0][0]U(0)+D[0][1]U(1);y[1]=C[1][0]x[0]+C[1][1]x[1]+
D[1][0]U(0)+D[1][1]U(1);psharedmem[0]=y[0];/将输出放到共享内存中供
其它进程使用/psharedmem[1]=y[1];}?#defineMDL_DERIVATIVESstati
cvoidmdlDerivatives(SimStructS){real_Td
x=ssGetdX(S);/获得指向状态导数向量的指针/real_Tx=
ssGetContStates(S);InputRealPtrsTypeuPtrs=ssGetInputPort
RealSignalPtrs(S,0);/xdot=Ax+Bu//连续状态方程/dx[0]=A[0]
[0]x[0]+A[0][1]x[1]+B[0][0]U(0)+B[0][1]U(1);dx[1]=A[1][0
]x[0]+A[1][1]x[1]+B[1][0]U(0)+B[1][1]U(1);}?staticvoidmd
lTerminate(SimStructS){UNUSED_ARG(S);UnmapViewOfFil
e(psharedmem);/在仿真结束时,释放共享内存/}#ifdefMATLAB_MEX_FILE/
是否编译成MEX文件/#include"simulink.c"/其中包含MEX文件的接口方
法/#else#include"cg_sfun.h"/代码生成注册函数/#endif
由以上程序可以看出,CMEXS-函数是通过一套宏函数获得指向存储在SimStruct中的输入、输出、状态、状态
导数向量的指针来引用输入输出状态等变量的,从而完成对系统的描述。9.4.6S-函数包装程序
当用户需要将已有的程序、算法集成到Simulink框图模型中时,通常使用S-函数包装程序(MEXS-functionWrapp
ers)来完成这个任务。所谓的S-函数包装程序就是一个可以调用其它模块代码的S-函数,实际上就是通过S-函数的形式来调用其它语言(
MATLAB语言除外)编写的程序。使用外部模块时,需要在S-函数中将已有的代码声明为extern(即外部函数),并且还必须在mdl
Outputs例程中调用这些已有的代码。下面是一个简单的例子如下。C源文件:/wrapfcn.c/doublewra
pfcn(doubleinput){return(input2.0);}CMEXS-函数源
文件如下:#defineS_FUNCTION_NAMEwrapsfcn#defineS_FUNCTION_LEVEL2
#include“simstruc.h”externreal_Twrapfcn((real_Tu);/声明函数为
外部函数/...staticvoidmdlOutputs(SimStructS,int_Ttid){I
nputRealPtrsTypeuPtrs=ssGetInputPortRealSignalPtrs(S,0);real_
Ty=ssGetOutputPortRealSignal(S,0);y=wrapfcn(uPtrs[0]);
/在mdlOutputs例程中调用外部函数wrapfcn/}编译带有外部函数的S-函数时,只需将已有程序
的源文件加在S-函数源文件的后面即可,如下所示:>>mexwrapsfcn.cwrapfcn.c
9.4.7S-functionBuilderSimulink为用户编写常用的CMEXS-函数提供了
方便的开发工具S-functionBuilder。使得用户无需了解众多的宏函数就可以编写出自己的S-函数,只要在对应的位置填入所
需的信息和代码,S-functionBuilder就会自动生成CMEXS-函数源文件,并且编译起来也非常方便,只需单击Bui
ld按钮,就会生成用户所需要的MEX文件。在Simulink库浏览器中双击S-functionBuilde
r块图标,即可打开如图9.14所示的S-functionBuilder界面。很容易看出1、3、4、5选项卡对应着S-函数的四个最
常用的例程。打开S-functionBuilder为用户生成的C源文件,就会发现在各个页面填入的信息和代码被放入了对应的例程中。
下面给出用户使用S-functionBuilder编写S-函数的步骤。(1)首先在S-functionna
me编辑栏里填入S-函数名。(2)如果存在用户参数,在S-functionparameters栏填入用户参
数缺省值。(3)在图9.14所示的S-functionBuilder的Initialization页
中按照提示填入仿真相关信息。(4)在Libraries选项卡中填入所需要的库文件(包括目录)、要包含
的头文件,以及外部函数声明。如图9.15所示。(5)在Outputs、ContinuesDerivativ
es和DiscreteUpdate页面填入输出方程、连续状态方程和离散状态方程,以及其它用户定制代码。(6)
单击Build按钮,完成生成C代码、编译链接等工作。图9.14S-functionBuilder初始化界面图9.1
5S-functionBuilder库文件选项卡第9章S-函数9.4编写CMEXS-函数用C语言
编写的S-函数具有以下优点:(1)执行速度快。(2)实时代码生成。(3)包含已有的C代码。(4)能够访问操作系统接
口。(5)可以编写设备驱动。下面介绍一些用C语言编写S-函数所需的几个基本概念。9.4.1MEX文件
对于M文件S-函数,在MATLAB环境下可以通过解释器直接执行,对于C文件或其它语言编写的S-函数,则需要先编译成可
以在MATLAB内运行的二进制代码:动态链接库或者静态库,然后才能使用,这些经过编译的二进制文件即是所谓的MEX文件,在Windo
ws系统下MEX文件后缀为dll。要将C文件S-函数编译成动态库,需在MATLAB命令行下输入:
>>mexmy_sfunction.c要使用mex命令,首先需要在系统中安装一个C编译器。如果还没有设
置编译器,则要在命令行下输入:>>mex–setup
然后按照提示选取VC、BC或者其它的C编译器。推荐使用VC编译器。生成的文件my_sfunction.dll即是我们需要的
动态库文件。当C文件中使用到其它库文件时,编译时应该在其后加上所需库文件名。9.4.2Simstruct数据结构
一个称为SimStruct的数据结构描述了S-函数中所包含的系统。此结构在头文件simstruc.h中定义。
SimStruct将描述系统的所有信息,即封装系统的所有动态信息。它保存了指向系统的输入、状态、时间等存储区的指针,另外它还包含指
向不同S-函数方法(S-函数例程)的指针。实际上整个Simulink框图模型本身也是通过一个SimStruct数据结构来描述
的,它可以被视为与Simulink框图模型等价的表达。9.4.3工作向量(WorkVector)
在仿真过程中不释放的内存区域称之为持续存储区(PersistentMemoryStorage),为全局变量或局部静态变量分配
的内存就是这样的区域。当一个模型中出现同一个S-函数的多个实例时,这些全局变量或者局部静态变量就会发生冲突,导致仿真不能正确进行。
因为这些实例使用了共同的动态链接库(MEX文件),正如在Windows下多个实例在内存中只有一个映像一样。此时Simulink为用
户提供了工作向量(workvector)来解决这个问题。工作向量是Simulink为每个S-函数实例分配的持续存储区,它完全可以
替代全局变量和局部静态变量。表9.2工作向量函数函数名称功能描述所在例程ssSetNumRWo
rk设置为实数型工作向量维数在mdlInitializeSizes例程中分配,在mdlStart或者mdlInitiali
zeConditions中初始化工作向量ssSetNumIWork为整型工作向量维数ssSetNumPWork为指
针数据类型工作向量维数ssSetRWorkvalue设置实数型工作向量值mdlOutputsssSetIWorkv
alue设置整型工作向量值ssSetPWorkvalue设置指针型工作向量值ssGetRWorkvalue读取
实数型工作向量值mdlUpdate或者mdlderivativesssGetIWorkvalue读取整型工作向量值
ssGetPWorkvalue读取指针型工作向量值下面说明如何使用工作向量来保存和使用一个指向文件的指针
。(1)首先在mdlInitializeSizes例程中设置指针工作向量的维数:ssSetNumPWo
rk(S,1)/设置指针工作向量维数为1/(2)在mdlStart例程中为该指针向量赋值:
staticvoidmdlStart(real_Tx0,SimStructS){FILEfPtr;voi
dPWork=ssGetPWork(S);/获取指向指针工作向量的指针,因为工作向量本身是指针数组,所以这里Pw
ork是指向指针???的指针/fPtr=fopen("file.dat","r");PWork[0]=fPtr;
/将文件指针存入指针工作向量//ssSetPWorkValue(S,0,fPtr);显然更为简洁/}(3
)在仿真结束时释放文件指针:staticvoidmdlTerminate(SimStructS){if(ssGe
tPWork(S)!=NULL){/首先判断是否存在指针工作向量/FILEfPtr;fPtr=(FILE
)ssGetPWorkValue(S,0); /再判断文件指针是否为空/if(fPtr!=NULL){f
close(fPtr);/关闭文件/}ssSetPWorkValue(S,0,NULL); /指针工作向量置空
/}}9.4.4CMEXS-函数流程了解Simulink如何与S-函数相互作用完成动态系统
的仿真对用户编写S-函数是非常有帮助的。前面已经对此进行了介绍,不同的是CMEXS-函数的流程控制更为精细,数据I/O也更为丰
富,但是这里还有一些前面没有涉及到的内容。图9.12显示了S-函数的数据交换过程。图9.12S-函数的
数据交换除了这些输入输出数据外,S-函数还经常用到的内部数据有:(1)连续状态。
(2)离散状态。(3)状态导数。(4)工作向量。访问这
些数据首先需要一套宏函数获取指向存储它们的存储器的指针,然后通过指针来访问。这时就需要特别注意指针的越界访问问题。
图9.13所示为Simulink引擎调用S-函数回调函数的过程。图中并没有列出全部的S-函数回调函数,尤其是初始化阶段远比图中
所示要复杂。几个必需的例程在图中用实线表示,其它的用虚线表示。图9.13CMEXS-函数工作流程9.4.5
CMEXS-函数模板与编写M文件S-函数一样,Simulink同样为用户提供了编写CMEXS-
函数所需的模该文件只包含了常用的几个例程,这对于一般的应用已经足够了。文件sfuntmpl_doc.c则包含了所有的例程,并附有详
细的注释。每个CMEXS-函数的开头应包含下列语句:#defineS_FUNCTION_NAMEyour_sfunct
ion_name_here#defineSFUNCTION_LEVEL2 #include"simstruc.h"
另外在文件的顶部还应包含适当的头文件或定义其它的宏或者变量,就和编写普通的C程序一样。在CMEXS-函数的尾部必然
包含下面几行代码:#ifdefMATLAB_MEX_FILE#include"simulink.c"#else#inc
lude"cg_sfun.h"#endif1.初始化CMEXS-函数的初始化部分
包含下面三个不同的例程函数:(1)mdlInitializeSizes:在该函数中给出各种数量信息。
(2)mdlInitializeSampleTimes:在该函数中给出采样时间。(3)mdlI
nitializeConditions:在该函数中给出初始状态。mdlInitializeSizes通过
宏函数对状态、输入、输出等进行设置。工作向量的维数也是在mdlInitializeSizes中确定的。表9.3S-函数初始
化所需宏函数宏函数定义功能描述ssSetNumContStates(S,numContStates
)设置连续状态个数ssSetNumDiscStates(S,numDiscStates)设置离散状态个数
ssSetNumOutputs(S,numOutputs)设置输出个数ssSetNumInputs(S,numIn
puts)设置输入个数sSetDirectFeedthrough(S,dirFeedThru)设置是否存在直接
前馈ssSetNumSampleTimes(S,numSamplesTimes)设置采样时间的数目ssSetN
umInputArgs(S,numInputArgs)设置输入参数个数设置各种工作向量的维数,实际上是为各个工作
向量分配内存提供依据。见表9.7ssSetNumRWork(S,numIWork)ssSetNumPWork(S,nu
mIWork)2.用输入和输出在CMEXS-函数中,同样可以通过描述该S-函数的Si
mStruct数据结构对输入输出进行处理。在CMEXS-函数中,当需要对一个输入进行处理时,使用宏:input=ssGetI
nputPortRealSignalPtrs(S,index)返回值中含有指向输入向量的指针,其中的每个元素通
过input[i]来访问。指向输出向量的指针通过宏函数output=ssGetOutputPortRealSignal(S,in
dex)得到。如果想知道输出信号的宽度,使用宏:width=ssGetOutputPortWidth(S,index);如果需要
获得指向输入值的指针,使用宏:ssGetInputPortRealSignalPtrs(S,input_index);如果需要获得
指向输出值的指针,使用宏:ssGetOutputReaSignal(S,output_index)。表9.4列出了输入输出相关宏
函数。表9.4输入输出相关宏函数宏函数功能描述ssGetInputPortRealSignalPt
rs获得指向输入的指针(double类型)ssGetInputPortSignalPtrs获得指向输入的指针(其它数
据类型)ssGetInputPortWidth获得指向输入信号宽度ssGetInputPortOffsetTime
获得输入端口的采样时间偏移量ssGetInputPortSampleTime获得输入端口的采样时间ssGetOutpu
tPortRealSignal获得指向输出的指针ssGetOutputPortWidth获得指向输出信号宽度ssG
etOutputPortOffsetTime获得输出端口的采样时间偏移量ssGetOutputPortSampleTime
获得输出端口的采样时间例如:下面的一段代码获得指向输入和输出的指针,然后把输入乘以5后送往输出。static
voidmdlOutputs(SimStructS,int_Ttid){int_T
i;InputRealPtrsTypeuPtrs=ssGetInputPortRealSignalPtrs(S,0
);real_Ty=ssGetOutputPortRealSignal(S,0);
int_Twidth=ssGetOutputPortWidth(S,0);for(i
=0;i3.使用参数使用用户自定义参数时,在初始化中必须说明参数的个数。为了得到指向存储参数的数据结构的指针,使
用宏:ptr=ssGetSFcnParam(S,index);为了得到存储在这个数据结构中指向参数值本身的指针,使用宏:mxGe
tPr(ptr);使用参数值时使用宏:param_value=mxGetPr(ptr)。下面的代码首先获取参数gain的值,然后
输入乘以gain作为输出:#defineGAIN=ssGetSFcnParam(S,0)staticvoidmdlOu
tputs(SimStructS,int_Ttid){int_Ti;Inp
utRealPtrsTypeuPtrs=ssGetInputPortRealSignalPtrs(S,0);re
al_Ty=ssGetOutputPortRealSignal(S,0);int_T
width=ssGetOutputPortWidth(S,0);real_T
gain=mxGetPr(GAIN);for(i=0;i=gain(uPtrs[i]);}}4.使用状态如果S-函数包含连续
的或离散的状态,则需要编写mdlDerivatives或mdlUpdate子函数。若要得到指向离散状态向量的指针,使用宏:ssGe
tRealDiscStates(S);若要得到指向连续状态向量的指针,使用宏:ssGetContStates(S);在mdlDe
rivatives中,连续状态的导数应当通过状态和输入计算得到,并将SimStruct结构体中的状态导数指针指向得到的结果,这通
过下面的宏完成:dx=ssGetdX(S),然后修改dx所指向的值。在多状态的情形下,通过索引得到dx中的单个元素。它们被返回给
求解器通过积分求得状态。需要注意的是,在离散系统中,没有对应于dx的变量,由于状态是由S-函数来更新的,不需要求解器做任何额外的工
作。下面的一段代码描述了连续状态方程:staticvoidmdlDerivatives(SimStructS){
real_T alpha=.01; real_T beta=.02; real_T
dx=ssGetdX(S); dx[0]=(1-alphax[1])x[0]; dx[1
]=(-1+betax[0])x[1];}下面的一段代码描述了离散状态方程:staticvoidmdl
Update(SimStructS,int_Ttid){real_TtempX[2]={0.0,0.0};
real_Tx=ssGetRealDiscStates(S);InputRealPtrsTypeuPtrs=ssG
etInputPortRealSignalPtrs(S,0);UNUSED_ARG(tid);/notusedins
ingletaskingmode/tempX[0]=(1-alphax[1])x[0];;tempX[1]=
(-1+betax[0])x[1];x[0]=tempX[0];x[1]=tempX[1];}【例
9.5】S-函数csfunc描述了一个用状态方程表示的线性连续系统:并通过共享内存与其它程序交换数据。下面是该系统CMEX
S-函数的完整源代码和注释。#defineS_FUNCTION_NAMEcsfunc/S-函数名/#defin
eS_FUNCTION_LEVEL2#include"simstruc.h"#include"windows.h" /创建共享内存所需头文件/#defineU(element)(uPtrs[element]) /宏定义,方便对输入的索引//定义状态方程ABCD阵/staticreal_TA[2][2]={{-0.09,-0.01},???{1,0}/在CS-函数中有一套自己的数据/};/类型表示方法,real_T表示双精/staticreal_TB[2][2]={{1,-7},/度,int_T表示整型。通用的C/{0,-2}/语言数据类型标识同样适用/};staticreal_TC[2][2]={{0,2},{1,-5}};staticreal_TD[2][2]={{-3,0},/注意,存在馈通/{1,0}};doublepsharedmem;/指向共享内存存储区的指针/HANDLEhfilemap;/共享内存句柄/staticvoidmdlInitializeSizes(SimStructS){ssSetNumSFcnParams(S,0);/不含用户参数,所以参数个数设为0/第9章S-函数
献花(0)
+1
(本文系行者hydraul...首藏)