Matlab 中S-函数模板翻译

10.0 基础知识

(1)Simulink仿真过程

Simulnk仿真分为两步:初始化、仿真循环。仿真是由求解器控制的,求解器主要作用是:计算模块输出、更新模块离散状态、计算连续状态。求解器传递给系统的信息包括:时间、输入和当前状态。系统的作用:计算模块的输出、更新状态、计算状态导数,然后将这些信息传递给求解器。求解器和系统之间的信息传递是通过不同标志来控制的。

(2)模块内参数意义

s-function name 是s函数的名称(注意这里是在matlab中所写的脚本语言的s函数)

s-function parameters 是s函数中的其他输入参数

s-function modules 是其他语言所写的函数如英文介绍所说如果函数名称是src.c,即c语言所写只需要填写src不要叫后缀

注意:在simulink中运行需要将工作目录设置成所用s函数的目录中否则会报错

10.1 S函数概述

S函数也称为Simulink中的系统函数,是用来描述模块的Simulink宏函数,支持M、C等多种语言。当Simulink默认的模块不能满足用户的需求时,用户可以通过S函数自己打造一个模块,实现自定义的算法或期待的动作。

10.2 S函数的类型

  • S函数有多种类型,按照语言分类有M、C、C++、Fortran等编写的;
  • 按照所支持功能多少分类,包括Level1和Level2;
  • 按照执行方式分类,可分为直接解释运行的M S函数和编译为Mex文件后执行的C Mex S函数。

Level1 M S函数输入输出端口最多位1且数据维数固定,Level2 M S函数的输入输出端口个数可以为多个,数据维数也可以动态改变。

编写一个既能用于仿真又能用于代码生成的算法时,需要给S函数编写同名的TLC文件。

由于M语言需要调用MATLAB解释器,故C Mex S函数运行速度比M S函数更快。

10.3 S函数的要素

一个Simulink模块包括输入、输出以及内部的状态量。除了3要素之外,还有一个无处不在的时间量。

所谓状态量,根据系统性质分为连续系统中的微分量和离散系统中的差分量。

dx/dt=f(t,x,u)

y=g(t,x,u)

10.4 S函数的组成及执行顺序

执行顺序:

 官方给出的s-function的执行顺序和上面的执行顺序有点不同

伪代码形式:

main
{
初始化模型;  //0;mdlInitializeSizes
//计算下一个采样时间点(大步长);  //4;mdlGetTimeOfNextVarHit
while(未到达仿真终止时间)
{
    计算下一个采样时间点(大步长); //4;mdlGeTimeOfNextVarHit 根据官网流程修改
计算模块的输出;  //3;mdlOutputs
更新离散状态量;  //2;mdlUpdate
if(此模型带有连续状态模块)  
{
     here:计算模块的输出; //3;mdloutputs
   计算微分;  //1;mdlDerivatives
if(精度未达标)
goto here;
过零检测;
计算写一个采样时间点(大步长);
}
}
执行仿真终止动作;   //9;mdlTerminate
}

仿真运行时,模型首先要对模块进行初始化,这个过程包括模块的实例化:输入/输出端口、信号唯独、端口数据类型及采样时间等的确定,模块参数的获取及个数检查,并决定模块的执行顺序等。

  • 实例化:Simulink标准库中提供的模块类似于C++等面向对象语言中的一个类,每当模块被拷贝或拖曳到模型中时,就相当于创建了这个类的一个对象,继承了这个类的属性并载入了默认的构造函数方法对其参数、端口等各个属性进行了初始化工作。
  • 信号维度:一根信号线传递的数据不仅可以是标量,也可以是一个向量或矩阵,一个模块的输出端口将具有这个数据维度的信号传递给相连的信号线然后再传递给下一个模块的输入端口,这些都是需要在初始化阶段确定下来的。
  • 端口数据类型:模块的输出/输出数据是浮点数还是固定点数,是8为、16位、32为或64位,有无符号,内建类型或者用户自定义类型,这些也在初始化阶段指定。
  • 采样时间:对于Simulink模型来说,解算器中的一个步长决定了整个模型最小的采样时间间隔。
  • 模型中模块的执行顺序:当众多模块同时存在于一个模型中时,Simulink是有明确的顺序优先度的。

S函数子方法表:

子方法 作用说明
初始化 在第一个采样时间的仿真之前运行的函数,用来初始化模块,包括设定输入/输出端口的个数和维数,输入是否直接馈入,参数个数设定采样时间,当使用工作向量时还需要为其分配存储空间
下一个采样时间点计算 根据模型解算器的算法求得下一个采样时间点,通常用于变步长模块
输出函数计算 在每一个major step计算模型所有的输出口的输出值
离散状态更新 在每一个major step都进行一次离散状态的更新计算,在输出函数之后
积分计算 如果模型具有连续状态,才采取此方法。将major step分隔为数个minor step,在每一个minor step里进行一次输出函数与积分计算。积分计算主要用来更新连续状态。当模型中存在非采样过零检测时,还会在minor step中进行过零检测
模型终止 当模型终止仿真时调用的子函数,用于清除不用的变量,释放内存空间等动作

Matlab为了用户使用方便,有一个S函数的模板sfuntmpl.m,一般来说,我们仅需要在sfuntmpl.m的基础上进行修改即可。在主窗口输入edit sfuntmpl即可出现模板函数的内容,可以详细地观察其帮助说明以便更好地了解S函数的工作原理。模板函数的定义形式为function[sys,x0,str,ts]=sfuntmpl(t,x,u,flag),一般来说,S函数的定义形式为[sys,x0,str,ts]=sfunc(t,x,u,flag,p1,…Pn),其中的sfunc为自己定义的函数名称,以上参数中,t、x、u分别对应时间、状态、输入信号,flag为标志位,其取值不同,S函数执行的任务和返回的数据也是不同的,pn为额外的参数sys为一个通用的返回参数值,其数值根据flag的不同而不同,x0为状态初始数值,str在目前为止的matlab版本中并没有什么作用,一般str=[]即可,ts为一个两列的矩阵,包含采样时间和偏移量两个参数,如果设置为[0 0],那么每个连续的采样时间步都运行,[-1 0]则表示按照所连接的模块的采样速率进行,[0.25 0.1]表示仿真开始的0.1s后每0.25s运行一次,采样时间点为TimeHit=n*period+offset。

S函数的使用过程中有2个概念值得注意:

  1. direct feedthrough,系统的输出是否直接和输入相关联,即输入是否出现在输出端的标志,若是为1,否则为0,一般可以根据在flag=3的时候,mdlOutputs函数是否调用输入u来判断是否直接馈通。
  2. dynamically sized inputs,主要给出连续状态的个数、离散状态的个数、输入数目、输出数目和直接馈通否。

