整理自《Simulink仿真及代码生成技术入门到精通》
当Simulink默认提供的模块不能够满足用户的需求时,用户可以通过S函数打造自己的模块,实现自定义的算法或期望的动作。
S函数的类型,按照所支持的功能分类,可以分为
除了仿真时对端口数据类型的要求之外,这两类S函数还有一个区别
编写支持目标硬件外设寄存器配置的驱动模块时,C Mex S函数是一个不错的选择
S函数由几个子方法构成
mdlInitializeSizes()
mdlInitializeSampleTimes()
mdlOutputs()
mdlTerminate()
Level1 M S函数支持简单的MATLAB接口及少数S函数的API,是结构简单、功能最少的S函数
<后期补充吧>
<后期补充吧>
使用C语言编写S函数成为C Mex S函数,C语言编写的S函数可执行文件为mex文件,是MATLAB环境下的动态链接可执行文件。
C Mex S函数中模块属性的设置和获取,以及数据结构的访问和操作都需要C Mex宏函数来实现。
C Mex S函数的编写:
定义宏
#define S_FUNCTION_NAME demo
#define S_FUNCTION_LEVEL 2
包含头文件如果需要使用Simluink内建的固定点数据类型,应该包括fixedpoint.h
#include "simstruc.h"
C Mex S函数的各个子方法
其中,mdlRTW方法比较特别,负责传递模块参数到rtw文件中,以便TLC文件在代码生成阶段使用RTW中的记录。
注意:这个的各个子方法都是static子函数,其含义不是指存储方式,而是指函数的作用于仅局限于本文件,而不会被其他S函数所调用
static void mdlInitializeSizes(SimStruct *S){}
static void mdlInitializeSampleTimes(SimStruct *S){}
static void mdlOutputs(SimStruct *S,int_T tid){}
static void mdlTerminate(SimStruct *S){}
static void mdlRTW(SimStruct *S){}
结尾部分
#ifdef MATLAB_MEX_FILE
#include"Simulink.c"//用于仿真
#else
#include"cg_sfun.h"//用于RTW代码生成(非内嵌的S函数)
#endif
定义S函数块的基本特性,包括采样时间、连续或离散状态的初始条件以及sizes数组
SimStruct宏函数名 | 作用 |
---|---|
ssSetNumSFcnParams | 设定模块参数个数,第2个参数表示参数个数 |
ssGetNumSFcnParams | 获取模块参数的个数 |
ssGetSFcnParamsCount | 获取S函数实际拥有的参数个数 |
ssSetNumInputPorts | 设置输入端口的个数 |
ssSetInputPortWidth | 设置某个输入端口的位数,端口号从0开始,第3个参数是维度 |
ssSetInputPortDirectFeedThrough | 设置某个输入端口是狗直接馈入,通过第2个参数制定端口号,第3个参数1表示存在直接馈入,0则相反 |
ssSetNumOutputPorts | 设置输出端口的个数 |
ssSetOutputPortWidth | 设置某个输出端口的位数,端口号从0开始,第3个参数是维度 |
ssSetNumContStates | 设置连续状态变量个数,第2个参数表示连续状态变量个数 |
ssSetNumDiscStates | 设置离散状态变量个数,第2个参数表示离散状态变量个数 |
ssSetNumSampleTimes | 设置采样时间的个数,第2个参数表示采样时间的个数 |
ssSetOptions | 设置S函数的选项 |
ssSetInputPortDataType | 设置输入端口的类型 |
… | … |
这里说一下ssSetInputPortDataType,默认情况下端口的数据类型为double,对应到C Mex S函数的SimStruct数据结构为SS_DOUBLE
数据类型ID号 | SimStruct数据类型宏 | Simulink内建类型 |
---|---|---|
0 | SS_DOUBLE | double |
1 | SS_SINGLE | single |
2 | SS_INT8 | int8 |
3 | SS_UINT8 | uint8 |
4 | SS_INT16 | int16 |
5 | SS_UINT16 | uint16 |
6 | SS_INT32 | int32 |
7 | SS_UINT32 | uint32 |
8 | SS_BOOLEAN | boolean |
使用方法:
ssSetInputPortDataType(S,0, SS_UINT32)
自定义数据类型
一般情况下,自定义数据类型可以通过M语言在workspace中创建Simulink.NumericType和Simulink.AliasType。
% 创建Simulink Numeric Type
% Simulink.NumericType默认是double类型
MyDataType = Simulink.NumericType;
% 设置这个变量是一个类型定义的别名
MyDataType.IsAlias = 1;
之后在C Mex S函数的mdlInitializeSizes子方法中注册,并设置到输入输出端口或工作向量数据类型中去:
用一个滤波器实例,其数学模型表述如下:
Y ( t ) = ( U ( t ) − Y ( t − 1 ) ) × L c + Y ( t − 1 ) Y(t) = (U(t) - Y(t-1)) \times L_c + Y(t-1) Y(t)=(U(t)−Y(t−1))×Lc+Y(t−1)
#define S_FUNCTION_NAME sfun_c_filter
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#define COEF_IDX 0
#define COEF(S) mxGetScalar(ssGetSFcnParam(S,COEF_IDX))
/*Function:mdlInitializeSizes*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S,1);
if (ssGetNumSFcnParams(S)!= ssGetSFcnParamsCount(S)){
return;
}
if (!ssSetNumInputPorts(S,1)) return;
ssSetInputPortWidth(S,0,DYNAMICALLY_SIZED);
ssSetInputPortDirectFeedThrough(S,0,1);
if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S,0,DYNAMICALLY_SIZED);
ssSetNumDWork(S,1);
ssSetDWorkWidth(S,0,DYNAMICALLY_SIZED);
ssSetNumSampleTimes(S,1);
ssSetSimStateCompliance(S,USE_DEFAULT_SIM_STATE);
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 mdlInitializeConditions(SimStruct *S){
real_T *x = (real_T *) ssGetDWork(S,0);
x[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);
real_T *x = (real_T *) ssGetDWork(S,0);
real_T Lc = COEF(S);
for (i=0;i< width;i++){
y[i] = (*uPtrs[i] - x[i]) * Lc +x[i];
}
for(i=0;i
mex sfun_c_filter.c
然后建立S-Function模块,并封装Mask,将封装的Paramters与S-Function中的参数设置成一致的
建立Simulink模型如下:
如果这时选择编译的话,会报错,如下:
这里,Nonline S Function就是指不具有TLC文件,不支持代码生成的S函数。因此会报错。
这里提供两种内联方法:
为C MEX S函数配置TLC
在配置TLC文件之前,需要先在sfun_c_filter.c
中加入mdlRTW()
函数。
void mdlRTW(SimStruct *S)
{
/*Get Parameters*/
real_T c_coef = COEF(S);
/*Write parameter into rtw file*/
if (!ssWriteRTWParamSettings(S,1,
SSWRITE_VALUE_DTYPE_NUM, "r_coef", &c_coef, DTINFO(SS_DOUBLE, COMPLEX_NO)))
return;
}
模块TLC编写
%implements sfun_c_filter "C"
%% Function :blockTypeSetup
%% Purpose: add some macro defines.
%function BlockTypeSetup(block,system) void
%endfunction
%% Function :Start
%% Purpose: these code will appear at model.c initialization function
%function Start(block,system) Output
%endfunction
%% Function :Outputs
%% Purpose: these code will appear at model.c step function
%function Outputs(block,system) Output
%assign t_coef = SFcnParamSettings.r_coef
%assign rollVars = ["U","Y","DWork"]
%roll sigIdx = RollRegions, lcv = RollThreshold, block, "Roller" ,rollVars
%assign u = LibBlockInputSignal(0,"",lcv, sigIdx)
%assign y = LibBlockOutputSignal(0,"",lcv, sigIdx)
%assign x = LibBlockDWork(dwork,"",lcv, sigIdx)
/*Calculate the filter result*/
% = (% - %)*% + %;
% = ;
%endroll
%endfunction
【未完】
包含头文件的部分,C Mex S函数使用用户自定义数据类型有两种方法:
head.h
文件中。typedef struct{
signed long Alpha;
signed long Beta;
}AlphaBeta;
然后将S函数的头文件部分修改为:
#include "simstruc.h"
#include "head.h"