Simulink カスタム・ブロック

□ 参考ページ

自作ブロックライブラリをSimulinkライブラリに追加する
http://www.mathworks.co.jp/support/solutions/ja/data/1-9IUXR7/?solution=1-9IUXR7
ライブラリの EnableLBRepository プロパティを 'on' に設定
https://jp.mathworks.com/help/simulink/ug/adding-libraries-to-the-library-browser_ja_JP.html

簡単なマスクの作成
https://jp.mathworks.com/help/simulink/ug/how-to-mask-a-block.html

S-Function でメモリを割り当てて, アクセスする方法
https://www.mathworks.com/matlabcentral/answers/488429-how-to-allocate-memory-and-access-it-in-s-function 

 

 

□ カスタムブロックの作成

フォルダ
C:\Program Files\MATLAB\R2020b\toolbox\simulink\simdemos\simfeatures\src
にアクセスするために, MATLAB を管理者権限で起動する (Windows 10)

Simulink で空のモデルに User-Defined Functions\S-function ブロックを配置する

f:id:sato-7411:20211111150634p:plain

ダブルクリックでブロックパラメーターのウィンドウを表示

f:id:sato-7411:20211111150833p:plain

S-Function 名を, system から timestwo に変更。編集
MATLAB で timestwo.c が開くので, 名前を付けて保存で GBLInport.c に変更
S-Function 名も, GBLInport に変更。適用, OK
Simulink を一旦閉じる。保存しなくて良い
(GBLOutport は GBLInport から作成)

 

GBLInport.c をエディターで編集

#define S_FUNCTION_NAME GBLInport
#define S_FUNCTION_LEVEL 2

 

#define MDL_INITIAL_SIZES
static void mdlInitializeSizes(SimStruct *S)
入力ポート数, 出力ポート数, 状態数, パラメータ数, 等の特性を指定する

ssSetNumSFcnParams でパラメータ数を指定する(4)
ssSetNumContStates と ssSetNumDiscStates で状態数を指定する(0)

入力ポート
  ssSetNumInputPorts で入力ポート数を指定する(0)

出力ポート
  ssSetNumOutputPorts で出力ポート数を指定する(1)

ssSetNumRWork と ssSetNumIWork でワークベクターのサイズを指定する(0)
ssSetSFcnParamTunable シミュレーション中にパラメータを変更不可にする場合は0にする(1)

ssSetOutputPortWidth でサイズは固定(1)
ssSetOutputPortDataType で型は動的(DYNAMICALLY_TYPED)

(サンプルと同じ設定)
ssSetNumSampleTimes(S, 1)
ssSetNumPWork(S, 0)
ssSetNumModes(S, 0)
ssSetNumNonsampledZCs(S, 0)

ssSetOptions を指定(0)

 

#define MDL_SET_OUTPUT_PORT_DATA_TYPE
static void mdlSetOutputPortDataType(SimStruct *S, int_T port, DTypeId id)
ssSetOutputPortDataType で型を id にする

 

#define MDL_INITIALIZE_SAMPLE_TIMES
static void mdlInitializeSampleTimes(SimStruct *S)
ssSetSampleTime
ssSetOffsetTime
index = 0
sample time and offset values = [CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET]

 

#define MDL_INITIALIZE_CONDITIONS
static void mdlInitializeConditions(SimStruct *S)
処理無し

 

#define MDL_START
static void mdlStart(SimStruct *S)
Simulink はシミュレーションの開始で, オプションとして, 本関数を実行する
ユーザーデータのセットアップや状態の初期化を行う

char buffer[256];
mxGetString( ssGetSFcnParam(S,0), buffer, 128 ); //mxGetString は mxChar* から char* への変換。ssGetSFcnParam(S,0) は1番目のパラメータ(P1)
ssSetUserData(S, getInputArrayPtr(buffer));

 

#define MDL_OUTPUTS
static void mdlOutputs(SimStruct *S, int_T tid)
メイン処理
#define portIndex (int32_T) mxGetPr(ssGetSFcnParam(S,1))[0]
double *inputArray;
inputArray = ssGetUserData(S);
if (ssGetOutputPortDataType(S, 0) == SS_DOUBLE)
*(real_T *) ssGetOutputPortRealSignal(S,0) = inputArray[portIndex];

 

#define MDL_UPDATE
static void mdlUpdate(SimStruct *S, int_T tid)
処理無し

 

#define MDL_DERIVATIVES
static void mdlDerivatives(SimStruct *S)
処理無し

 

static void mdlTerminate(SimStruct *S)
処理無し

 

(outport の場合)

#define MDL_INITIAL_SIZES
ssSetNumSFcnParams 2
ssSetNumInputPorts 1
ssSetNumOutputPorts 0
ssSetInputPortWidth 0,1
ssSetInputPortDataType 0,DYNAMICALLY_TYPED
ssSetInputPortRequiredContiguous 0,true
ssSetInputPortDirectFeedThrough 0,1
ssSetSFcnParamTunable 0,1
ssSetSFcnParamTunable 1,1

# define MDL_SET_INPUT_PORT_DATA_TYPE
ssSetInputPortDataType 0,id

#define MDL_INITIALIZE_SAMPLE_TIMES
index = 0
sample time and offset values = [CONTINUOUS_SAMPLE_TIME, 0.0]

#define MDL_START
ssSetUserData(S, getOutputArrayPtr(buffer));

#define MDL_OUTPUTS
outputArray = ssGetUserData(S);
if (ssGetInputPortDataType(S, 0) == SS_DOUBLE)
outputArray[portIndex] = *(real_T *) ssGetInputPortRealSignal(S,0);

 

 