S函数中目前支持的flag选择有0、1、2、3、4、9等几个数值,下面说一下在不同的flag情况下S函数的执行情况。

  1. flag=0。进行系统的初始化过程,调用mdlInitializeSizes函数,对参数进行初始化设置,比如离散状态个数、连续状态个数、模块输入和输出的路数、模块的采样周期个数、状态变量初始数值等。
  2. flag=1。进行连续状态变量的更新,调用mdlDerivatives函数。
  3. flag=2。进行离散状态变量的更新,调用mdlUpdate函数。
  4. flag=3。求取系统的输出信号,调用mdlOutputs函数。
  5. flag=4。调用mdlGetTimeOfNextVarHit函数,计算下一仿真时刻,由sys返回。
  6. flag=9。终止仿真过程,调用mdlTerminate函数。

  在实际仿真过程中,Simulink会自动将flag设置为0,进行初始化过程,然后将flag的数值设置为3,计算模块的输出,一个仿真周期后,Simulink将flag的数值先后设置为1和2,更新系统的连续和离散状态,再将其设置为3,计算模块的输出,如此循环直至仿真结束条件满足。

  在S函数的编写过程中,首先需要搞清楚模块中有多少个连续和离散状态,离散模块的采样周期是如何的,同时需要了解模块的连续和离散的状态方程分别是什么,输出如何表示。下面以实例说明S函数的具体应用。

10.5 使用不同语言编写S函数

不同S函数的特点:

S函数  特点
Level1M   支持简单的MATLAB接口及少数的API
Level2 M   支持扩展的S函数API及代码生成功能,使用场合更加广泛
C MEX      提供更灵活的编程方式,即可手写C代码也可以调用既存的C/C++或Fortran代码。要求掌握很多C MEX S函数API用法及TLC代码编写方法,才能够制定具有代码生成功能的C MEX S函数

10.5.1 Level1 M S函数

[sys,x0,str,ts]=f(t,x,u,flag,p1,p2,...)

其中f是S函数的函数名,Simulink会在仿真过程中的每个步长内多次调用f。

flag的值随着仿真过程自动变化,其值对应的S函数子方法如下:

flag值 Level1 M S函数子方法名 说明
0 mdlInitializeSizes 定义S函数的基本属性,如输入/输出维数、连续/离散状态变量个数、采样时间、以及输入是否为直接馈入等
1 mdlDerivatives 连续状态变量的微分函数,这里通常通过给定的微分计算表达式,通过积分计算得到状态变量的值
2 mdlUpdate 更新离散状态变量
3 mdlOutputs 计算S函数的输出
4 mdlGetTimeOfNextVarHit 仅在变离散采样时间情况下使用,用于计算下一个采样时时刻的绝对时间,若模块不是变步长此函数不会执行
9 mdlTerminate 在仿真结束时执行一些必要的动作,如清除临时变量,或显示提示信息等

说明——直接馈入:

如果S函数的输出y或采样时间t与输入u有直接联系,就是直接馈入;否则不存在直接馈入情况。如果若干直接馈入的模块通过反馈构成了一个环形,就会出现所谓的代数环。

Level1 M S输入参数表:

输入参数 功能
t 当前时刻的仿真时间
x 状态变量向量
u 输入向量
pn,n=1,2,3,... 用户自定义参数,数目不定

Level1 M S输出参数表:

输出参数 功能
sys 通用输出参数,根据flag的值来决定返回值,比如flag=3时返回S函数的输出信号;flag=2时则返回更新后的离散状态变量的值;flag=1时根据设置的微分值积分计算出连续状态变量的值
x0 状态变量的初始值,仅在flag=0时有效,其余情况被忽略
str 保留变量,用户只能将其初始化为[ ]
ts S函数的采样时间,由一个二维的数组表示

ts=[m,n]中m为模块的采样时间周期,表示每隔多长时间采样一次,n为采样时间的偏移量,表示与采样时间周期所表示的时刻点的偏差时间。

例如:

采样时间表示 含义
[0,0] 连续采样时间
[-1,0] 继承S函数输入信号或父层模型的采样时间
[0.5,0.1] 离散采样时间,从0.1s开始每0.5s采样一次
[0.25,0;1,0.1] [0,0.1,0.25,0.5,0.75,1,1.1,...]

示例:

dx1/dt=-0.5572x1-0.7814x2+u1-u2;

dx2/dt=0.7814x1+2u2;

y=1.9691x1+6.4493x2;

由方程可知:

 说明:这个s函数是有问题的,最重的图像结果不正确,主要是由于这篇文章的博主对function sys = mlupdate(t, x, u)理解不到位,本博主进行了修正并且说明了什么地方不正确以及我是怎么去验证这个程序不正确的,修改文章见:s函数中第一个程序修改(介绍function sys = mlupdate(t, x, u)用法)

 A=[-0.5572,-0.7814;0.7814,0];
B=[1,-1;0,2];
C=[1.9691,6.4493];
 function [sys,x0,str,ts,simStateCompliance] = sfun_state01(t,x,u,flag,A,B,C)
