C MEX S-Function Examples
C MEX S-Function Examples
®
Simulink
Provide feedback about this page
On this page…
Back to Top
Continuous States
The matlabroot/simulink/src/csfunc.c example shows how to model a continuous system with states using a C
MEX S-function. The following Simulink model uses this S-function.
matlabroot/toolbox/simulink/simdemos/simfeatures/sfcndemo_csfunc.mdl
In continuous state integration, the Simulink solvers integrate a set of continuous states using the following equations.
S-functions that contain continuous states implement a state-space equation. The mdlOutputs method contains the output
portion and mdlDerivatives method contains the derivative portion of the state-space equation. To visualize how the
integration works, see the flowchart in How the Simulink Engine Interacts with C S-Functions. The output equation
corresponds to the mdlOutputs in the major time step. Next, the example enters the integration section of the flowchart. Here
the Simulink engine performs a number of minor time steps during which it calls mdlOutputs and mdlDerivatives. Each
of these pairs of calls is referred to as an integration stage. The integration returns with the continuous states updated and the
simulation time moved forward. Time is moved forward as far as possible, providing that error tolerances in the state are met.
The maximum time step is subject to constraints of discrete events such as the actual simulation stop time and the
user-imposed limit.
The csfunc.c example specifies that the input port has direct feedthrough. This is because matrix D is initialized to a nonzero
matrix. If D is set equal to a zero matrix in the state-space representation, the input signal is not used in mdlOutputs. In this
case, the direct feedthrough can be set to 0, which indicates that csfunc.c does not require the input signal when executing
mdlOutputs.
matlabroot/simulink/src/csfunc.c
The S-function begins with #define statements for the S-function's name and level, and a #include statement for the
simstruc.h header. After these statements, the S-function can include or define any other necessary headers, data, etc. The
csfunc.c example defines the variable U as a pointer to the first input port's signal and initializes static variables for the
state-space matrices.
/* File : csfunc.c
* Abstract:
*
* Example C-file S-function for defining a continuous system.
*
* x' = Ax + Bu
* y = Cx + Du
1 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
*
* For more details about S-functions, see simulink/src/sfuntmpl_doc.c.
*
* Copyright 1990-2007 The MathWorks, Inc.
*/
#include "simstruc.h"
The required S-function method mdlInitializeSizes then sets up the following S-function characteristics.
/*====================*
* S-function methods *
*====================*/
ssSetNumContStates(S, 2);
ssSetNumDiscStates(S, 0);
2 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
The required S-function method mdlInitializeSampleTimes specifies the S-function's sample rates. The value
CONTINOUS_SAMPLE_TIME passed to the ssSetSampleTime macro specifies that the S-function's first sample rate is
continuous. ssSetOffsetTime then specifies an offset time of zero for this sample rate. The call to
ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the default rule to determine if
submodels containing this S-function can inherit their sample times from the parent model.
The optional S-function method mdlInitializeConditions initializes the continuous state vector. The #define
statement before this method is required for the Simulink engine to call this function. In the example below,
ssGetContStates obtains a pointer to the continuous state vector. The for loop then initializes each state to zero.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ========================================
* Abstract:
* Initialize both continuous states to zero.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = ssGetContStates(S);
int_T lp;
for (lp=0;lp<2;lp++) {
*x0++=0.0;
}
}
The required mdlOutputs function computes the output signal of this S-function. The beginning of the function obtains
pointers to the first output port, continuous states, and first input port. The S-function uses the data in these arrays to solve the
output equation y=Cx+Du.
/* y=Cx+Du */
y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1);
y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1);
}
The mdlDerivatives function calculates the continuous state derivatives. Because this function is an optional method, a
#define statement must precede the function. The beginning of the function obtains pointers to the S-function's continuous
states, state derivatives, and first input port. The S-function uses this data to solve the equation dx=Ax+Bu.
#define MDL_DERIVATIVES
/* Function: mdlDerivatives =================================================
* Abstract:
* xdot = Ax + Bu
*/
static void mdlDerivatives(SimStruct *S)
3 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
{
real_T *dx = ssGetdX(S);
real_T *x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/* xdot=Ax+Bu */
dx[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1);
dx[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1);
}
The required mdlTerminate function performs any actions, such as freeing memory, necessary at the end of the simulation.
In this example, the function is empty.
The required S-function trailer includes the files necessary for simulation or code generation, as follows.
Note The mdlOutputs and mdlTerminate functions use the UNUSED_ARG macro to indicate that an input
argument the callback requires is not used. This optional macro is defined in
matlabroot/simulink/include/simstruc_types.h. If used, you must call this macro once for each input
argument that a callback does not use.
Back to Top
Discrete States
The matlabroot/simulink/src/dsfunc.c example shows how to model a discrete system in a C MEX S-function. The
following Simulink model uses this S-function.
matlabroot/toolbox/simulink/simdemos/simfeatures/sfcndemo_dsfunc.mdl
The dsfunc.c example implements a discrete state-space equation. The mdlOutputs method contains the output portion
and the mdlUpdate method contains the update portion of the discrete state-space equation. To visualize how the simulation
works, see the flowchart in How the Simulink Engine Interacts with C S-Functions. The output equation above corresponds to
the mdlOutputs in the major time step. The preceding update equation corresponds to the mdlUpdate in the major time
step. If your model does not contain continuous elements, the Simulink engine skips the integration phase and time is moved
forward to the next discrete sample hit.
matlabroot/simulink/src/dsfunc.c
The S-function begins with #define statements for the S-function's name and level, along with a #include statement for the
simstruc.h header. After these statements, the S-function can include or define any other necessary headers, data, etc. The
dsfunc.c example defines U as a pointer to the first input port's signal and initializes static variables for the state-space
matrices.
/* File : dsfunc.c
* Abstract:
*
* Example C-file S-function for defining a discrete system.
*
* x(n+1) = Ax(n) + Bu(n)
* y(n) = Cx(n) + Du(n)
4 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
*
* For more details about S-functions, see simulink/src/sfuntmpl_doc.c.
*
* Copyright 1990-2007 The MathWorks, Inc.
*/
#include "simstruc.h"
The required S-function method mdlInitializeSizes then sets up the following S-function characteristics.
/*====================*
* S-function methods *
*====================*/
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 2);
5 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
The required S-function method mdlInitializeSampleTimes specifies the S-function's sample rates. A call to
ssSetSampleTime sets this S-function's first sample period to 1.0. ssSetOffsetTime then specifies an offset time of zero
for the first sample rate. The call to ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the
default rule to determine if submodels containing this S-function can inherit their sample times from the parent model.
The optional S-function method mdlInitializeConditions initializes the discrete state vector. The #define statement
before this method is required for the Simulink engine to call this function. In the example below, ssGetRealDiscStates
obtains a pointer to the discrete state vector. The for loop then initializes each discrete state to one.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ========================================
* Abstract:
* Initialize both discrete states to one.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = ssGetRealDiscStates(S);
int_T lp;
for (lp=0;lp<2;lp++) {
*x0++=1.0;
}
}
The required mdlOutputs function computes the output signal of this S-function. The beginning of the function obtains
pointers to the first output port, discrete states, and first input port. The S-function uses the data in these arrays to solve the
output equation y=Cx+Du.
/* y=Cx+Du */
y[0]=C[0][0]*x[0]+C[0][1]*x[1]+D[0][0]*U(0)+D[0][1]*U(1);
y[1]=C[1][0]*x[0]+C[1][1]*x[1]+D[1][0]*U(0)+D[1][1]*U(1);
}
The Simulink engine calls the mdlUpdate function once every major integration time step to update the discrete states'
values. Because this function is an optional method, a #define statement must precede the function. The beginning of the
function obtains pointers to the S-function's discrete states and first input port. The S-function uses the data in these arrays to
solve the equation dx=Ax+Bu, which is stored in the temporary variable tempX before being assigned into the discrete state
vector x.
#define MDL_UPDATE
/* Function: mdlUpdate ======================================================
* Abstract:
* xdot = Ax + Bu
6 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T tempX[2] = {0.0, 0.0};
real_T *x = ssGetRealDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/* xdot=Ax+Bu */
tempX[0]=A[0][0]*x[0]+A[0][1]*x[1]+B[0][0]*U(0)+B[0][1]*U(1);
tempX[1]=A[1][0]*x[0]+A[1][1]*x[1]+B[1][0]*U(0)+B[1][1]*U(1);
x[0]=tempX[0];
x[1]=tempX[1];
}
The required mdlTerminate function performs any actions, such as freeing memory, necessary at the end of the simulation.
In this example, the function is empty.
The required S-function trailer includes the files necessary for simulation or code generation, as follows.
Note The mdlOutputs and mdlTerminate functions use the UNUSED_ARG macro to indicate that an input
argument the callback requires is not used. This optional macro is defined in
matlabroot/simulink/include/simstruc_types.h. If used, you must call this macro once for each input
argument that a callback does not use.
Back to Top
matlabroot/toolbox/simulink/simdemos/simfeatures/sfcndemo_mixedm.mdl
If you have a hybrid system, the mdlDerivatives method calculates the derivatives of the continuous states of the state
vector, x, and the mdlUpdate method contains the equations used to update the discrete state vector, xD. The mdlOutputs
method computes the S-function outputs after checking for sample hits to determine at what point the S-function is being
called.
matlabroot/simulink/src/mixedm.c
The S-function begins with #define statements for the S-function's name and level, along with a #include statement for the
simstruc.h header. After these statements, the S-function can include or define any other necessary headers, data, etc. The
mixedm.c example defines U as a pointer to the first input port's signal.
/* File : mixedm.c
* Abstract:
*
* An example S-function illustrating multiple sample times by implementing
7 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
#include "simstruc.h"
The required S-function method mdlInitializeSizes then sets up the following S-function characteristics.
*====================*
* S-function methods *
*====================*/
ssSetNumContStates(S, 1);
ssSetNumDiscStates(S, 1);
ssSetNumRWork(S, 1); /* for zoh output feeding the delay operator */
ssSetNumSampleTimes(S, 2);
8 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
SS_OPTION_PORT_SAMPLE_TIMES_ASSIGNED));
} /* end mdlInitializeSizes */
The required S-function method mdlInitializeSampleTimes specifies the S-function's block-based sample rates. The first
call to ssSetSampleTime specifies that the first sample rate is continuous, with the subsequent call to ssSetOffsetTime
setting the offset to zero. The second call to this pair of macros sets the second sample time to 1 with an offset of zero. The
S-function's port-based sample times set in mdlInitializeSizes must all be registered as a block-based sample time. The
call to ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the default rule to determine if
submodels containing this S-function can inherit their sample times from the parent model.
ssSetSampleTime(S, 1, 1.0);
ssSetOffsetTime(S, 1, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
} /* end mdlInitializeSampleTimes */
The optional S-function method mdlInitializeConditions initializes the continuous and discrete state vectors. The
#define statement before this method is required for the Simulink engine to call this function. In this example,
ssGetContStates obtains a pointer to the continuous state vector and ssGetRealDiscStates obtains a pointer to the
discrete state vector. The method then sets all states' initial conditions to one.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ==========================================
* Abstract:
* Initialize both continuous states to one.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *xC0 = ssGetContStates(S);
real_T *xD0 = ssGetRealDiscStates(S);
xC0[0] = 1.0;
xD0[0] = 1.0;
} /* end mdlInitializeConditions */
The required mdlOutputs function performs computations based on the current task. The macro ssIsContinuousTask
checks if the continuous task is executing. If this macro returns true, ssIsSpecialSampleHit then checks if the discrete
sample rate is also executing. If this macro also returns true, the method sets the value of the floating-point work vector to
the current value of the continuous state, via pointers obtained using ssGetRWork and ssGetContStates, respectively. The
mdlUpdate method later uses the floating-point work vector as the input to the zero-order hold. Updating the work vector in
mdlOutputs ensures that the correct values are available during subsequent calls to mdlUpdate. Finally, if the S-function is
running at its discrete rate, i.e., the call to ssIsSampleHit returns true, the method sets the output to the value of the
discrete state.
/* y=xD */
if (ssIsSampleHit(S, 1, tid)) {
real_T *y = ssGetOutputPortRealSignal(S,0);
real_T *xD = ssGetRealDiscStates(S);
y[0]=xD[0];
}
} /* end mdlOutputs */
9 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
The Simulink engine calls the mdlUpdate function once every major integration time step to update the discrete states'
values. Because this function is an optional method, a #define statement must precede the function. The call to
ssIsSampleHit ensures the body of the method is executed only when the S-function is operating at its discrete rate. If
ssIsSampleHit returns true, the method obtains pointers to the S-function's discrete state and floating-point work vector
and updates the discrete state's value using the value stored in the work vector.
#define MDL_UPDATE
/* Function: mdlUpdate ======================================================
* Abstract:
* xD = xC
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
UNUSED_ARG(tid); /* not used in single tasking mode */
/* xD=xC */
if (ssIsSampleHit(S, 1, tid)) {
real_T *xD = ssGetRealDiscStates(S);
real_T *zoh = ssGetRWork(S);
xD[0]=*zoh;
}
} /* end mdlUpdate */
The mdlDerivatives function calculates the continuous state derivatives. Because this function is an optional method, a
#define statement must precede the function. The function obtains pointers to the S-function's continuous state derivative
and first input port then sets the continuous state derivative equal to the value of the first input.
#define MDL_DERIVATIVES
/* Function: mdlDerivatives =================================================
* Abstract:
* xdot = U
*/
static void mdlDerivatives(SimStruct *S)
{
real_T *dx = ssGetdX(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/* xdot=U */
dx[0]=U(0);
} /* end mdlDerivatives */
The required mdlTerminate function performs any actions, such as freeing memory, necessary at the end of the simulation.
In this example, the function is empty.
The S-function trailer includes the files necessary for simulation or code generation, as follows.
Note The mdlUpdate and mdlTerminate functions use the UNUSED_ARG macro to indicate that an input
argument the callback requires is not used. This optional macro is defined in
matlabroot/simulink/include/simstruc_types.h. If used, you must call this macro once for each input
argument that a callback does not use.
Back to Top
matlabroot/toolbox/simulink/simdemos/simfeatures/sfcndemo_vsfunc.mdl
10 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
Variable step-size functions require a call to mdlGetTimeOfNextVarHit, which is an S-function routine that calculates the
time of the next sample hit. S-functions that use the variable-step sample time can be used only with variable-step solvers.
The vsfunc.c example is a discrete S-function that delays its first input by an amount of time determined by the second
input.
The vsfunc.c example outputs the input u delayed by a variable amount of time. mdlOutputs sets the output y equal to
state x. mdlUpdate sets the state vector x equal to u, the input vector. This example calls mdlGetTimeOfNextVarHit to
calculate and set the time of the next sample hit, that is, the time when vsfunc.c is next called. In
mdlGetTimeOfNextVarHit, the macro ssGetInputPortRealSignalPtrs gets a pointer to the input u. Then this call is
made:
ssSetTNext(S, ssGetT(S)(*u[1]));
The macro ssGetT gets the simulation time t. The second input to the block, (*u[1]), is added to t, and the macro
ssSetTNext sets the time of the next hit equal to t+(*u[1]), delaying the output by the amount of time set in (*u[1]).
matlabroot/simulink/src/vsfunc.c
The S-function begins with #define statements for the S-function's name and level, along with a #include statement for the
simstruc.h header. After these statements, the S-function can include or define any other necessary headers, data, etc. The
vsfunc.c example defines U as a pointer to the first input port's signal.
/* File : vsfunc.c
* Abstract:
*
* Variable step S-function example.
* This example S-function illustrates how to create a variable step
* block. This block implements a variable step delay
* in which the first input is delayed by an amount of time determined
* by the second input:
*
* dt = u(2)
* y(t+dt) = u(t)
*
* For more details about S-functions, see simulink/src/sfuntmpl_doc.c.
*
* Copyright 1990-2007 The MathWorks, Inc.
*/
#include "simstruc.h"
The required S-function method mdlInitializeSizes then sets up the following S-function characteristics.
11 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 1);
ssSetNumSampleTimes(S, 1);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
The required S-function method mdlInitializeSampleTimes specifies the S-function's sample rates. The input argument
VARIABLE_SAMPLE_TIME passed to ssSetSampleTime specifies that this S-function has a variable-step sample time and
ssSetOffsetTime specifies an offset time of zero. The call to
ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the default rule to determine if
submodels containing this S-function can inherit their sample times from the parent model. Because the S-function has a
variable-step sample time, vsfunc.c must calculate the time of the next sample hit in the mdlGetTimeOfNextVarHit
method, shown later.
The optional S-function method mdlInitializeConditions initializes the discrete state vector. The #define statement
before this method is required for the Simulink engine to call this function. In the example, the method uses
ssGetRealDiscStates to obtain a pointer to the discrete state vector and sets the state's initial value to zero.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ========================================
* Abstract:
* Initialize discrete state to zero.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *x0 = ssGetRealDiscStates(S);
x0[0] = 0.0;
}
The optional mdlGetTimeOfNextVarHit method calculates the time of the next sample hit. Because this method is
optional, a #define statement precedes it. First, this method obtains a pointer to the first input port's signal using
ssGetInputPortRealSignalPtrs. If the input signal's second element is positive, the macro ssGetT gets the simulation
time t. The macro ssSetTNext sets the time of the next hit equal to t+(*U[1]), delaying the output by the amount of time
specified by the input's second element (*U[1]).
#define MDL_GET_TIME_OF_NEXT_VAR_HIT
12 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
The required mdlOutputs function computes the S-function's output signal. The function obtains pointers to the first output
port and discrete state and then assigns the state's current value to the output.
The mdlUpdate function updates the discrete state's value. Because this method is optional, a #define statement precedes
it. The function first obtains pointers to the S-function's discrete state and first input port then assigns the value of the first
element of the first input port signal to the state.
#define MDL_UPDATE
/* Function: mdlUpdate ========================================================
* Abstract:
* This function is called once for every major integration time step.
* Discrete states are typically updated here, but this function is useful
* for performing any tasks that should only take place once per integration
* step.
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T *x = ssGetRealDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
x[0]=U(0);
}
The required mdlTerminate function performs any actions, such as freeing memory, necessary at the end of the simulation.
In this example, the function is empty.
The required S-function trailer includes the files necessary for simulation or code generation, as follows.
Back to Top
matlabroot/toolbox/simulink/simdemos/simfeatures/sfcndemo_matadd.mdl
The S-function adds signals of various dimensions to a parameter value entered in the S-function. The S-function accepts and
13 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
matlabroot/simulink/src/sfun_matadd.c
The S-function begins with #define statements for the S-function's name and level, along with a #include statement for the
simstruc.h header. After these statements, the S-function includes or defines any other necessary headers, data, etc. This
example defines additional variables for the number of S-function parameters, the S-function parameter value, and the flag
EDIT_OK that indicates if the parameter value can be edited during simulation.
#include "simstruc.h"
The S-function next implements the mdlCheckParameters method to validate the S-function dialog parameters. The
#ifdef statement checks that the S-function is compiled as a MEX-file, instead of for use with the Real-Time Workshop
product. Because mdlCheckParameters is optional, the S-function code contains a #define statement to register the
method. The body of the function checks that the S-function parameter value is not empty. If the parameter check fails, the
S-function errors out with a call to ssSetErrorStatus.
#ifdef MATLAB_MEX_FILE
#define MDL_CHECK_PARAMETERS
/* Function: mdlCheckParameters ================================
* Abstract:
* Verify parameter settings.
*/
static void mdlCheckParameters(SimStruct *S)
{
if(EDIT_OK(S, PARAM_ARG)){
/* Check that parameter value is not empty*/
if( mxIsEmpty(PARAM_ARG) ) {
ssSetErrorStatus(S, "Invalid parameter specified. The"
"parameter must be non-empty");
return;
}
}
} /* end mdlCheckParameters */
#endif
The required S-function method mdlInitializeSizes then sets up the following S-function characteristics.
ssSetNumSFcnParams sets the number of expected S-function dialog parameters to one, as defined by the variable
NUM_PARAMS.
If this S-function is compiled as a MEX-file, ssGetSFcnParamsCount determines how many parameters the user
actually entered into the S-function dialog. If the number of user-specified parameters matches the number returned by
ssGetNumSFcnParams, the method calls mdlCheckParameters to validate the user-entered data. Otherwise, the
S-function errors out.
If the parameter check passes, the S-function specifies that all S-function parameters are tunable using
ssSetSFcnParamTunable.
The S-function then invokes ssAllowSignalsWithMoreThan2D to allow the S-function to accept n-D signals.
Next, ssSetNumOutputPorts and ssSetNumInputPorts specify that the S-function has a single output port and a
14 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; }
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) return;
#endif
{
int iParam = 0;
int nParam = ssGetNumSFcnParams(S);
if( pWidth == 1) {
/* Scalar parameter: output dimensions are unknown. */
if(!ssSetOutputPortDimensionInfo(S,0,DYNAMIC_DIMENSION)){
return; }
}
else{
/*
* Non-scalar parameter: output dimensions are the same
* as the parameter dimensions. To support n-D signals,
* must use a dimsInfo structure to specify dimensions.
*/
DECL_AND_INIT_DIMSINFO(di); /*Initializes structure*/
int_T pSize = mxGetNumberOfDimensions(PARAM_ARG);
const int_T *pDims = mxGetDimensions(PARAM_ARG);
di.width = pWidth;
di.numDims = pSize;
di.dims = pDims;
if(!ssSetOutputPortDimensionInfo(S, 0, &di)) return;
}
}
15 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
ssSetInputPortDirectFeedThrough(S, 0, 1);
ssSetNumSampleTimes(S, 1);
ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_EXCEPTION_FREE_CODE);
} /* end mdlInitializeSizes */
The required S-function method mdlInitializeSampleTimes specifies the S-function's sample rates. To specify that this
S-function inherits its sample time from its driving block, the S-function calls ssSetSampleTime with the input argument
INHERITED_SAMPLE_TIME. The call to ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to
use the default rule to determine if submodels containing this S-function can inherit their sample times from the parent model.
The S-function calls the mdlSetWorkWidths method to register its run-time parameters. Because mdlSetWorkWidths is
an optional method, a #define statement precedes it. The method first initializes a name for the run-time parameter and then
uses ssRegAllTunableParamsAsRunTimeParams to register the run-time parameter.
The S-function's mdlOutputs method uses a for loop to calculate the output as the sum of the input and S-function
parameter. The S-function handles n-D arrays of data using a single index into the array.
/*
* Note1: Matrix signals are stored in column major order.
* Note2: Access each matrix element by one index not two
* indices. For example, if the output signal is a
* [2x2] matrix signal,
* - -
* | y[0] y[2] |
* | y[1] y[3] |
* - -
* Output elements are stored as follows:
* y[0] --> row = 0, col = 0
* y[1] --> row = 1, col = 0
* y[2] --> row = 0, col = 1
* y[3] --> row = 1, col = 1
*/
16 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
During signal propagation, the S-function calls the optional mdlSetInputPortDimensionInfo method with the candidate
input port dimensions stored in dimsInfo. The #if defined statement checks that the S-function is compiled as a
MEX-file. Because mdlSetInputPortDimensionInfo is an optional method, a #define statement precedes it. In
mdlSetInputPortDimensionInfo, the S-function uses ssSetInputPortDimensionInfo to set the dimensions of the
input port to the candidate dimensions. If the call to this macro succeeds, the S-function further checks the candidate
dimensions to ensure that the input signal is either a 2-D scalar or a matrix. If this condition is met and the output port
dimensions are still dynamically sized, the S-function calls ssSetOutputPortDimensionInfo to set the dimension of the
output port to the same candidate dimensions. The ssSetOutputPortDimensionInfo macro cannot modify the output
port dimensions if they are already specified.
#if defined(MATLAB_MEX_FILE)
#define MDL_SET_INPUT_PORT_DIMENSION_INFO
/* Function: mdlSetInputPortDimensionInfo ======================
* Abstract:
* This routine is called with the candidate dimensions for
* an input port with unknown dimensions. If the proposed
* dimensions are acceptable, the routine should go ahead and
* set the actual port dimensions. If they are unacceptable
* an error should be generated via ssSetErrorStatus.
* Note that any other input or output ports whose dimensions
* are implicitly defined by virtue of knowing the dimensions
* of the given port can also have their dimensions set.
*/
static void mdlSetInputPortDimensionInfo(SimStruct *S,
int_T port,
const DimsInfo_T *dimsInfo)
{
int_T pWidth = mxGetNumberOfElements(PARAM_ARG);
int_T pSize = mxGetNumberOfDimensions(PARAM_ARG);
const int_T *pDims = mxGetDimensions(PARAM_ARG);
int_T numDims;
boolean_T isOk = true;
int iParam = 0;
int_T outWidth = ssGetOutputPortWidth(S, 0);
/*
* The block only accepts 2-D or higher signals. Check
* number of dimensions. If the parameter and the input
* signal are non-scalar, their dimensions must be the same.
*/
isOk = (uNumDims >= 2) && (pWidth == 1 || uWidth == 1 ||
pWidth == uWidth);
numDims = (pSize != uNumDims) ? numDims : uNumDims;
if(!isOk){
ssSetErrorStatus(S,"Invalid input port dimensions. The "
"input signal must be a 2-D scalar signal, or it must "
"be a matrix with the same dimensions as the parameter "
"dimensions.");
return;
}
During signal propagation, if any output ports have unknown dimensions, the S-function calls the optional
mdlSetOutputPortDimensionInfo method. Because this method is optional, a #define statement precedes it. In
17 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
# define MDL_SET_OUTPUT_PORT_DIMENSION_INFO
/* Function: mdlSetOutputPortDimensionInfo =====================
* Abstract:
* This routine is called with the candidate dimensions for
* an output port with unknown dimensions. If the proposed
* dimensions are acceptable, the routine should go ahead and
* set the actual port dimensions. If they are unacceptable
* an error should be generated via ssSetErrorStatus.
* Note that any other input or output ports whose dimensions
* are implicitly defined by virtue of knowing the dimensions
* of the given port can also have their dimensions set.
*/
static void mdlSetOutputPortDimensionInfo(SimStruct *S,
int_T port,
const DimsInfo_T *dimsInfo)
{
/*
* If the block has scalar parameter, the output dimensions
* are unknown. Set the input and output port to have the
* same dimensions.
*/
if(!ssSetOutputPortDimensionInfo(S, port, dimsInfo)) return;
Because the S-function has ports that are dynamically sized, it must provide an mdlSetDefaultPortDimensionInfo
method. The Simulink engine invokes this method during signal propagation when it cannot determine the dimensionality of
the signal connected to the block's input port. This situation can happen, for example, if the input port is unconnected. In this
example, the mdlSetDefaultPortDimensionInfo method sets the input and output ports dimensions to a scalar.
# define MDL_SET_DEFAULT_PORT_DIMENSION_INFO
/* Function: mdlSetDefaultPortDimensionInfo ====================
* This routine is called when the Simulink engine is not able
* to find dimension candidates for ports with unknown dimensions.
* This function must set the dimensions of all ports with
* unknown dimensions.
*/
static void mdlSetDefaultPortDimensionInfo(SimStruct *S)
{
int_T outWidth = ssGetOutputPortWidth(S, 0);
/* Input port dimension must be unknown. Set it to scalar.*/
if(!ssSetInputPortMatrixDimensions(S, 0, 1, 1)) return;
if(outWidth == DYNAMICALLY_SIZED){
/* Output dimensions are unknown. Set it to scalar. */
if(!ssSetOutputPortMatrixDimensions(S, 0, 1, 1)) return;
}
} /* end mdlSetDefaultPortDimensionInfo */
#endif
The required mdlTerminate function performs any actions, such as freeing memory, necessary at the end of the simulation.
In this example, the function is empty.
} /* end mdlTerminate */
18 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
The required S-function's trailer includes the files necessary for simulation or code generation.
#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif
/* [EOF] sfun_matadd.c */
Note The mdlOutputs and mdlTerminate functions use the UNUSED_ARG macro to indicate that an input
argument the callback requires is not used. This optional macro is defined in
matlabroot/simulink/include/simstruc_types.h. You must call this macro once for each input argument
that a callback does not use.
Back to Top
Zero-Crossing Detection
The example S-function matlabroot/simulink/src/sfun_zc_sat.c demonstrates how to implement a Saturation
block. The following Simulink model uses this S-function.
matlabroot/toolbox/simulink/simdemos/simfeatures/sfcndemo_sfun_zc_sat.mdl
The S-function works with either fixed- or variable-step solvers. When this S-function inherits a continuous sample time and
uses a variable-step solver, it uses a zero-crossings algorithm to locate the exact points at which the saturation occurs.
matlabroot/simulink/src/sfun_zc_sat.c
The S-function begins with #define statements for the S-function's name and level, along with a #include statement for the
simstruc.h header. After these statements, the S-function includes or defines any other necessary headers, data, etc. This
example defines various parameters associated with the upper and lower saturation bounds.
/* File : sfun_zc_sat.c
* Abstract:
*
* Example of an S-function which has nonsampled zero crossings to
* implement a saturation function. This S-function is designed to be
* used with a variable or fixed step solver.
*
* A saturation is described by three equations
*
* (1) y = UpperLimit
* (2) y = u
* (3) y = LowerLimit
*
* and a set of inequalities that specify which equation to use
*
* if UpperLimit < u then use (1)
* if LowerLimit <= u <= UpperLimit then use (2)
* if u < LowerLimit then use (3)
*
* A key fact is that the valid equation 1, 2, or 3, can change at
* any instant. Nonsampled zero crossing support helps the variable step
* solvers locate the exact instants when behavior switches from one equation
* to another.
*
* Copyright 1990-2007 The MathWorks, Inc.
*/
#include "simstruc.h"
/*========================*
* General Defines/macros *
*========================*/
19 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
#define N_PAR 2
/*
* Make access to mxArray pointers for parameters more readable.
*/
#define P_PAR_UPPER_LIMIT ( ssGetSFcnParam(S,I_PAR_UPPER_LIMIT) )
#define P_PAR_LOWER_LIMIT ( ssGetSFcnParam(S,I_PAR_LOWER_LIMIT) )
This S-function next implements the mdlCheckParameters method to check the validity of the S-function dialog parameters.
Because this method is optional, a #define statement precedes it. The #if defined statement checks that this function is
compiled as a MEX-file, instead of for use with the Real-Time Workshop product. The body of the function performs basic
checks to ensure that the user entered real vectors of equal length for the upper and lower saturation limits. If the parameter
checks fail, the S-function errors out.
#define MDL_CHECK_PARAMETERS
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
/*
* check parameter basics
*/
for ( i = 0; i < N_PAR; i++ ) {
if ( mxIsEmpty( ssGetSFcnParam(S,i) ) ||
mxIsSparse( ssGetSFcnParam(S,i) ) ||
mxIsComplex( ssGetSFcnParam(S,i) ) ||
!mxIsNumeric( ssGetSFcnParam(S,i) ) ) {
msg = "Parameters must be real vectors.";
goto EXIT_POINT;
}
}
/*
* Check sizes of parameters.
*/
numUpperLimit = mxGetNumberOfElements( P_PAR_UPPER_LIMIT );
numLowerLimit = mxGetNumberOfElements( P_PAR_LOWER_LIMIT );
if ( ( numUpperLimit != 1 ) &&
( numLowerLimit != 1 ) &&
( numUpperLimit != numLowerLimit ) ) {
msg = "Number of input and output values must be equal.";
goto EXIT_POINT;
}
/*
* Error exit point
*/
EXIT_POINT:
if (msg != NULL) {
ssSetErrorStatus(S, msg);
}
}
#endif /* MDL_CHECK_PARAMETERS */
The required S-function method mdlInitializeSizes sets up the following S-function characteristics.
ssSetNumSFcnParams sets the number of expected S-function dialog parameters to two, as defined previously in the
variable N_PAR.
If this method is compiled as a MEX-file, ssGetSFcnParamsCount determines how many parameters the user
actually entered into the S-function dialog. If the number of user-specified parameters matches the number returned by
ssGetNumSFcnParams, the method calls mdlCheckParameters to check the validity of the user-entered data.
Otherwise, the S-function errors out.
If the parameter check passes, the S-function determines the maximum number of elements entered into either the
upper or lower saturation limit parameter. This number is needed later to determine the appropriate output width.
Next, the number of continuous and discrete states is set using ssSetNumContStates and ssSetNumDiscStates,
respectively. This example has no continuous or discrete states.
The method specifies that the S-function has a single output port using ssSetNumOutputPorts and sets the width of
20 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
this output port using ssSetOutputPortWidth. The output port width is either the maximum number of elements in
the upper or lower saturation limit or is dynamically sized. Similar code specifies a single input port and indicates the
input port has direct feedthrough by passing a value of 1 to ssSetInputPortDirectFeedThrough.
ssSetNumSampleTimes initializes one sample time, which the mdlInitializeSampleTimes function configures
later.
The S-function indicates that no work vectors are used by passing a value of 0 to ssSetNumRWork, ssSetNumIWork,
etc. These lines could be omitted because zero is the default value for all of these macros. However, for clarity, the
S-function explicitly sets the number of work vectors.
The method initializes the zero-crossing detection work vectors using ssSetNumModes and
ssSetNumNonsampledZCs. The mdlSetWorkWidths method specifies the length of these dynamically sized vectors
later.
Lastly, ssSetOptions sets any applicable options. In this case, SS_OPTION_EXCEPTION_FREE_CODE stipulates
that the code is exception free and SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION permits scalar expansion of the
input without having to provide an mdlSetInputPortWidth function.
/*
* Set and Check parameter count
*/
ssSetNumSFcnParams(S, N_PAR);
#if defined(MATLAB_MEX_FILE)
if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
mdlCheckParameters(S);
if (ssGetErrorStatus(S) != NULL) {
return;
}
} else {
return; /* Parameter mismatch reported by the Simulink engine*/
}
#endif
/*
* Get parameter size info.
*/
numUpperLimit = mxGetNumberOfElements( P_PAR_UPPER_LIMIT );
numLowerLimit = mxGetNumberOfElements( P_PAR_LOWER_LIMIT );
/*
* states
*/
ssSetNumContStates(S, 0);
ssSetNumDiscStates(S, 0);
/*
* outputs
* The upper and lower limits are scalar expanded
* so their size determines the size of the output
* only if at least one of them is not scalar.
*/
if (!ssSetNumOutputPorts(S, 1)) return;
if ( maxNumLimit > 1 ) {
ssSetOutputPortWidth(S, 0, maxNumLimit);
} else {
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
}
/*
* inputs
* If the upper or lower limits are not scalar then
* the input is set to the same size. However, the
21 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
ssSetInputPortDirectFeedThrough(S, 0, 1 );
if ( maxNumLimit > 1 ) {
ssSetInputPortWidth(S, 0, maxNumLimit);
} else {
ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
}
/*
* sample times
*/
ssSetNumSampleTimes(S, 1);
/*
* work
*/
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
/*
* Modes and zero crossings:
* If we have a variable-step solver and this block has a continuous
* sample time, then
* o One mode element will be needed for each scalar output
* in order to specify which equation is valid (1), (2), or (3).
* o Two ZC elements will be needed for each scalar output
* in order to help the solver find the exact instants
* at which either of the two possible "equation switches"
* One will be for the switch from eq. (1) to (2);
* the other will be for eq. (2) to (3) and vice versa.
* otherwise
* o No modes and nonsampled zero crossings will be used.
*
*/
ssSetNumModes(S, DYNAMICALLY_SIZED);
ssSetNumNonsampledZCs(S, DYNAMICALLY_SIZED);
/*
* options
* o No mexFunctions and no problematic mxFunctions are called
* so the exception free code option safely gives faster simulations.
* o Scalar expansion of the inputs is desired. The option provides
* this without the need to write mdlSetOutputPortWidth and
* mdlSetInputPortWidth functions.
*/
ssSetOptions(S, ( SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_ALLOW_INPUT_SCALAR_EXPANSION));
} /* end mdlInitializeSizes */
The required S-function method mdlInitializeSampleTimes specifies the S-function's sample rates. The input argument
INHERITED_SAMPLE_TIME passed to ssSetSampleTime specifies that this S-function inherits its sample time from its
driving block. The call to ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to use the default rule
to determine if submodels containing this S-function can inherit their sample times from the parent model.
The optional method mdlSetWorkWidths initializes the size of the zero-crossing detection work vectors. Because this
method is optional, a #define statement precedes it. The #if defined statement checks that the S-function is being
compiled as a MEX-file. Zero-crossing detection can be done only when the S-function is running at a continuous sample rate
using a variable-step solver. The if statement uses ssIsVariableStepSolver, ssGetSampleTime, and
ssGetOffsetTime to determine if this condition is met. If so, the method sets the number of modes equal to the width of the
first output port and the number of nonsampled zero crossings to twice this amount. Otherwise, the method sets both values to
22 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
zero.
#define MDL_SET_WORK_WIDTHS
#if defined(MDL_SET_WORK_WIDTHS) && defined(MATLAB_MEX_FILE)
/* Function: mdlSetWorkWidths ===============================================
* The width of the Modes and the ZCs depends on the width of the output.
* This width is not always known in mdlInitializeSizes so it is handled
* here.
*/
static void mdlSetWorkWidths(SimStruct *S)
{
int nModes;
int nNonsampledZCs;
if (ssIsVariableStepSolver(S) &&
ssGetSampleTime(S,0) == CONTINUOUS_SAMPLE_TIME &&
ssGetOffsetTime(S,0) == 0.0) {
/*
* modes and zero crossings
* o One mode element will be needed for each scalar output
* in order to specify which equation is valid (1), (2), or (3).
* o Two ZC elements will be needed for each scalar output
* in order to help the solver find the exact instants
* at which either of the two possible "equation switches"
* One will be for the switch from eq. (1) to (2);
* the other will be for eq. (2) to (3) and vice versa.
*/
nModes = numOutput;
nNonsampledZCs = 2 * numOutput;
} else {
nModes = 0;
nNonsampledZCs = 0;
}
ssSetNumModes(S,nModes);
ssSetNumNonsampledZCs(S,nNonsampledZCs);
}
#endif /* MDL_SET_WORK_WIDTHS */
After declaring variables for the input and output signals, the mdlOutputs functions uses an if-else statement to create
blocks of code used to calculate the output signal based on whether the S-function uses a fixed-step or variable-step solver.
The if statement queries the length of the nonsampled zero-crossing vector. If the length, set in mdlWorkWidths, is zero,
then no zero-crossing detection is done and the output signals are calculated directly from the input signals. Otherwise, the
function uses the mode work vector to determine how to calculate the output signal. If the simulation is at a major time step,
i.e., ssIsMajorTimeStep returns true, mdlOutputs determines which mode the simulation is running in, either saturated
at the upper limit, saturated at the lower limit, or not saturated. Then, for both major and minor time steps, the function
calculates an output based on this mode. If the mode changed between the previous and current time step, then a zero
crossing occurred. The mdlZeroCrossings function, not mdlOutputs, indicates this crossing to the solver.
23 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
* During a minor time step, the equation specified by the mode vector
* is used without question. Most of the time, the value of u will agree
* with the equation specified by the mode vector. However, sometimes u's
* value will indicate a different equation. Nonetheless, the equation
* specified by the mode vector must be used.
*
* When the mode and u indicate different equations, the corresponding
* calculations are not correct. However, this is not a problem. From
* the ZC function, the solver will know that an equation switch occurred
* in the middle of the last MajorTimeStep. The calculations for that
* time step will be discarded. The ZC function will help the solver
* find the exact instant at which the switch occurred. Using this knowledge,
* the length of the MajorTimeStep will be reduced so that only one equation
* is valid throughout the entire time step.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T *y = ssGetOutputPortRealSignal(S,0);
int_T numOutput = ssGetOutputPortWidth(S,0);
int_T iOutput;
/*
* Set index and increment for input signal, upper limit, and lower limit
* parameters so that each gives scalar expansion if needed.
*/
int_T uIdx = 0;
int_T uInc = ( ssGetInputPortWidth(S,0) > 1 );
const real_T *upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
int_T upperLimitInc = ( mxGetNumberOfElements( P_PAR_UPPER_LIMIT ) > 1 );
const real_T *lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );
int_T lowerLimitInc = ( mxGetNumberOfElements( P_PAR_LOWER_LIMIT ) > 1 );
if (ssGetNumNonsampledZCs(S) == 0) {
/*
* This block is being used with a fixed-step solver or it has
* a noncontinuous sample time, so we always saturate.
*/
for (iOutput = 0; iOutput < numOutput; iOutput++) {
if (*uPtrs[uIdx] >= *upperLimit) {
*y++ = *upperLimit;
} else if (*uPtrs[uIdx] > *lowerLimit) {
*y++ = *uPtrs[uIdx];
} else {
*y++ = *lowerLimit;
}
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
uIdx += uInc;
}
} else {
/*
* This block is being used with a variable-step solver.
*/
int_T *mode = ssGetModeVector(S);
/*
* Specify indices for each equation.
*/
enum { UpperLimitEquation, NonLimitEquation, LowerLimitEquation };
/*
* Update the Mode Vector ONLY at the beginning of a MajorTimeStep
*/
if ( ssIsMajorTimeStep(S) ) {
/*
* Specify the mode, ie the valid equation for each output scalar.
*/
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
if ( *uPtrs[uIdx] > *upperLimit ) {
/*
* Upper limit eq is valid.
*/
mode[iOutput] = UpperLimitEquation;
} else if ( *uPtrs[uIdx] < *lowerLimit ) {
/*
24 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
/*
* Reset index to input and limits.
*/
uIdx = 0;
upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );
} /* end IsMajorTimeStep */
/*
* For both MinorTimeSteps and MajorTimeSteps calculate each scalar
* output using the equation specified by the mode vector.
*/
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
if ( mode[iOutput] == UpperLimitEquation ) {
/*
* Upper limit eq.
*/
*y++ = *upperLimit;
} else if ( mode[iOutput] == LowerLimitEquation ) {
/*
* Lower limit eq.
*/
*y++ = *lowerLimit;
} else {
/*
* Nonlimit eq.
*/
*y++ = *uPtrs[uIdx];
}
/*
* Adjust indices to give scalar expansion if needed.
*/
uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
}
} /* end mdlOutputs */
The mdlZeroCrossings method determines if a zero crossing occurred between the previous and current time step. The
method obtains a pointer to the input signal using ssGetInputPortRealSignalPtrs. A comparison of this signal's value
to the value of the upper and lower saturation limits determines values for the elements of the nonsampled zero-crossing
vector. If any element of the nonsampled zero-crossing vector switches from negative to positive, or positive to negative, a
zero crossing occurred. In the event of a zero crossing, the Simulink engine modifies the step size and recalculates the outputs
to try to locate the exact zero crossing.
#define MDL_ZERO_CROSSINGS
#if defined(MDL_ZERO_CROSSINGS) && (defined(MATLAB_MEX_FILE) || defined(NRT))
25 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
/*
* Set index and increment for the input signal, upper limit, and lower
* limit parameters so that each gives scalar expansion if needed.
*/
int_T uIdx = 0;
int_T uInc = ( ssGetInputPortWidth(S,0) > 1 );
real_T *upperLimit = mxGetPr( P_PAR_UPPER_LIMIT );
int_T upperLimitInc = ( mxGetNumberOfElements( P_PAR_UPPER_LIMIT ) > 1 );
real_T *lowerLimit = mxGetPr( P_PAR_LOWER_LIMIT );
int_T lowerLimitInc = ( mxGetNumberOfElements( P_PAR_LOWER_LIMIT ) > 1 );
/*
* For each output scalar, give the solver a measure of "how close things
* are" to an equation switch.
*/
for ( iOutput = 0; iOutput < numOutput; iOutput++ ) {
26 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
* u - LowerLimit.
*/
zcSignals[2*iOutput+1] = *uPtrs[uIdx] - *lowerLimit;
/*
* Adjust indices to give scalar expansion if needed.
*/
uIdx += uInc;
upperLimit += upperLimitInc;
lowerLimit += lowerLimitInc;
}
}
The S-function concludes with the required mdlTerminate function. In this example, the function is empty.
The required S-function trailer includes the files necessary for simulation or code generation, as follows.
Note The mdlOutputs and mdlTerminate functions use the UNUSED_ARG macro to indicate that an input
argument the callback requires is not used. This optional macro is defined in
matlabroot/simulink/include/simstruc_types.h. If used, you must call this macro once for each input
argument that a callback does not use.
Back to Top
matlabroot/toolbox/simulink/simdemos/simfeatures/sfcndemo_stvctf.mdl
The S-function demonstrates how to work with the solvers so that the simulation maintains consistency, which means that the
block maintains smooth and consistent signals for the integrators although the equations that are being integrated are
changing.
matlabroot/simulink/src/stvctf.c
The S-function begins with #define statements for the S-function's name and level, along with a #include statement for the
simstruc.h header. After these statements, the S-function includes or defines any other necessary headers, data, etc. This
example defines parameters for the transfer function's numerator and denominator, which are entered into the S-function's
dialog. The comments at the beginning of this S-function provide additional information on the purpose of the work vectors in
this example.
/*
* File : stvctf.c
* Abstract:
* Time Varying Continuous Transfer Function block
*
* This S-function implements a continuous time transfer function
* whose transfer function polynomials are passed in via the input
* vector. This is useful for continuous time adaptive control
* applications.
*
* This S-function is also an example of how to use banks to avoid
* problems with computing derivatives when a continuous output has
* discontinuities. The consistency checker can be used to verify that
* your S-function is correct with respect to always maintaining smooth
* and consistent signals for the integrators. By consistent we mean that
* two mdlOutputs calls at major time t and minor time t are always the
* same. The consistency checker is enabled on the diagnostics page of the
27 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
#include "simstruc.h"
/*
* Defines for easy access to the numerator and denominator polynomials
* parameters
*/
#define NUM(S) ssGetSFcnParam(S, 0)
#define DEN(S) ssGetSFcnParam(S, 1)
#define TS(S) ssGetSFcnParam(S, 2)
#define NPARAMS 3
This S-function implements the mdlCheckParameters method to check the validity of the S-function dialog parameters.
Because this method is optional, a #define statement precedes it. The #if defined statement checks that this function is
compiled as a MEX-file, instead of for use with the Real-Time Workshop product. The body of the function performs basic
checks to ensure that the user entered real vectors for the numerator and denominator, and that the denominator has a higher
order than the numerator. If the parameter check fails, the S-function errors out.
#define MDL_CHECK_PARAMETERS
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
/* Function: mdlCheckParameters =============================================
* Abstract:
* Validate our parameters to verify:
* o The numerator must be of a lower order than the denominator.
* o The sample time must be a real positive nonzero value.
*/
static void mdlCheckParameters(SimStruct *S)
{
int_T i;
28 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
mxIsSparse( ssGetSFcnParam(S,i)) ||
mxIsComplex( ssGetSFcnParam(S,i)) ||
!mxIsNumeric( ssGetSFcnParam(S,i)) ) {
ssSetErrorStatus(S,"Parameters must be real finite vectors");
return;
}
pr = mxGetPr(ssGetSFcnParam(S,i));
nEls = mxGetNumberOfElements(ssGetSFcnParam(S,i));
for (el = 0; el < nEls; el++) {
if (!mxIsFinite(pr[el])) {
ssSetErrorStatus(S,"Parameters must be real finite vectors");
return;
}
}
}
The required S-function method mdlInitializeSizes then sets up the following S-function characteristics.
ssSetNumSFcnParams sets the number of expected S-function dialog parameters to three, as defined previously in
the variable NPARAMS.
If this method is compiled as a MEX-file, ssGetSFcnParamsCount determines how many parameters the user
entered into the S-function dialog. If the number of user-specified parameters matches the number returned by
ssGetNumSFcnParams, the method calls mdlCheckParameters to check the validity of the user-entered data.
Otherwise, the S-function errors out.
If the parameter check passes, the S-function specifies the number of continuous and discrete states using
ssSetNumContStates and ssSetNumDiscStates, respectively. This example has no discrete states and sets the
number of continuous states based on the number of coefficients in the transfer function's denominator.
Next, ssSetNumInputPorts specifies that the S-function has a single input port and sets its width to one plus twice
the length of the denominator using ssSetInputPortWidth. The method uses the value provided by the third
S-function dialog parameter as the input port's sample time. This parameter indicates the rate at which the transfer
function is modified during simulation. The S-function specifies that the input port has direct feedthrough by passing a
value of 1 to ssSetInputPortDirectFeedThrough.
ssSetNumOutputPorts specifies that the S-function has a single output port. The method uses
ssSetOutputPortWidth to set the width of this output port, ssSetOutputPortSampleTime to specify that the
output port has a continuous sample time, and ssSetOutputPortOffsetTime to set the offset time to zero.
ssSetNumSampleTimes then initializes two sample times, which the mdlInitializeSampleTimes function
configures later.
The method passes a value of four times the number of denominator coefficients to ssSetNumRWork to set the length
of the floating-point work vector. ssSetNumIWork then sets the length of the integer work vector to two. The RWork
vectors store two banks of transfer function coefficients, while the IWork vector indicates which bank in the RWork
vector is currently in use. The S-function sets the length of all other work vectors to zero. These lines could be omitted
because zero is the default value for these macros. However, for clarity, the S-function explicitly sets the number of
work vectors.
Lastly, ssSetOptions sets any applicable options. In this case, SS_OPTION_EXCEPTION_FREE_CODE stipulates
that the code is exception free.
29 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
/*
* Define the characteristics of the block:
*
* Number of continuous states: length of denominator - 1
* Inputs port width 2 * (NumContStates+1) + 1
* Output port width 1
* DirectFeedThrough: 0 (Although this should be computed.
* We'll assume coefficients entered
* are strictly proper).
* Number of sample times: 2 (continuous and discrete)
* Number of Real work elements: 4*NumCoeffs
* (Two banks for num and den coeff's:
* NumBank0Coeffs
* DenBank0Coeffs
* NumBank1Coeffs
* DenBank1Coeffs)
* Number of Integer work elements: 2 (indicator of active bank 0 or 1
* and flag to indicate when banks
* have been updated).
*
* The number of inputs arises from the following:
* o 1 input (u)
* o the numerator and denominator polynomials each have NumContStates+1
* coefficients
*/
nCoeffs = mxGetNumberOfElements(DEN(S));
nContStates = nCoeffs - 1;
ssSetNumContStates(S, nContStates);
ssSetNumDiscStates(S, 0);
if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S, 0, 1);
ssSetOutputPortSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetOutputPortOffsetTime(S, 0, 0);
ssSetNumSampleTimes(S, 2);
ssSetNumRWork(S, 4 * nCoeffs);
ssSetNumIWork(S, 2);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
} /* end mdlInitializeSizes */
The required S-function method mdlInitializeSampleTimes specifies the S-function's sample rates. The first call to
ssSetSampleTime specifies that the first sample rate is continuous and the subsequent call to ssSetOffsetTime sets the
offset to zero. The second call to this pair of macros sets the second sample time to the value of the third S-function
parameter with an offset of zero. The call to ssSetModelReferenceSampleTimeDefaultInheritance tells the solver to
use the default rule to determine if submodels containing this S-function can inherit their sample times from the parent model.
30 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
/*
* the second, discrete sample time, is user provided
*/
ssSetSampleTime(S, 1, mxGetPr(TS(S))[0]);
ssSetOffsetTime(S, 1, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
} /* end mdlInitializeSampleTimes */
The optional S-function method mdlInitializeConditions initializes the continuous state vector and the initial numerator
and denominator vectors. The #define statement before this method is required for the Simulink engine to call this function.
The function initializes the continuous states to zero. The numerator and denominator coefficients are initialized from the first
two S-function parameters, normalized by the first denominator coefficient. The function sets the value stored in the IWork
vector to zero, to indicate that the first bank of numerator and denominator coefficients stored in the RWork vector is currently
in use.
#define MDL_INITIALIZE_CONDITIONS
/* Function: mdlInitializeConditions ==========================================
* Abstract:
* Initalize the states, numerator and denominator coefficients.
*/
static void mdlInitializeConditions(SimStruct *S)
{
int_T i;
int_T nContStates = ssGetNumContStates(S);
real_T *x0 = ssGetContStates(S);
int_T nCoeffs = nContStates + 1;
real_T *numBank0 = ssGetRWork(S);
real_T *denBank0 = numBank0 + nCoeffs;
int_T *activeBank = ssGetIWork(S);
/*
* The continuous states are all initialized to zero.
*/
for (i = 0; i < nContStates; i++) {
x0[i] = 0.0;
numBank0[i] = 0.0;
denBank0[i] = 0.0;
}
numBank0[nContStates] = 0.0;
denBank0[nContStates] = 0.0;
/*
* Set up the initial numerator and denominator.
*/
{
const real_T *numParam = mxGetPr(NUM(S));
int numParamLen = mxGetNumberOfElements(NUM(S));
/*
* Normalize if this transfer function has direct feedthrough.
31 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
*/
for (i = 1; i < nCoeffs; i++) {
numBank0[i] -= denBank0[i]*numBank0[0];
}
/*
* Indicate bank0 is active (i.e. bank1 is oldest).
*/
*activeBank = 0;
} /* end mdlInitializeConditions */
The mdlOutputs function calculates the S-function output signals when the S-function is simulating in a continuous task, i.e.,
ssIsContinuousTask is true. If the simulation is also at a major time step, mdlOutputs checks if the numerator and
denominator coefficients need to be updated, as indicated by a switch in the active bank stored in the IWork vector. At both
major and minor time steps, the S-function calculates the output using the numerator coefficients stored in the active bank.
/*
* Switch banks because we've updated them in mdlUpdate and we're no
* longer in a minor time step.
*/
if (ssIsMajorTimeStep(S)) {
int_T *banksUpdated = ssGetIWork(S) + 1;
if (*banksUpdated) {
*activeBank = !(*activeBank);
*banksUpdated = 0;
/*
* Need to tell the solvers that the derivatives are no
* longer valid.
*/
ssSetSolverNeedsReset(S);
}
}
num = ssGetRWork(S) + (*activeBank) * (2*nCoeffs);
/*
* The continuous system is evaluated using a controllable state space
* representation of the transfer function. This implies that the
* output of the system is equal to:
*
* y(t) = Cx(t) + Du(t)
* = [ b1 b2 ... bn]x(t) + b0u(t)
*
* where b0, b1, b2, ... are the coefficients of the numerator
* polynomial:
*
* B(s) = b0 s^n + b1 s^n-1 + b2 s^n-2 + ... + bn-1 s + bn
*/
*y = *num++ * (*uPtrs[0]);
for (i = 0; i < nContStates; i++) {
*y += *num++ * *x++;
}
}
} /* end mdlOutputs */
Although this example has no discrete states, the method still implements the mdlUpdate function to update the transfer
function coefficients at every major time step. Because this method is optional, a #define statement precedes it. The method
uses ssGetInputPortRealSignalPtrs to obtain a pointer to the input signal. The input signal's values become the new
transfer function coefficients, which the S-function stores in the bank of the inactive RWork vector. When the mdlOutputs
function is later called at this major time step, it updates the active bank to be this updated bank of coefficients.
32 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
#define MDL_UPDATE
/* Function: mdlUpdate ========================================================
* Abstract:
* Every time through the simulation loop, update the
* transfer function coefficients. Here we update the oldest bank.
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
if (ssIsSampleHit(S, 1, tid)) {
int_T i;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
int_T uIdx = 1;/*1st coeff is after signal input*/
int_T nContStates = ssGetNumContStates(S);
int_T nCoeffs = nContStates + 1;
int_T bankToUpdate = !ssGetIWork(S)[0];
real_T *num = ssGetRWork(S)+bankToUpdate*2*nCoeffs;
real_T *den = num + nCoeffs;
real_T den0;
int_T allZero;
/*
* Get the first denominator coefficient. It will be used
* for normalizing the numerator and denominator coefficients.
*
* If all inputs are zero, we probably could have unconnected
* inputs, so use the parameter as the first denominator coefficient.
*/
den0 = *uPtrs[uIdx+nCoeffs];
if (den0 == 0.0) {
den0 = mxGetPr(DEN(S))[0];
}
/*
* Grab the numerator.
*/
allZero = 1;
for (i = 0; (i < nCoeffs) && allZero; i++) {
allZero &= *uPtrs[uIdx+i] == 0.0;
}
/*
* Grab the denominator.
*/
allZero = 1;
for (i = 0; (i < nCoeffs) && allZero; i++) {
allZero &= *uPtrs[uIdx+i] == 0.0;
}
den0 = denParam[0];
for (i = 0; i < denParamLen; i++) {
*den++ = *denParam++ / den0;
}
} else {
for (i = 0; i < nCoeffs; i++) {
*den++ = *uPtrs[uIdx++] / den0;
}
33 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
/*
* Normalize if this transfer function has direct feedthrough.
*/
num = ssGetRWork(S) + bankToUpdate*2*nCoeffs;
den = num + nCoeffs;
for (i = 1; i < nCoeffs; i++) {
num[i] -= den[i]*num[0];
}
/*
* Indicate oldest bank has been updated.
*/
ssGetIWork(S)[1] = 1;
}
} /* end mdlUpdate */
The mdlDerivatives function calculates the continuous state derivatives. The function uses the coefficients from the active
bank to solve a controllable state-space representation of the transfer function.
#define MDL_DERIVATIVES
/* Function: mdlDerivatives ===================================================
* Abstract:
* The derivatives for this block are computed by using a controllable
* state-space representation of the transfer function.
*/
static void mdlDerivatives(SimStruct *S)
{
int_T i;
int_T nContStates = ssGetNumContStates(S);
real_T *x = ssGetContStates(S);
real_T *dx = ssGetdX(S);
int_T nCoeffs = nContStates + 1;
int_T activeBank = ssGetIWork(S)[0];
const real_T *num = ssGetRWork(S) + activeBank*(2*nCoeffs);
const real_T *den = num + nCoeffs;
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
/*
* The continuous system is evaluated using a controllable state-space
* representation of the transfer function. This implies that the
* next continuous states are computed using:
*
* dx = Ax(t) + Bu(t)
* = [-a1 -a2 ... -an] [x1(t)] + [u(t)]
* [ 1 0 ... 0] [x2(t)] + [0]
* [ 0 1 ... 0] [x3(t)] + [0]
* [ . . ... .] . + .
* [ . . ... .] . + .
* [ . . ... .] . + .
* [ 0 0 ... 1 0] [xn(t)] + [0]
*
* where a1, a2, ... are the coefficients of the numerator polynomial:
*
* A(s) = s^n + a1 s^n-1 + a2 s^n-2 + ... + an-1 s + an
*/
dx[0] = -den[1] * x[0] + *uPtrs[0];
for (i = 1; i < nContStates; i++) {
dx[i] = x[i-1];
dx[0] -= den[i+1] * x[i];
}
} /* end mdlDerivatives */
The required mdlTerminate function performs any actions, such as freeing memory, necessary at the end of the simulation.
In this example, the function is empty.
34 of 35 1/21/2010 7:41 PM
C MEX S-Function Examples :: Implementing Block Features (Simul... jar:file:///C:/Program%20Files/MATLAB/R2009a/help/toolbox/simuli...
The required S-function trailer includes the files necessary for simulation or code generation, as follows.
Note The mdlTerminate function uses the UNUSED_ARG macro to indicate that an input argument the callback
requires is not used. This optional macro is defined in matlabroot/simulink/include/simstruc_types.h. If
used, you must call this macro once for each input argument that a callback does not use.
Back to Top
Provide feedback about this page
Error Handling S-Function Callback Methods — Alphabetical List
35 of 35 1/21/2010 7:41 PM