| Real-Time Workshop User's Guide | ![]() |
概要
デバイスドライバS-Functionは、2,3の操作しか行わないので、比較的実現が容易です。それらの操作は以下の通りです。
SimStructの初期化yに割り当てます。ドライバは、S-Function APIが要求する固有の関数を実現することによって、これらの操作を行います。
これらの関数は、ソースファイルに対してプライベートなので、複数の同じS-Functionをモデルに組み込むことができます。そのような非インラインS-Functionは、SimStructを例示します。
Simulinkとリアルタイムに対する条件付きのコンパイル
非インラインS-Functionは、Simulinkとリアルタイム環境の両方で機能しなければなりません。Real-Time Workshopは、プリプロセッサシンボルMATLAB_MEX_FILE, RTとNRTを定義して、シミュレーションコードとリアルタイムコードを区別します。これらのシンボルは以下のように使います。
MATLAB_MEX_FILEこのシンボルのもとでは、条件付きで、シミュレーションのみの利用を目的とするコードをインクルードします。mexコマンドを使って、S-FunctionとMEX-ファイルとしてビルドする際、MATLAB_MEX_FILEは自動的に定義されます。
RTこのシンボルのもとでは、条件付きで、リアルタイムプログラムでのみの利用を目的とするコードをインクルードします。Real-Time Workshopのビルドコマンドを使ってコードを生成する際、RTは自動的に定義されます。
NRTこのシンボルのもとでは、条件付きで、非リアルタイムのスタンドアロンシミュレーションにおいて、可変ステップソルバを用いたときのみの利用を目的とするコードをインクルードします。
Real-Time Workshopは、これらの条件文を提供し、ドライバS-Functionが適切なときにのみハードウェアにアクセスすることを保証することを援助します。ターゲットI/Oハードウェアはシミュレーション中に現れないので、ターゲット環境内でのアドレスへの書き込みは、不正なメモリ参照、システムメモリの上書き、その他の深刻なエラーとなる場合があります。同様に、存在しないハードウェアレジスタからの読み込み操作は、モデル実行エラーを生じる場合があります。
つぎのコードでは、ハードウェアの初期化呼び出しが生成されたリアルルタイムコードでコンパイルされます。シミュレーション中に、MATLABコマンドウィンドウにメッセージが表示されます。
#if defined(RT)/* generated code calls function to initialize an A/D device */INIT_AD();#elif defined(MATLAB_MEX_FILE)/* during simulation, just print a message */if (ssGetSimMode(S) == SS_SIMMODE_NORMAL) {mexPrintf("\n adc.c: Simulating initialization\n");}#endif
MATLAB_MEX_FILEとRTの条件文は、必要なインクルードファイルの制御も行います。下記の「必要な定義とインクルードファイル」を参照してください。
その他の手段でリアルタイムおよびシミュレーションコードの制御を行うほうがいい場合があります。例として、matlabroot/rtw/c/dos/devices/das16ad.cの変数ACCESS_HWの使用法を参照してください。
必要な定義とインクルードファイル
ドライバS-Functionは、つぎの3つのステートメントを、つぎの順で始めなければなりません。
#define S_FUNCTION_NAME name
これは、S-Functionコードのエントリポイント名を定義します。nameは、拡張子.cを除いたS-Functionソースファイル名でなければなりません。たとえば、S-Functionソースファイルがdas16ad.cの場合、
#define S_FUNCTION_NAMEdas16ad
#define S_FUNCTION_LEVEL 2
このステートメントは、レベル2のS-Functionとしてファイルを定義します。これにより、S-Functionのすべての機能を利用することができます。レベル1のS-Functionは、現在は下位互換性を維持するためにのみ利用されます。
#include "simstruc.h"
ファイルsimstruc.hは、SimStruct(Simulinkデータ構造体) と関連するアクセスマクロを定義します。MATLAB MEX APIかmx*関数に対するアクセス方法も定義します。
S-Functionを、MEXファイルあるいはリアルタイムコードのどちらとしてビルドするかにより、S-Functionの終了時につぎのファイルのうちの1つをインクルードしなければなりません。
simulink.cは、Simulinkとのインタフェースを行う必要な関数を提供します。非インラインS-Functionは、sfuntmpl.cの下記のコードのように、条件付きでこれらのファイルを両方インクルードします。
#ifdef MATLAB_MEX_FILE /* File being compiled as a MEX-file? */ #include "simulink.c" /* MEX-file interface mechanism */ #else #include "cg_sfun.h" /* Code generation registration function */ #endif
必要な関数
S-Function APIでは、ドライバ内で複数の関数を実現する必要があります。
mdlInitializeSizesは、ブロックの出力端子数のような、SimStructの様々なパラメータのサイズを指定します。mdlInitializeSampleTimesは、ブロックのサンプル時間を指定します。デバイスドライバブロックがマスクされている場合、初期化関数は、ユーザがブロックのダイアログボックスに入力したサンプル時間やその他のパラメータを取得できます。
mdlOutputs: 入力デバイスに対して、ハードウェアから値を読み込み、これらの値を出力ベクトルyに設定します。出力デバイスに対して、上流ブロックから入力uを読み込み、値をハードウェアに出力します。mdlTerminateは、ハードウェアデバイスが存在すれば、希望する状態にリセットします。この関数は、スタブとして実現される場合があります。上記に加えて、mdlStart関数を実現したい場合があります。mdlStartは、モデル実行の開始時に1回呼び出され、I/Oハードウェアを希望する初期状態に設定するような操作に対して役立ちます。
つぎの節では、これらの関数の実現のためのガイドラインを説明します。
mdlInitializeSizes
この関数では、SimStructの様々なパラメータのサイズを指定します。この情報は、S-Functionに渡されるパラメータにより異なります。「ドライバのパラメータ化」では、S-Functionダイアログボックスで指定されたパラメータ値にアクセスする方法を説明しています。
サイズの初期化- 入力デバイス. mdlInitializeSizes関数は、SimStructのサイズ情報を設定します。mdlInitializeSizesの下記の実現は、典型的なADCドライバブロックを初期化します。
static void mdlInitializeSizes(SimStruct *S)
{
uint_T num_channels;
ssSetNumSFcnParams(S, 3); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)){
/*Return if number of expected != number of actual params */
return;
}
num_channels = mxGetPr(NUM_CHANNELS_PARAM)[0];
ssSetNumInputPorts(S, 0);
ssSetNumOutputPorts(S, num_channels);
ssSetNumSampleTimes(S,1);
}
このルーチンは、最初に入力パラメータ数がブロックのダイアログボックス内のパラメータ数と等しいことを検証しています。つぎに、ダイアログからNumber of Channelsパラメータを取得します。
ADCはソースブロックで出力のみをもつので、ssSetNumInputPortsは、入力端子数を0に設定します。
ssSetNumOutputPortsは、出力端子数を、ダイアログボックスから取得したI/Oチャンネル数と等しく設定します。
ssSetNumSampleTimesは、サンプル時間数を1に設定します。これは、すべてのADCチャンネルが同じレートで実行される場合です。実際のサンプル間隔は、mdlInitializeSampleTimesで設定されます。
デフォルトでは、ADCブロックな直接フィードスルーをもちません。ADC 出力は、他のブロックから得られたデータではなく、ハードウェアから読み込まれた値を基に計算されます。
サイズの初期化 - 出力デバイス. DACのような出力デバイスに対するサイズ情報の初期化は、ADCのサイズの初期化とはいくつかの重要な点で異なります。
つぎの例は、DACドライバブロックに対するmdlInitializeSizesの実現です。
static void mdlInitializeSizes(SimStruct *S)
{
uint_T num_channels;
ssSetNumSFcnParams(S, 3); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)){
/* Return if number of expected != number of actual params */
return;
}
num_channels = mxGetPr(NUM_CHANNELS_PARAM)[0];
ssSetNumInputPorts(S, num_channels);
/* Number of inputs is now the number of channels. */
ssSetNumOutputPorts(S, 0);
/* Set direct feedthrough for all ports */
{
uint_T i;
for(i=0, i < num_channels, i++) {
ssSetInputPortDirectFeedThrough(S,i,1);
}
}
ssSetNumSampleTimes(S, 1);
}
mdlInitializeSampleTimes
デバイスドライバブロックは、離散ブロックで、サンプル時間を設定する必要があります。サンプル時間の設定の手順は、入力および出力デバイスドライバに対して同じです。すべてのデバイスのチャンネルが同じレートで実行されると仮定すると、S-Functionはサンプル時間を1つだけもちます。
mdlInitializeSampleTimesの以下の実現は、サンプル時間をブロックのダイアログボックスから読み込みます。この場合、サンプル時間はダイアログボックス5番目のパラメータです。サンプル時間のオフセットは0に設定されます。
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, mxGetPr(ssGetSFcnParams(S,4))[0]);
ssSetOffsetTime(S, 0, 0.0);
}
mdlStart
mdlStartは、オプション関数です。モデル実行の開始時に1回呼び出され、ハードウェアを初期化するために利用します。ハードウェアにアクセスするため、この例のように、リアルタイムコードまたはシミュレーション用に条件付きでコンパイルします。
static void mdlStart(SimStruct *S)
{
#if defined(RT)
/* Generated code calls function to initialize an A/D device */
INIT_AD(); /* This call accesses hardware */
#elif defined(MATLAB_MEX_FILE)
/* During simulation, just print a message */
if (ssGetSimMode(S) == SS_SIMMODE_NORMAL) {
mexPrintf("\n adc.c: Simulating initialization\n");
}
#endif
}
mdlOutputs
デバイスドライバブロックの基本的な目的は、プログラムとI/Oハードウェアとの通信を行うことです。一般的に、これはコンパイラのCライブラリの一部である低レベルのハードウェア呼び出し、あるいはI/Oハードウェアに付属するCの呼び出し可能な関数を使って行われます。
すべてのS-Functionは、mdlOutputs関数を実現して、ブロック出力を計算します。デバイスドライバブロックに対して、mdlOutputsは、ハードウェアから読み込んだり、ハードウェアへ書き込むコードを含みます。
mdlOutputs - 入力デバイス。 入力デバイス用のドライバ(ADC)のようなでは、mdlOutputsは、以下を行う必要があります。
つぎのコードは、ADCドライバmatlabroot/rtw/c/dos/devices/das16ad.cのmdlOutputs関数です。関数は、matlabroot/rtw/c/dos/devices/das16ad.hで定義されたマクロを使って、低レベルのハードウェアのアクセスを行います。Boolean変数ACCESS_HW(条件付きのコンパイル以外)は、シミュレーションとリアルタイムコードの実行を制御します。リアルタイムコードは、ハードウェアから値を読み込み、それらを出力ベクトルに格納します。シミュレーションコードは、単純にすべてのチャンネルにおいて0を出力します。
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortRealSignal(S,0);
uint_T i;
if (ACCESS_HW) {
/* Real-time code reads hardware*/
ADCInfo *adcInfo = ssGetUserData(S);
uint_T baseAddr = adcInfo->baseAddr;
real_T offset = adcInfo->offset;
real_T resolution = adcInfo->resolution;
/* For each ADC channel initiate conversion,*/
/* then read channel value, scale and offset it and store */
/* it to output y */
for (i = 0; i < NUM_CHANNELS; i++) {
uint_T adcValue;
adcStartConversion(baseAddr);
for ( ; ; ){
if (!adcIsBusy(baseAddr)) break;
}
adcValue = adcGetValue(baseAddr);
y[i] = offset + resolution*adcValue;
}
}
else {
/* simulation code just zeroes the output for all channels*/
for (i = 0; i < NUM_CHANNELS; i++){
y[i] = 0.0;
}
}
}
mdlOutputs - 出力デバイス. (DACのような)出力デバイス用のドライバでは、mdlOutputsは以下を行う必要があります。
uを上流ブロックから読み込みます。つぎのコードは、DACドライバmatlabroot/rtw/c/dos/devices/das16da.cのmdlOutputs関数です。関数は、matlabroot/rtw/c/dos/devices/das16ad.hで定義されたマクロを使って、低レベルのハードウェアアクセスを行います。この関数は、すべてのチャンネルにわたって、ブロックの入力値の取得とスケーリングを繰り返します。その後範囲チェックと(必要ならば) 各値のトリミングを行います。最後に、値をハードウェアに書き出します。
static void mdlOutputs(SimStruct *S, int_T tid)
{
if (ACCESS_HW) {
int_T i;
DACInfo *dacInfo = ssGetUserData(S);
uint_T baseAddr = dacInfo->baseAddr;
real_T resolution = dacInfo->resolution;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
for (i = 0; i < NUM_CHANNELS; i++) {
uint_T codeValue;
/* Get and scale input for channel i. */
real_T value = (*uPtrs[i] - MIN_OUTPUT)*resolution;
/* Range check value */
value = (value < DAC_MIN_OUTPUT) ? DAC_MIN_OUTPUT : value;
value = (value > DAC_MAX_OUTPUT) ? DAC_MAX_OUTPUT : value;
codeValue = (uint_T) value;
/* Output to hardware */
switch (i) {
case 0:
dac0SetValue(baseAddr, codeValue);
break;
case 1:
dac1SetValue(baseAddr, codeValue);
break; }
}
}
}
mdlTerminate
この最後に必要な関数は、一般にDACドライバでのみ必要です。つぎのルーチンは、各DACチャンネルの出力を0に設定します。
static void mdlTerminate(SimStruct *S)
{
uint_T num_channels;
uint_T i;
num_channels = (uint_t)mxGetPr(ssGetSFcnParams(S,0)[0]);
for (i = 0; i < num_channels; i++){
ds1102_da(i + 1, 0.0); /* Hardware-specific DAC output */
}
}
ADCドライバは、通常mdlTerminateを空のスタブとして実現します。
| ドライバのパラメータ化 | インラインS-Functionデバイスドライバの作成 | ![]() |