switch flag,
case 0,
[sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes;
case 1,
sys=mdlDerivatives(t,x,u);
case 2,
sys=mdlUpdate(t,x,u,A,B);
case 3,
sys=mdlOutputs(t,x,u,C);
case 4,
sys=mdlGetTimeOfNextVarHit(t,x,u);
case 9,
sys=mdlTerminate(t,x,u);
otherwise
DAStudio.error('Simulink:blocks:unhandledFlag', num2str(flag));
end
function [sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes
sizes = simsizes;
sizes.NumContStates = 0;
sizes.NumDiscStates = 2;
sizes.NumOutputs = 1;
sizes.NumInputs = 2;
sizes.DirFeedthrough = 0;
sizes.NumSampleTimes = 1;
sys = simsizes(sizes);
x0 = [0 0]';
str = [];
ts = [0,0];
simStateCompliance = 'UnknownSimState';
function sys=mdlDerivatives(t,x,u) sys = [];
function sys=mdlUpdate(t,x,u,A,B)
% update state variable

sys = A * x + B * u; function sys=mdlOutputs(t,x,u,C)
% update output
sys = C * x; function sys=mdlGetTimeOfNextVarHit(t,x,u) sampleTime = 1;
sys = t + sampleTime;
function sys=mdlTerminate(t,x,u) sys = [];

注意:这里需要进行设置才能显示成阶梯形式的图像(右边图像是改变成线性显示)

示例:积分

 1 function [sys,x0,str,ts,simStateCompliance] = int_hyo(t,x,u,flag)
2 switch flag,
3 case 0,
4 [sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes;
5 case 1,
6 sys=mdlDerivatives(t,x,u);
7 case 2,
8 sys=mdlUpdate(t,x,u);
9 case 3,
10 sys=mdlOutputs(t,x,u);
11 case 4,
12 sys=mdlGetTimeOfNextVarHit(t,x,u);
13 case 9,
14 sys=mdlTerminate(t,x,u);
15 otherwise
16 DAStudio.error('Simulink:blocks:unhandledFlag', num2str(flag));
17 end
18 function [sys,x0,str,ts,simStateCompliance]=mdlInitializeSizes
19 sizes = simsizes;
20 sizes.NumContStates = 1;
21 sizes.NumDiscStates = 0;
22 sizes.NumOutputs = 1;
23 sizes.NumInputs = 1;
24 sizes.DirFeedthrough = 0;
25 sizes.NumSampleTimes = 1; % at least one sample time is needed
26 sys = simsizes(sizes);
27 x0 = [0];
28 str = [];
29 ts = [0 0];
30 simStateCompliance = 'UnknownSimState';
31 function sys=mdlDerivatives(t,x,u)
32 sys = u;
33 function sys=mdlUpdate(t,x,u)
34 sys=[];
35 function sys=mdlOutputs(t,x,u)
36 sys=x;
37 function sys=mdlGetTimeOfNextVarHit(t,x,u)
38 sampleTime = 1; % Example, set the next hit to be one second later.
39 sys = t + sampleTime;
40 function sys=mdlTerminate(t,x,u)
41 sys = [];

这个程序用问题,输入信号是sin(t),s-function的功能是积分的功能,则sin(t)的积分应该是-cos(t),最大值不可能是2,而上面的结果的图像显示的结果是0-2的一个图像,显然又问题,博主重新梳理了一遍进行了更改,更改文章: s函数中积分程序更改

10.5.2 Level2 M S函数

Level2 M S函数使得用户能够使用MATLAB语言来编写支持多个输入/输出端口的自定义模块,并且每个端口都能够支持包括矩阵在内的Simulink支持的所有数据类型。

1. Setup子方法

Setup子方法是Level2 M S函数体中唯一调用的语句,对模块的属性和其他子方法进行初始化,Setup子方法类似Level1 M S函数中mdlInitializeSizes子方法的功能,并且相比之下功能更加强大,在Setup中不仅可以设置多输入多输出,而且每个输出的端口信号的维数可以是标量数或矩阵甚至是可变维数,另外S函数的其他子方法也是通过Setup子方法进行注册的,因此Setup可以成为Level2 M S函数的根本。

Setup子方法实现以下功能:

  • 设定模块输入输出端口的个数;
  • 设定每一个端口的数据类型、数据维度、实数复数性和采样时间等;
  • 规定模块的采样时间;
  • 设定S函数参数的个数;
  • 注册S函数的子方法(将子方法函数的句柄传递到实时对象的RegBlockMethod函数的对应属性中)。

S函数实时对象的属性列表:

实时对象属性成员 说明
NumDialogPrms 模块GUI参数个数
NumInputPorts 输入端口数
NumOutputPorts 输出端口数
BlockHandle 模块句柄,只读
CurrentTime 当前仿真时间,只读
NumContStates 连续状态变量数目
NumDworkDiscStates 离散状态变量数目
NumRuntimePrms 运行时参数个数
SampleTimes 产生输出的模块的采样时间
NumDworks 离散工作向量个数

函数端口的属性列表:

属性端口名 说明
Dimensions 端口数据维度
DatatypeID/Datatype 端口数据类型,可以通过ID号指定也可以直接指定数据类型名
Complexity 端口数据是否为复数
DirectFeedthrough 端口数据是否直接馈入
DimensionsMode 端口维数是固定或可变的(fixed/variable)

实时对象的方法列表:

实时对象方法 说明
ContStates 获取模块的连续状态
DataTypeIsFixedPoint 判断数据类型是否为固定点数
DatatypeName 获取数据类型的名称
DatatypeSize 获取数据类型大小
Derivatives 获取连续状态的微分
DialogPrm 获取GUI中的参数
Dwork 获取Dwork向量
FixedPointNumericType 获取固定点数据类型的操作
InputPort 获取输入端口
OutputPort 获取输出端口
RuntimePrm 获取运行时参数

其他子方法列表:

子方法名 说明
PostPropagationSetup 设置工作向量及状态变量的函数(可选)
InitializeConditions 在仿真开始时被调用的初始化函数(可选)
Start 在模型运行仿真时调用一次,用来初始化状态变量和工作向量(可选)
Outputs 在每个步长里计算模型输出
Updata 在每个步长里更新离散状态变量的值(可选)
Derivatives 在每个步长里更新连续状态变量的微分值(可选)
Terminate 在仿真结束时调用,用来清除变量内存

2. PostPropagationSetup

PostPropagationSetup子方法是用来初始化Dwork工作向量的方法,规定Dwork向量的个数及每个向量的维数、数据类型、离散状态变量的名字和虚拟性,以及是否作为离散变量使用。

Dwork向量是Simulink分配给模型中每个S函数实例的存储空间块。当不同S函数块之间需要通过全局变量或者静态变量进行数据交互时,必须在S函数中使用Dwork向量来进行变量存储。

Dwork属性列表:

名称 含义
Name 名字
Dimensions 数据维数
DatetypeID 数据类型
Complexity 是否复数
UsedAsDiscState 是否作为离散变量使用

代表不同数据类型的ID(整数):

数据类型 ID
ingerited -1
double 0
single 1
int8 2
uint8 3
int16 4
uint16 5
int32 6
uint32 7
boolean或定点类型 8

3. InitializeConditions/Start子方法

InitializeConditions子方法可以用来初始化状态变量或者Dwork工作向量的值。

Start子方法跟InitializeConditions子方法功能一致,但仅仅在仿真开始的时候初始化一次,而S函数模块放置在是能子系统中时其InitializeConditions子方法在每次子系统被使能时都会被调用。

4. Output子方法

Output子方法跟Level1 M S函数的mdlOutputs子方法作用一样,用于计算S函数的输出。

5. Updata子方法

Updata子方法跟Level1 M S函数中mdlUpdata子方法作用相同,用于计算离散状态变量的值。

6. Derivatives子方法

Derivatives子方法跟Level1 M S函数中的mdlDerivatives子方法作用相同,用于计算并更新连续状态变量的值。

7. Terminate子方法

S函数的收尾工作放在Terminate子方法中进行,如存储空间的释放,变量的删除等。

阿布罗狄:

艾欧里亚:

1 m1=imread('阿布罗狄.jpg');
2 m1=m1(:,:,1);
3 m2=imread('艾欧里亚.jpg');
4 m2=m2(:,:,1);

 1 function sfun_image_merge(block)
2
3 setup(block);
4
5 function setup(block)
6 block.BlockHandle
7 % Register number of ports
8 block.NumInputPorts = 2;
9 block.NumOutputPorts = 0;
10
11 % Setup port properties to be inherited or dynamic
12 block.SetPreCompInpPortInfoToDynamic;
13
14 % Override input port properties
15 block.InputPort(1).Dimensions = [375 500];
16 block.InputPort(1).DatatypeID = 3; % uint8
17 block.InputPort(1).Complexity = 'Real';
18 block.InputPort(1).DirectFeedthrough = false;
19
20
21 block.InputPort(2).Dimensions = [375 500];
22
23 block.InputPort(2).DatatypeID = 3; % uint8
24 block.InputPort(2).Complexity = 'Real';
25 block.InputPort(2).DirectFeedthrough = false;
26
27
28 % Register parameters
29 block.NumDialogPrms = 0;
30
31 % Register sample times
32 % [0 offset] : Continuous sample time
33 % [positive_num offset] : Discrete sample time
34 %
35 % [-1, 0] : Inherited sample time
36 % [-2, 0] : Variable sample time
37 block.SampleTimes = [0 0];
38
39 % Specify the block simStateCompliance. The allowed values are:
40 % 'UnknownSimState', < The default setting; warn and assume DefaultSimState
41 % 'DefaultSimState', < Same sim state as a built-in block
42 % 'HasNoSimState', < No sim state
43 % 'CustomSimState', < Has GetSimState and SetSimState methods
44 % 'DisallowSimState' < Error out when saving or restoring the model sim state
45 block.SimStateCompliance = 'DefaultSimState';
46
47
48 block.RegBlockMethod('Terminate', @Terminate); % Required
49
50
51 function Terminate(block)
52 imshow((block.InputPort(1).data + block.InputPort(2).data) / 2);

好丑!看不出什么效果。

再试一次:

融合后:

原来核心语句在这里:

1 imshow((block.InputPort(1).data + block.InputPort(2).data) / 2);

10.5.3 C MEX S函数

S函数支持的语言除了MATLAB/Simulink自身环境的M语言之外,最常用的就是C语言了。使用C语言编写S函数成为C Mex Sh函数,相比于解释运行的M S函数,在仿真过程中不需要反复调用MATALB解释器,而是在运行前将.c文件编译成mexw32/mexw64类型的可执行文件,运行速度和效率上有明显优势;并且C语言拥有编程语言生的灵活性优势。

C Mex S函数的运行机制与M S函数是一致的,包括常用的初始化、更新、微分计算、输出和终止等子方法,这些子方法几乎可以与Level2 M S函数的子方法一一对应起来。但是由于语言的不同,所使用的数据结构形式上也有区别。

对应关系:

C Mex S函数子方法 Level2 M S函数子方法 子方法功能
mdlInitializeSizes Setup 模块属性初始化
mdlDerivatices Derivatives 更新连续状态变量的微分值
mdlInitializeConditions InitializeConditions 初始化工作向量的状态值
mdlOutputs Outputs 计算模块的输出
mdlSetWorkWidths PostPropagationSetup 当S函数模块存在于使能子系统中时,每次子系统被使能均进行工作向量属性的初始化工作
mdlStart Start 在仿真开始时初始化工作向量及状态变量的属性
mdlTerminate Terminate 在仿真终止时所调用的子方法
mdlUpdate Updata 更新离散状态变量的子方法
mdlRTW WriteRTW 将S函数中获取到的GUI参数变量值写入到rtw文件中以使TLC文件用来生成代码的子方法

1. C Mex S函数的构成

  1. 最开头必须定义的两个宏:C Mex S函数名及C Mex S函数的等级。
  2. 头文件包含部分C Mex S函数核心数据结构simstruct类型的声明及其他库文件,另外用户根据使用需要也可以包含其他头文件。
  3. 参数对话框访问宏函数的定义。
  4. 紧接着定义C Mex S函数的各个子方法。
  5. S函数必须根据使用情况包含必要的源文件和头文件,从而将该S函数文件与Simulink或Simulink Coder产品进行连接。

2. C Mex S函数的编译

编译型语言编写的程序执行之前,需要一个专门的编译过程,把程序编译成为机器语言的文件,如exe文件或mexw32/menw64文件,运行时不需要重新翻译。

 1 >> mex -setup
2 MEX 配置为使用 'Microsoft Visual C++ 2010 (C)' 以进行 C 语言编译。
3 Warning: The MATLAB C and Fortran API has changed to support MATLAB
4 variables with more than 2^32-1 elements. In the near future
5 you will be required to update your code to utilize the
6 new API. You can find more information about this at:
7 http://www.mathworks.com/help/matlab/matlab_external/upgrading-mex-files-to-use-64-bit-api.html.
8
9 要选择不同的语言,请从以下选项中选择一种命令:
10 mex -setup C++
11 mex -setup FORTRAN
12 >> mex -setup C++
13 MEX 配置为使用 'Microsoft Visual C++ 2010' 以进行 C++ 语言编译。
14
15 >> mex sfun_c_filter.c
16 使用 'Microsoft Visual C++ 2010 (C)' 编译。
17 MEX 已成功完成。

3. C Mex S函数的应用

  1 //sfun_c_filter.c#define S_FUNCTION_NAME  sfun_c_filter
2 #define S_FUNCTION_LEVEL 2
3
4 #include "simstruc.h"
5
6 #define COEF_IDX 0
7 #define COEF(S) mxGetScalar(ssGetSFcnParam(S,COEF_IDX))
8 /*================*
9 * Build checking *
10 *================*/
11
12
13 /* Function: mdlInitializeSizes ===============================================
14 * Abstract:
15 * Setup sizes of the various vectors.
16 */
17 static void mdlInitializeSizes(SimStruct *S)
18 {
19 ssSetNumSFcnParams(S, 1);
20 if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
21 return; /* Parameter mismatch will be reported by Simulink */
22 }
23
24 if (!ssSetNumInputPorts(S, 1)) return;
25 ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
26 ssSetInputPortDirectFeedThrough(S, 0, 1);
27
28 if (!ssSetNumOutputPorts(S,1)) return;
29 ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
30
31 ssSetNumDWork(S, 1);
32 ssSetDWorkWidth(S, 0, DYNAMICALLY_SIZED);
33
34
35 ssSetNumSampleTimes(S, 1);
36
37
38 /* specify the sim state compliance to be same as a built-in block */
39 ssSetSimStateCompliance(S, USE_DEFAULT_SIM_STATE);
40
41 ssSetOptions(S,
42 SS_OPTION_WORKS_WITH_CODE_REUSE |
43 SS_OPTION_EXCEPTION_FREE_CODE |
44 SS_OPTION_USE_TLC_WITH_ACCELERATOR);
45 }
46
47
48 /* Function: mdlInitializeSampleTimes =========================================
49 * Abstract:
50 * Specifiy that we inherit our sample time from the driving block.
51 */
52 static void mdlInitializeSampleTimes(SimStruct *S)
53 {
54 ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
55 ssSetOffsetTime(S, 0, 0.0);
56 ssSetModelReferenceSampleTimeDefaultInheritance(S);
57 }
58
59 #define MDL_INITIALIZE_CONDITIONS
60 /* Function: mdlInitializeConditions ========================================
61 * Abstract:
62 * Initialize both discrete states to one.
63 */
64 static void mdlInitializeConditions(SimStruct *S)
65 {
66 real_T *x = (real_T*) ssGetDWork(S,0);
67 x[0] = 0.0; // initial to 0.0
68 }
69 /* Function: mdlOutputs =======================================================
70 * Abstract:
71 * y = 2*u
72 */
73 static void mdlOutputs(SimStruct *S, int_T tid)
74 {
75 int_T i;
76 InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
77 real_T *y = ssGetOutputPortRealSignal(S,0);
78 int_T width = ssGetOutputPortWidth(S,0);
79 real_T *x = (real_T*) ssGetDWork(S,0);
80 real_T Lc = COEF(S);
81
82 for (i = 0; i < width; i++)
83 {
84 y[i] = (*uPtrs[i] - x[i]) * Lc + x[i];
85 }
86
87 /* save the current output as the DWork Vector */
88 for (i=0; i<width; i++) {
89 x[i] = y[i];
90 }
91 }
92
93
94 /* Function: mdlTerminate =====================================================
95 * Abstract:
96 * No termination needed, but we are required to have this routine.
97 */
98 static void mdlTerminate(SimStruct *S)
99 {
100 }
101
102
103
104 #ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
105 #include "simulink.c" /* MEX-file interface mechanism */
106 #else
107 #include "cg_sfun.h" /* Code generation registration function */
108 #endif

1 g_coef=0.005;

4. C Mex S函数的自动生成

Simulink提供了两个工具箱帮助用户快速生成C Mex S函数。

  • S-Function Builder:根据用户的配置自动生成C Mex S函数;
  • Legacy Code Tool:能够将既存的或者用户自定义的C代码打包生成内联的C Mex S函数,并且能够生成嵌入式C代码应用于嵌入式目标芯片。

(1)S-Function Builder

3个部分:

  1. S函数名称及参数列表显示部分;
  2. S函数端口及参数的结构树形图;
  3. 与S函数各个子方法对应的配置界面。

配置界面与S函数子方法的对应关系:

配置界面 对应的S函数子方法
Initialization mdlInitializationSizes中状态变量和mdlInitializeSampleTimes中采样时间的设定及mdlInitializeConditions中中状态变量初始值的确定
Data Properties mdlInitialize中输入输出端口和参数的个数、数据类型
Libraries 无对应子方法,此处填入编译用户代码时所需的头文件
Outputs mdlOutputs子方法
(Continous) Derivatives mdlDerivatives子方法
(Discrete) Update mdlUpdate子方法
Build Info 无子方法对应,用来显示编译信息,并提供给用户功能选项,生成C代码时可以选择是否生成TLC文件,是否同时编译C文件为mex文件等

Initialization

  • Number of discrete/continuous states:S函数中需要设置几个离散/连续状态变量,这些状态变量的初始值通过Discrete/Continuous states IC配置。当多个状态变量存在时,使用向量形式给出,如给出3个连续/离散状态变量的初始值[0,0,0],元素个数必须与初始化的状态变量个数相同;
  • Sample mode:提供了3种采样时间,
    1. Inherited:S函数继承输入端口的采样时间;
    2. Continuous:S函数采用连续采样时间,在每个步长更新输出值;
    3. Discrete:S函数采用离散采样时间,Sample time value设置采样时间间隔。

Data Properties

Data Properties将S函数相关的数据量和信息分散在4个子页面上进行配置。

左侧提供了4个按钮,为端口和参数的添加、删除、上移和下移操作。

  • Input ports:输入端口的名称、维数、数据行列、实数虚数性和总线类型设置;
  • Output ports:输出端口的名称、维数、数据行列、实数虚数性和总线类型设置;
  • Parameters:增加参数并设置其名称、数据类型和实数虚数性。

注:Dimensions下拉菜单支持1-D和2-D数据,Rows中输入数据的行数,-1表示行数是动态的,Columns仅在2-D时有效。

  • Data type attributes:对输入/输出端口的数据类型进行设定,包括内建类型和固定点类型。当选择固定点类型时,可对数据类型的详细信息进行配置,包括数据字长(Word length)和小数位数(Fraction length)等。

Library

Library页面中主要用于添加头文件、外部源文件、用户自定义代码相关文件和函数声明。

  • Library/Object/Source files(One per line):当S函数的子方法配置中使用了外部的库、目标文件或源文件时,需要将库文件、目标文件和源文件的全路径写在此对话框中。如果这些文件存放在当前路径之下,则只需要写入文件名即可。

如:customfunctions.lib、userfunctions.obj、freesource.c。

用户可以在此处通过关键字LIB_PATH、INC_PATH、SRC_PATH添加搜索路径,在关键字之后给出文件路径,S-Function Builder会自动选择到关键字后的路径中搜索相关文件。

如:

1 SRC_PATH D:\externalsource
2 LIB_PATH $ MATLABROOT\customobjs
3 INC_PATH C:\customfolder
4 customfunctions.lib
  • Includes:当用户自定义代码出现在S-Function Builder的任何配置中时,所涉及的头文件、函数声明、变量和宏定义等都应该在此使用#include语句进行包含。如果所包含的头文件是标准C语言库,使用尖括号,如#include<math.h>;如果所包含的头文件是用户自定义代码,使用双引号,如#include "my_device.h"。特别的,当被包含的头文件不在当前路径时,需要在Library/Object/Source files(One per line)对话框中使用INC_PATH来指定这个头文件所在的文件路径。
  • External functions Declaretion:当S-Function Builder中需要在状态变量和Outputs子方法中调用某些外部函数计算,并且这些函数既不是Simulink自带的,也不被Includes中列出的头文件所包含时,在此处进行函数声明。

Outputs

Outputs子页面中输入的内容就是S函数Outputs子方法函数体内容,C Mex S函数的输入/输出端口数据的部分代码可以省略,直接使用输入/输出端口名和下标索引号即可作为输入/输出变量,并规定它们之间的关系。

输入的直接馈入在页面左下角的勾选框设置,勾选Inputs are needed in the output function(direct feedthrough)后,即可在对话框中使用输入端口变量名。

用户在该对话框中编写的代码将在编译时打包为一个函数sfun_Outputs_wrapper,插入到函数体中,再在C Mex S函数的mdlOutputs中进行调用。

(Continous) Derivatives

用户可以填入计算连续状态变量的代码,连续状态变量按照维数索引号,可以使用xC[0],xC[1]或者dx[0],dx[1]表示,这些变量必须是double类型。输入/输出端口号和参数必须在Data Properties中定义过的端口名和参数变量名。

此对话框中所填入的内容被打包为一个函数sfun_Derivatives_wrapper函数,再在mdlDerivatives中调用。

(Discrete) Update

用于输入mdlUpdate子方法对应的内容,无需使用宏函数获取输入/输出端口、参数和离散状态变量的值。离散状态变量使用xD[0],xD[1]等引用即可。输入/输出端口号和参数必须在Data Properties中定义过的端口名和参数变量名。

此对话框中所填入的内容被打包为一个函数sfun_Update_wrapper函数,再在mdlUpdate中调用。

Build Info

用于显示编译C代码和可执行文件时的编译信息。

Build options:

Build options 作用说明
Show compile steps 在Compilation diagnostics框记录每一个编译步骤信息
Generate wrapper TLC 生成TLC文件以支持代码生成或加速仿真模式
Enable access to SimStruct 使得Outputs、Derivatives、Updates 3个页面的代码可以使用SimStruct类提供的宏函数
Create a debuggable MEX-file 生成mex文件时包含调试信息
Save code only 只生成C Mex S函数代码不生成MEX文件

例:使用S-Function Builder生成一阶滤波器

Y(t)=(U(t)-Y(t-1)) ×Lc+Y(t-1)

离散状态变量:1个;

输入:u,输出:y,Dimen为1-D,Row为-1;

double型参数filter_coef,值为0.05;

Outputs:

1 y[0]=(u[0]-xD[0])*filter_coef[0]+xD[0];

Update:

1 xD[0]=y[0];

Build!

得到:

报错:

错误使用 mex,未找到支持的编译器。

参考:

https://ww2.mathworks.cn/matlabcentral/answers/101105-how-do-i-install-microsoft-windows-sdk-7-1#answer_110453

将以下两个文件从C:\Users\lenovo\AppData\Roaming\MathWorks\MATLAB\R2014a拷贝到C:\Users\lenovo\AppData\Roaming\MathWorks\MATLAB\R2018a,获得成功。

(2)Legacy Code Tool

能够将既存的C/C++代码转换为Simulink模型中可以使用的C Mex S函数,同时也能生成TLC文件。

Legacy Code Tool将用户既存的算法代码插入到C Mex S函数的Outputs子方法中,用户需要提供足够的信息,这些信息包括:为MATLAB安装一个C编译器,S函数名,既存算法的函数原型,及为了编译既存C文件所需要的其他头文件、源文件及其存放路径。

legacy_code命令可以完成以下几件事情:

  1. 根据既有C代码初始化Legacy Code Tool的数据结构;
  2. 生成可用于仿真的C Mex S函数;
  3. 将生成的S函数编译链接为动态可执行文件(mex文件);
  4. 生成一个封装起来的模块来调用S函数;
  5. Simulink Coder组件会生成S函数的模块级TLC文件。

LCT(Legacy Code Tool)的使用流程:

通常使用'initialize'作为legacy_code的参数初始化一个LCT对象:

 1 >> lct_spec=legacy_code('initialize')
2
3 lct_spec =
4
5 包含以下字段的 struct:
6
7 SFunctionName: ''
8 InitializeConditionsFcnSpec: ''
9 OutputFcnSpec: ''
10 StartFcnSpec: ''
11 TerminateFcnSpec: ''
12 HeaderFiles: {}
13 SourceFiles: {}
14 HostLibFiles: {}
15 TargetLibFiles: {}
16 IncPaths: {}
17 SrcPaths: {}
18 LibPaths: {}
19 SampleTime: 'inherited'
20 Options: [1×1 struct]

LCT对象的各个属性:

属性名 作用说明 
SFunctionName 所生成S函数的名字 
InitializeConditionsFcnSpec 应用于InitializeConditions子方法中的既存C代码函数原型 
OutputFcnSpec 应用于OutputFcn子方法中的既存C代码函数原型 
StartFcnSpec 应用于StartFcn子方法中的既存C代码函数原型 
TerminateFcnSpec 应用于TerminateFcn子方法中的既存C代码函数原型 
HeaderFiles 声明既存C函数及其他需要编译的头文件 
SourceFiles 定义既存C函数及其他需要编译的源文件 
HostLLibFiles/TargetLibFiles 主机/目标端编译C文件所依赖的库文件 
 IncPaths LCT搜索路径寻找编译需要的头文件 
 SrcPaths LCT搜索路径寻找编译需要的源文件 
 LibPaths Lct搜索路径寻找编译需要的库和目标文件 
 SampleTime 采样时间 
 Options 控制S函数Options的选项 
  • legacy_code('help'):打开LCT工具的详细使用说明的帮助文档;
  • legacy_code('sfcn_cmex_generate',lct_spec):根据lct_spec生成S函数源文件;
  • legacy_code('compile',lct_spec):对生成的S函数进行编译链接;
  • legacy_code('slblock_generate',lct_spec,modename):生成一个封装模块调用生成的S函数,并自动将此模块添加到名为modename的模型文件里;
  • legacy_code('sfcn_tlc_generate',lct_spec):生成S函数配套的TLC文件,用于加速仿真模型或通过Simulink模型生成代码;
  • legacy_code('rtwmakecfg_generate',lct_spec):生成rtwmakecfg.m文件,此文件是用于生成适用于当前lct_spec对象的makefile的M脚本。

例:使用Legacy Code Tool集成正弦C代码

 1 //EmMath.c
2
3
4 #include"EmMath.h"
5
6 const unsigned short SinTbl[] = {0x0000, //0
7 0x0019, 0x0032, 0x004B, 0x0064, 0x007D, 0x0096, 0x00AF, 0x00C8, 0x00E2, 0x00FB,//10
8 0x0114, 0x012D, 0x0146, 0x015F, 0x0178, 0x0191, 0x01AA, 0x01C3, 0x01DC, 0x01F5,//20
9 0x020E, 0x0227, 0x0240, 0x0258, 0x0271, 0x028A, 0x02A3, 0x02BC, 0x02D4, 0x02ED,//30
10 0x0306, 0x031F, 0x0337, 0x0350, 0x0368, 0x0381, 0x0399, 0x03B2, 0x03CA, 0x03E3,//40
11 0x03FB, 0x0413, 0x042C, 0x0444, 0x045C, 0x0474, 0x048C, 0x04A4, 0x04BC, 0x04D4,//50
12 0x04EC, 0x0504, 0x051C, 0x0534, 0x054C, 0x0563, 0x057B, 0x0593, 0x05AA, 0x05C2,//60
13 0x05D9, 0x05F0, 0x0608, 0x061F, 0x0636, 0x064D, 0x0664, 0x067B, 0x0692, 0x06A9,//70
14 0x06C0, 0x06D7, 0x06ED, 0x0704, 0x071B, 0x0731, 0x0747, 0x075E, 0x0774, 0x078A,//80
15 0x07A0, 0x07B6, 0x07CC, 0x07E2, 0x07F8, 0x080E, 0x0824, 0x0839, 0x084F, 0x0864,//90
16 0x087A, 0x088F, 0x08A4, 0x08B9, 0x08CE, 0x08E3, 0x08F8, 0x090D, 0x0921, 0x0936,//100
17 0x094A, 0x095F, 0x0973, 0x0987, 0x099C, 0x09B0, 0x09C4, 0x09D7, 0x09EB, 0x09FF,//110
18 0x0A12, 0x0A26, 0x0A39, 0x0A4D, 0x0A60, 0x0A73, 0x0A86, 0x0A99, 0x0AAB, 0x0ABE,//120
19 0x0AD1, 0x0AE3, 0x0AF6, 0x0B08, 0x0B1A, 0x0B2C, 0x0B3E, 0x0B50, 0x0B61, 0x0B73,//130
20 0x0B85, 0x0B96, 0x0BA7, 0x0BB8, 0x0BC9, 0x0BDA, 0x0BEB, 0x0BFC, 0x0C0C, 0x0C1D,//140
21 0x0C2D, 0x0C3E, 0x0C4E, 0x0C5E, 0x0C6E, 0x0C7D, 0x0C8D, 0x0C9C, 0x0CAC, 0x0CBB,//150
22 0x0CCA, 0x0CD9, 0x0CE8, 0x0CF7, 0x0D06, 0x0D14, 0x0D23, 0x0D31, 0x0D3F, 0x0D4D,//160
23 0x0D5B, 0x0D69, 0x0D76, 0x0D84, 0x0D91, 0x0D9F, 0x0DAC, 0x0DB9, 0x0DC6, 0x0DD2,//170
24 0x0DDF, 0x0DEB, 0x0DF8, 0x0E04, 0x0E10, 0x0E1C, 0x0E28, 0x0E33, 0x0E3F, 0x0E4A,//180
25 0x0E55, 0x0E60, 0x0E6B, 0x0E76, 0x0E81, 0x0E8B, 0x0E96, 0x0EA0, 0x0EAA, 0x0EB4,//190
26 0x0EBE, 0x0EC8, 0x0ED1, 0x0EDB, 0x0EE4, 0x0EED, 0x0EF6, 0x0EFF, 0x0F07, 0x0F10,//200
27 0x0F18, 0x0F21, 0x0F29, 0x0F31, 0x0F39, 0x0F40, 0x0F48, 0x0F4F, 0x0F56, 0x0F5D,//210
28 0x0F64, 0x0F6B, 0x0F72, 0x0F78, 0x0F7F, 0x0F85, 0x0F8B, 0x0F91, 0x0F96, 0x0F9C,//220
29 0x0FA1, 0x0FA7, 0x0FAC, 0x0FB1, 0x0FB6, 0x0FBA, 0x0FBF, 0x0FC3, 0x0FC7, 0x0FCB,//230
30 0x0FCF, 0x0FD3, 0x0FD7, 0x0FDA, 0x0FDE, 0x0FE1, 0x0FE4, 0x0FE7, 0x0FE9, 0x0FEC,//240
31 0x0FEE, 0x0FF0, 0x0FF2, 0x0FF4, 0x0FF6, 0x0FF8, 0x0FF9, 0x0FFB, 0x0FFC, 0x0FFD,//250
32 0x0FFE, 0x0FFE, 0x0FFF, 0x0FFF, 0x0FFF, 0x0FFF}; //256
33
34
35 /****************************************************
36 Function name: Em_Sin
37 description: calculate sin(theta)
38 input: Angle(0x3FFFFF equal one Cycle
39 output:
40 ****************************************************/
41 signed long Em_Sin(unsigned long Angle)
42 {
43 unsigned long AngleTemp;
44 signed long SineValue;
45
46 AngleTemp = Angle >> 12;
47 AngleTemp &= 0x03FF; //0~1024
48
49 if (AngleTemp <= 256)
50 {
51 SineValue = SinTbl[AngleTemp];
52 }
53 else if (AngleTemp <= 512)
54 {
55 AngleTemp = 512 - AngleTemp;
56 SineValue = SinTbl[AngleTemp];
57 }
58 else if (AngleTemp <= 768)
59 {
60 AngleTemp -= 512;
61 SineValue = -SinTbl[AngleTemp];
62 }
63 else if (AngleTemp <= 1024)
64 {
65 AngleTemp = 1024 - AngleTemp;
66 SineValue = -SinTbl[AngleTemp];
67 }
68
69 return (SineValue);
70 }
//EmMath.h

#ifndef __QM_MATH_H__
#define __QM_MATH_H__ #define PI 3.1415926 signed long Em_Sin(unsigned long Angle); #endif
 1 %lct_trial.m<br>clc;
2 clear all;
3 close all;
4 bdclose all;
5 %%
6 lct_spec = legacy_code('initialize');
7 lct_spec.SFunctionName = 'sfun_Em_Math';
8 lct_spec.HeaderFiles = {'EmMath.h'};
9 lct_spec.SourceFiles = {'EmMath.c'};
10 % signed long Q12_Sin(unsigned long Angle)
11 lct_spec.OutputFcnSpec = 'int32 y1 = Em_Sin(uint32 u1)';
12 legacy_code('sfcn_cmex_generate', lct_spec);
13 legacy_code('compile', lct_spec);
14 legacy_code('slblock_generate', lct_spec, 'lct_model');
15 %% struct model input
16 stop_time = get_param(gcs, 'StopTime');
17 simin.time = [0:str2num(stop_time)]';
18 simin.signals.values = [0:length(simin.time) - 1]';
19 simin.signals.demensions = [length(simin.time) 1];
1 ### Start Compiling sfun_Em_Math
2 mex('-IC:\Users\lenovo\Desktop', '-c', '-outdir', 'C:\Users\lenovo\AppData\Local\Temp\tp90d85757_f87e_4031_a3f5_a7e8c4e1b287', 'C:\Users\lenovo\Desktop\EmMath.c')
3 使用 'Microsoft Visual C++ 2010 (C)' 编译。
4 MEX 已成功完成。
5
6 ### Finish Compiling sfun_Em_Math
7 ### Exit
  1 //sfun_Em_Math.c
2 /**
3 * sfun_Em_Math.c
4 *
5 * ABSTRACT:
6 * The purpose of this sfunction is to call a simple legacy
7 * function during simulation:
8 *
9 * int32 y1 = Em_Sin(uint32 u1)
10 *
11 * Simulink version : 9.1 (R2018a) 06-Feb-2018
12 * C source code generated on : 04-Feb-2020 11:38:21
13 *
14 * THIS S-FUNCTION IS GENERATED BY THE LEGACY CODE TOOL AND MAY NOT WORK IF MODIFIED
15 */
16
17 /**
18 %%%-MATLAB_Construction_Commands_Start
19 def = legacy_code('initialize');
20 def.SFunctionName = 'sfun_Em_Math';
21 def.OutputFcnSpec = 'int32 y1 = Em_Sin(uint32 u1)';
22 def.HeaderFiles = {'EmMath.h'};
23 def.SourceFiles = {'EmMath.c'};
24 legacy_code('sfcn_cmex_generate', def);
25 legacy_code('compile', def);
26 %%%-MATLAB_Construction_Commands_End
27 */
28
29 /* Must specify the S_FUNCTION_NAME as the name of the S-function */
30 #define S_FUNCTION_NAME sfun_Em_Math
31 #define S_FUNCTION_LEVEL 2
32
33 /**
34 * Need to include simstruc.h for the definition of the SimStruct and
35 * its associated macro definitions.
36 */
37 #include "simstruc.h"
38
39 /* Specific header file(s) required by the legacy code function */
40 #include "EmMath.h"
41
42
43 /* Function: mdlInitializeSizes ===========================================
44 * Abstract:
45 * The sizes information is used by Simulink to determine the S-function
46 * block's characteristics (number of inputs, outputs, states, etc.).
47 */
48 static void mdlInitializeSizes(SimStruct *S)
49 {
50 /* Number of expected parameters */
51 ssSetNumSFcnParams(S, 0);
52
53
54 /* Set the number of work vectors */
55 if (!ssSetNumDWork(S, 0)) return;
56 ssSetNumPWork(S, 0);
57
58 /* Set the number of input ports */
59 if (!ssSetNumInputPorts(S, 1)) return;
60
61 /* Configure the input port 1 */
62 ssSetInputPortDataType(S, 0, SS_UINT32);
63 {
64 int_T u1Width = 1;
65 ssSetInputPortWidth(S, 0, u1Width);
66 }
67 ssSetInputPortComplexSignal(S, 0, COMPLEX_NO);
68 ssSetInputPortDirectFeedThrough(S, 0, 1);
69 ssSetInputPortAcceptExprInRTW(S, 0, 1);
70 ssSetInputPortOverWritable(S, 0, 1);
71 ssSetInputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL);
72 ssSetInputPortRequiredContiguous(S, 0, 1);
73
74 /* Set the number of output ports */
75 if (!ssSetNumOutputPorts(S, 1)) return;
76
77 /* Configure the output port 1 */
78 ssSetOutputPortDataType(S, 0, SS_INT32);
79 {
80 int_T y1Width = 1;
81 ssSetOutputPortWidth(S, 0, y1Width);
82 }
83 ssSetOutputPortComplexSignal(S, 0, COMPLEX_NO);
84 ssSetOutputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL);
85 ssSetOutputPortOutputExprInRTW(S, 0, 1);
86
87 /* Register reserved identifiers to avoid name conflict */
88 if (ssRTWGenIsCodeGen(S) || ssGetSimMode(S)==SS_SIMMODE_EXTERNAL) {
89
90 /* Register reserved identifier for */
91 ssRegMdlInfo(S, "Em_Sin", MDL_INFO_ID_RESERVED, 0, 0, ssGetPath(S));
92 } /* if */
93
94 /* This S-function can be used in referenced model simulating in normal mode */
95 ssSetModelReferenceNormalModeSupport(S, MDL_START_AND_MDL_PROCESS_PARAMS_OK);
96
97 /* Set the number of sample time */
98 ssSetNumSampleTimes(S, 1);
99
100 /* Set the compliance with the SimState feature */
101 ssSetSimStateCompliance(S, USE_DEFAULT_SIM_STATE);
102
103 ssSetSupportedForRowMajorCodeGen(S, true);
104
105 ssSetArrayLayoutForCodeGen(S, SS_COLUMN_MAJOR);
106
107 /* Set the Simulink version this S-Function has been generated in */
108 ssSetSimulinkVersionGeneratedIn(S, "9.1");
109
110 /**
111 * All options have the form SS_OPTION_<name> and are documented in
112 * matlabroot/simulink/include/simstruc.h. The options should be
113 * bitwise or'd together as in
114 * ssSetOptions(S, (SS_OPTION_name1 | SS_OPTION_name2))
115 */
116 ssSetOptions(S,
117 SS_OPTION_USE_TLC_WITH_ACCELERATOR |
118 SS_OPTION_CAN_BE_CALLED_CONDITIONALLY |
119 SS_OPTION_EXCEPTION_FREE_CODE |
120 SS_OPTION_WORKS_WITH_CODE_REUSE |
121 SS_OPTION_SFUNCTION_INLINED_FOR_RTW |
122 SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME
123 );
124 }
125
126 /* Function: mdlInitializeSampleTimes =====================================
127 * Abstract:
128 * This function is used to specify the sample time(s) for your
129 * S-function. You must register the same number of sample times as
130 * specified in ssSetNumSampleTimes.
131 */
132 static void mdlInitializeSampleTimes(SimStruct *S)
133 {
134 ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
135 ssSetOffsetTime(S, 0, FIXED_IN_MINOR_STEP_OFFSET);
136
137 #if defined(ssSetModelReferenceSampleTimeDefaultInheritance)
138 ssSetModelReferenceSampleTimeDefaultInheritance(S);
139 #endif
140 }
141
142 /* Function: mdlOutputs ===================================================
143 * Abstract:
144 * In this function, you compute the outputs of your S-function
145 * block. Generally outputs are placed in the output vector(s),
146 * ssGetOutputPortSignal.
147 */
148 static void mdlOutputs(SimStruct *S, int_T tid)
149 {
150
151 /* Get access to Parameter/Input/Output/DWork data */
152 int32_T* y1 = (int32_T*) ssGetOutputPortSignal(S, 0);
153 uint32_T* u1 = (uint32_T*) ssGetInputPortSignal(S, 0);
154
155
156 /* Call the legacy code function */
157 *y1 = Em_Sin(*u1);
158 }
159
160 /* Function: mdlTerminate =================================================
161 * Abstract:
162 * In this function, you should perform any actions that are necessary
163 * at the termination of a simulation.
164 */
165 static void mdlTerminate(SimStruct *S)
166 {
167 }
168
169 /* Required S-function trailer */
170 #ifdef MATLAB_MEX_FILE
171 # include "simulink.c"
172 #else
173 # include "cg_sfun.h"
174 #endif