MATLAB のコマンドウィンドウで, フォルダ
C:\Program Files\MATLAB\R2020b\toolbox\simulink\simdemos\simfeatures
に移動して, ビルド
mex .\src\GBLInport.c
GBLInport.mexw64 が出来る

 

 

次に, S-Function を配置して, S-Function 名に GBLInport を入力し, 適用ボタンを押下すると, S-Function パラメーターが空欄のままだと, エラーメッセージが出る

f:id:sato-7411:20211111150957p:plain

f:id:sato-7411:20211111151022p:plain

S-Function パラメーターに項目を4つ入力する。適用, OK

f:id:sato-7411:20211111151104p:plain

 

 

マウス右クリック, マスク, マスクの作成

f:id:sato-7411:20211111151519p:plain

 

アイコンと端子, はそのまま

f:id:sato-7411:20211111151649p:plain

 

パラメーターとダイアログ, で, パラメーター配下にエディットを4個追加
それぞれのプロンプトと名前を編集。適用

f:id:sato-7411:20211111151819p:plain

f:id:sato-7411:20211111151904p:plain

 
初期化とドキュメンテーションはそのまま。適用, OK

  

S-Function ブロックをダブルクリックすると, パラメーターの入力画面が開くようになる
ModelName に仮に 'ParentModel' を入力

f:id:sato-7411:20211111151952p:plain

 

Simulink モデルを, 例えば C:\Program Files\MATLAB\R2020b\toolbox\chbstyle フォルダに, GBLPortLib_Sample.slx と名前を付けて保存

f:id:sato-7411:20211111154035p:plain



 

新規で空のライブラリを作成。ブロックをコピー, 貼付け。コマンドウィンドウで
set_param(gcs,'EnableLBRepository','on');
を実行する。ライブラリを GBLPortLib.slx と名前を付けて保存

f:id:sato-7411:20211111152554p:plain

 

ファイルの配置

ファイルを同じフォルダにコピーする。ファイルが二重になってしまうが, 手順を分かりやすくするため
slblocks.m は他のものからコピーして, 内容を現在のブロックに合わせて変更する
GBLOutport.c
GBLInport.mexw64
GBLOutport.c
GBLOutport.mexw64
slblocks.m

f:id:sato-7411:20211111152702p:plain

 

パスの追加

MATLAB のホーム, 環境, パスの設定, フォルダを追加, で上記フォルダを追加する

Simulink を起動し, ライブラリブラウザーを開く。最初は My Library が表示されていないので, マウス右クリック, ライブラリ ブラウザーを更新, を選択すれば表示される

f:id:sato-7411:20211111152802p:plain

f:id:sato-7411:20211111152841p:plain

 

 

□ モデル(model_main)と S-Function の両方から参照できる共通モジュールを作る

modcomm.c を作成
model_main.c で modcomm.h をインクルード
S-Function のCコードで modcomm.h をインクルード

NewSimModel_main.c で
#define noInputs 2
#define noOutputs 1
static double localInputArray_NewSimModel[noInputs];
static double localOutputArray_NewSimModel[noOutputs];
を作る (値の格納先)

そのポインタを NewSimModel_main.c の Start で registerModule(名前, 入力数, 入力ptr, 出力数, 出力ptr) を使って共通モジュールに登録する。後で名前("NewSimModel")を使って検索できる

S-Function では mdlStart で ssSetUserData を行う。ここで, getInputArrayPtr を使って名前で検索

mdlOutputs で ssGetUserData で参照 (結果として NewSimModel_main.c の localInputArray_NewSimModel を参照する)
ポートインデックスはパラメータ mxGetPr(ssGetSFcnParam(S,1))[0] で取得
mxGetPr(ssGetSFcnParam(S,1))[0] は2番目のパラメータ(P2)のインデックス0
newsimmodel_data.c にそれがある

S-Function のパラメータ
-- GBLInport.c --
moduleName 自分があるモデル名"NewSimModel"
portIndex モデル内でのポートのインデックス
b_useConstant
constValue

-- GBLOutport.c --
moduleName 自分があるモデル名"NewSimModel"
portIndex モデル内でのポートのインデックス

rt_main で参照する場合は
#include "modcomm.h"
#define Inports_NewSimModel getInputArrayPtr("NewSimModel")
#define Outports_NewSimModel getOutputArrayPtr("NewSimModel")
Inports_NewSimModel[0] = val1;
Inports_NewSimModel[1] = val2;
/* OneStep_NewSimModel(); */
val3 = Outports_NewSimModel[0];

 

 

□ ModelName が設定されていること, InportID と OutportID に抜けと重複が無いことを確認

allBlocks = find_system('NewSimModel', 'LookUnderMasks', 'all', 'BlockType', 'S-Function');
inBlocks = find_system(allBlocks, 'LookUnderMasks', 'all', 'FunctionName', 'GBLInport');
outBlocks = find_system(allBlocks, 'LookUnderMasks', 'all', 'FunctionName', 'GBLOutport');
for noInputs = 1:length(inBlocks)
fprintf('%s, %s\n', get_param(inBlocks{noInputs},'ModelName'), get_param(inBlocks{noInputs},'InportID'));
end
for noOutputs = 1:length(outBlocks)
fprintf('%s, %s\n', get_param(outBlocks{noOutputs},'ModelName'), get_param(outBlocks{noOutputs},'OutportID'));
end

あるシステムでは, m ファイルで ModelName の自動書き込み, InportID, OutportID の自動割り振りを行っている