天天看點

[Simulink] 從S函數到子產品代碼生成S函數

文章目錄

  • S函數
    • S函數類型
    • S函數的組成及執行順序
    • 編寫S函數
      • Level1 M S函數
      • Level2 M S函數
      • C Mex S函數
        • mdlInitializeSizes
        • C Mex S函數的執行個體
整理自《Simulink仿真及代碼生成技術入門到精通》

S函數

當Simulink預設提供的子產品不能夠滿足使用者的需求時,使用者可以通過S函數打造自己的子產品,實作自定義的算法或期望的動作。

S函數類型

S函數的類型,按照所支援的功能分類,可以分為

  • Level 1 —— 編寫簡單的數學算法用來仿真
  • Level 2 —— 所編寫的算法需要傳遞多個輸入輸出端口且每個端口資料都是多元矩陣時,需要使用該類型

除了仿真時對端口資料類型的要求之外,這兩類S函數還有一個差別

  • Level 1的S函數不支援代碼生成
  • Level 2的S函數支援代碼生成,但如果需要生成代碼,需要給S函數編寫同名的tlc檔案
編寫支援目标硬體外設寄存器配置的驅動子產品時,C Mex S函數是一個不錯的選擇

S函數的組成及執行順序

S函數由幾個子方法構成

  • 初始化

    mdlInitializeSizes()

  • 采樣時間設定

    mdlInitializeSampleTimes()

  • 系統輸出

    mdlOutputs()

  • 仿真結束前終止方法

    mdlTerminate()

  • mdlRTW()

    其中,加粗部分為S函數能正常仿真的必要方法。

編寫S函數

Level1 M S函數

Level1 M S函數支援簡單的MATLAB接口及少數S函數的API,是結構簡單、功能最少的S函數

<後期補充吧>

Level2 M S函數

<後期補充吧>

C Mex 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
           

mdlInitializeSizes

定義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内建類型
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子方法中注冊,并設定到輸入輸出端口或工作向量資料類型中去:

C Mex S函數的執行個體

用一個濾波器執行個體,其數學模型表述如下:

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)

  • U ( t ) U(t) U(t)表示目前采樣時刻的輸入
  • Y ( t ) Y(t) Y(t)表示目前采樣時刻的輸出
  • Y ( t − 1 ) Y(t-1) 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<width;i++){
		x[i] = y[i];
	}
}

static void mdlTerminate(SimStruct *S){}

#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif

           
mex sfun_c_filter.c
           

然後建立S-Function子產品,并封裝Mask,将封裝的Paramters與S-Function中的參數設定成一緻的

[Simulink] 從S函數到子產品代碼生成S函數
[Simulink] 從S函數到子產品代碼生成S函數

建立Simulink模型如下:

[Simulink] 從S函數到子產品代碼生成S函數

如果這時選擇編譯的話,會報錯,如下:

[Simulink] 從S函數到子產品代碼生成S函數

這裡,Nonline S Function就是指不具有TLC檔案,不支援代碼生成的S函數。是以會報錯。

這裡提供兩種内聯方法:

  • Full inlined 完全内聯 ——在TLC的Output子方法中實作具體的算法,明确給出輸入/輸出關系
  • wrapper inlined 封裝内聯 —— 在TLC的Output子方法中不是實作具體算法代碼,而是規定輸入/輸出端口變量如何調用已經存在的C代碼。

為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*/
		% <y> = (%<u> - %<x>)*%<t_coef> + %<x>;
		%<x> = <y>;
	%endroll
%endfunction

           

【未完】

包含頭檔案的部分,C Mex S函數使用使用者自定義資料類型有兩種方法:

  • C Mex S函數中使用外部結構體資料類型,儲存在

    head.h

    檔案中。
typedef struct{
	signed long Alpha;
	signed long Beta;
}AlphaBeta;
           

然後将S函數的頭檔案部分修改為:

#include "simstruc.h"
#include "head.h"
           

繼續閱讀