lct_model:

转自:https://www.cnblogs.com/dingdangsunny/p/12248471.html

s函数的更多相关文章

  1. Python 小而美的函数

    python提供了一些有趣且实用的函数,如any all zip,这些函数能够大幅简化我们得代码,可以更优雅的处理可迭代的对象,同时使用的时候也得注意一些情况   any any(iterable) ...

  2. 探究javascript对象和数组的异同,及函数变量缓存技巧

    javascript中最经典也最受非议的一句话就是:javascript中一切皆是对象.这篇重点要提到的,就是任何jser都不陌生的Object和Array. 有段时间曾经很诧异,到底两种数据类型用来 ...

  3. JavaScript权威指南 - 函数

    函数本身就是一段JavaScript代码,定义一次但可能被调用任意次.如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法.用于初始化一个新创建的对象的函数被称作构造函数. 相对 ...

  4. C++对C的函数拓展

    一,内联函数 1.内联函数的概念 C++中的const常量可以用来代替宏常数的定义,例如:用const int a = 10来替换# define a 10.那么C++中是否有什么解决方案来替代宏代码 ...

  5. 菜鸟Python学习笔记第一天:关于一些函数库的使用

    2017年1月3日 星期二 大一学习一门新的计算机语言真的很难,有时候连函数拼写出错查错都能查半天,没办法,谁让我英语太渣. 关于计算机语言的学习我想还是从C语言学习开始为好,Python有很多语言的 ...

  6. javascript中的this与函数讲解

    前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码, ...

  7. 复杂的 Hash 函数组合有意义吗?

    很久以前看到一篇文章,讲某个大网站储存用户口令时,会经过十分复杂的处理.怎么个复杂记不得了,大概就是先 Hash,结果加上一些特殊字符再 Hash,结果再加上些字符.再倒序.再怎么怎么的.再 Hash ...

  8. JS核心系列:浅谈函数的作用域

    一.作用域(scope) 所谓作用域就是:变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的. function scope(){ var foo = "global&quo ...

  9. C++中的时间函数

    C++获取时间函数众多,何时该用什么函数,拿到的是什么时间?该怎么用?很多人都会混淆. 本文是本人经历了几款游戏客户端和服务器开发后,对游戏中时间获取的一点总结. 最早学习游戏客户端时,为了获取最精确 ...

  10. Python高手之路【四】python函数装饰器

    def outer(func): def inner(): print('hello') print('hello') print('hello') r = func() print('end') p ...

随机推荐

  1. Qt:QVariant

    0.说明 QVariant可以表现出Qt数据类型中最普遍的行为. 一个QVariant对象中一次只保留一个type()的单个值(有的type()可以是多值的,例如StringList),可以用conv ...

  2. Python post中session和auth 的三种方法

    1.方式一,获取session后.存储起来.下次调用时候,传递一个session植即可 #/bin/python # -*- coding: utf-8 -*- import requests fro ...

  3. js 数组/对象/日期的浅克隆

    //封装 function clone (obj) { // Handle the 3 simple types, and null or undefined if (null == obj || & ...

  4. python刷剑指offer(1-20)(一刷)

    2019/07/28开始刷每天6题,一共66题,刷三遍,9月1号完成. 1.二维数组中的查找: 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增 ...

  5. Flask 自建扩展

    自建扩展介绍 Flask扩展分两类 纯功能, 如: Flask-Login 提供用户认证 对已有的库和工具包装(简化继承操作,并提供有用的功能,更方便) 如: Flask-SQLAlchemy 包装了 ...

  6. python+pytest接口自动化(6)-请求参数格式的确定

    我们在做接口测试之前,先需要根据接口文档或抓包接口数据,搞清楚被测接口的详细内容,其中就包含请求参数的编码格式,从而使用对应的参数格式发送请求.例如某个接口规定的请求主体的编码方式为 applicat ...

  7. Jmeter计数器实现自增功能

    如果需要引用的数据量较大,且要求不能重复或者需要自增,那么可以使用计数器来实现 如:新增功能,要求名称不能重复 1,新增计数器 计数器:允许用户创建一个在线程组之内都可以被引用的计数器. 计数器允许用 ...

  8. jmeter(二十八)利用beanshell进行多重断言

    在接口测试中,我们对返回结果的正确性判断一般是基于响应报文的返回内容进行断言.但有些时候,按照正常的业务逻辑来说,一个请求返回的内容是多种不同的. 比如:用户注册功能,注册成功是正常的返回messag ...

  9. ASP.NET Core 6.0对热重载的支持

    .NET 热重载技术支持将代码更改(包括对样式表的更改)实时应用到正在运行的程序中,不需要重启应用,也不会丢失应用状态. 一.整体介绍 目前 ASP.NET Core 6.0 项目都支持热重载.在以下 ...

  10. web服务器-Nginx URL重写

    web服务器-Nginx URL重写 一. URL重写介绍 和apache等web服务软件一样,rewrite的主要功能是实现URL地址的重定向.Nginx的rewrite功能需要PCRE软件的支持, ...