I'm not an expert on calling conventions so don't ask me why I used
the ones I did but these have worked for me in the past.
In the DLL, export your functions using:
extern "C" __declspec(dllexport)
You can get rid of the extern "C" part if the DLL is C and not C++.
In the S-Function declare your function pointers as follows:
typedef type (__cdecl *name) (args);
Since you say you're new to S-Functions there are some other things
you should know about them. They are simply DLLs with fancy extensions
and a fixed format in which to write them. Every instance of your S-
Function in the Simulink model calls the same DLL with a different set
of parameters, all of which are cached using the SimStruct * which you
will see is passed to every routine in an S-Function. So any global or
static variables are common to all instances.
A quick google search seems to indicate that multiple LoadLibrary()
calls from a process to a DLL always returns the same pointer as the
first call to LoadLibrary(). What this means is that multiple
instances of your S-Function block CANNOT load separate instances of
the DLL which you want to link to. Since they're all using the same
instance you have to be careful not to unload the DLL (using
FreeLibrary()) until all instances of your S-Function are done using
it. The way I implemented this last part was to create a simple
reference counting mechanism. Use the following steps:
1) Declare a global HMODULE variable which will receive the LoadLibrary
() return value and initialize to NULL. Also declare the function
pointers to the DLLs exported functions as global variables.
2) Declare a global counter variable which will count the number of S-
Function instance.
3) In mdlInitializeSizes() make sure you add the
SS_OPTION_CALL_TERMINATE_ON_EXIT flag when calling ssSetOptions().
This will force Simulink to always call mdlTerminate() when an S-
Function instance exits.
4) In mdlStart() check the HMODULE variable, if it is NULL then call
LoadLibrary(), use GetProcAddress() to initialize the function
pointers and increment the counter. If it is not NULL simply increment
the counter.
5) in mdlTerminate() decrement the counter, if non-zero do nothing
else. If zero, call FreeLibrary() and reset the HMODULE variable to
NULL.
HTH,
Ashish.
Thank you for your reply.
If its ok with you, can you send me the code you wrote?
i just want to take a look at it.
Unfortunately that S-Function is rather complicated and gets linked
into 2 custom libraries in addition to the DLL I mentioned. So I would
have to send you everything in order for you to be able to make sense
of it and that is not an option because we have code in it that cannot
be disclosed. Read through the Simulink documentation on S-Functions;
they explain how exactly Simulink interacts with them. Go through the
API function documentation. Also, if you're new to DLLs in general a
google will return lots of information on them. But I should warn you,
if you're new to DLL programming as well as S-Functions you do have a
steep learning curve ahead of you. Of course, you could always post
code snippets and additional questions and I'll be happy to help you
out.
Ashish.
#include "simstruc.h"
/*================*
* Build checking *
*================*/
/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Setup sizes of the various vectors.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0);
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch will be reported by Simulink */
}
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetInputPortDirectFeedThrough(S, 0, 1);
if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetNumSampleTimes(S, 1);
/* Take care when specifying exception free code - see sfuntmpl_doc.c */
ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_USE_TLC_WITH_ACCELERATOR);
}
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Specifiy that we inherit our sample time from the driving block.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
/* Function: mdlOutputs =======================================================
* Abstract:
* y = 2*u
*/
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);
for (i=0; i<width; i++) {
/*
* This example does not implement complex signal handling.
* To find out see an example about how to handle complex signal in
* S-function, see sdotproduct.c for details.
*/
*y++ = 2.0 *(*uPtrs[i]);
}
}
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
}
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif
what i want to do is that in mdlOutPuts, i would like to use a function defined in my dll.
what other codes do i have to add?
i have used the following codes to call the dll.
HINSTANCE hDll = LoadLibrary("mmcdlltest.dll");
typedef double (*mmcdlltest)(double num);
mmcdlltest lpmmcdlltest;
lpmmcdlltest=(mmcdlltest)GetProcAddress(hDll,"mmcdlltest");
FreeLibrary(hDll);
i even have no idea where this code should be put.
Please help me.
Thank you
You could add the DLL access code to mdlOutputs, load the DLL, use the
function and unload it each time. While that would work just fine it
is horribly inefficient to do that. Instead declare hDll and
lpmmcdlltest as global variables in your timestwo.c file and
initialize both to NULL. You will need to add a mdlStart() routine to
your S-Function, you should be able to find examples for that in the
same folder as timestwo.c. Inside mdlStart() use the LoadLibrary() and
GetProcAddress() calls to initialize the global variables. Use the
function pointer within mdlOutputs(). Call FreeLibrary() in the
mdlTerminate() routine and reset both globals to NULL after doing
that. To make sure Simulink always calls mdlTerminate() add the flag
SS_OPTION_CALL_TERMINATE_ON_EXIT to ssSetOptions() in
mdlInitializeSizes().
Try to get this to work first; if your model is going to contain only
one instance of this S-Function then this code will work without any
problems. If you add more than one instance of your S-Function block
then you might have problems depending on how the blocks are used and
the model optimizations you enable. But we can discuss that once you
have the first part implemented.
HTH,
Ashish.
- mmcdlltest is an undelcared identifier.
what should i do for this error?
It means the compiler does not know what mmcdlltest is; you're either
missing the declaration, or its within the wrong scope, or it appears
after you've used mmcdlltest.
i am still not clear how i can handle the error, so i'll attach the source.
/*
* File : timestwo.c
* Abstract:
* An example C-file S-function for multiplying an input by 2,
* y = 2*u
*
* Real-Time Workshop note:
* This file can be used as is (noninlined) with the Real-Time Workshop
* C rapid prototyping targets, or it can be inlined using the Target
* Language Compiler technology and used with any target. See
* matlabroot/toolbox/simulink/blocks/tlc_c/timestwo.tlc
* matlabroot/toolbox/simulink/blocks/tlc_ada/timestwo.tlc
* the C and Ada TLC code to inline the S-function.
*
* See simulink/src/sfuntmpl_doc.c
*
* Copyright 1990-2004 The MathWorks, Inc.
* $Revision: 1.12.4.3 $
*/
#define S_FUNCTION_NAME mmctesttimestwo
#define S_FUNCTION_LEVEL 2
#include "windows.h"
#include "windef.h"
#include "winuser.h"
#include "winbase.h"
#include "simstruc.h"
#include "mex.h"
#ifdef __cplusplus
extern "C" { // use the C fcn-call standard for all functions
#endif // defined within this scope
HINSTANCE hDll = NULL;
//mmcdlltest lpmmcdlltest;
lpmmcdlltest = NULL;
/*================*
* Build checking *
*================*/
/* Function: mdlInitializeSizes ===============================================
* Abstract:
* Setup sizes of the various vectors.
*/
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0);
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
return; /* Parameter mismatch will be reported by Simulink */
}
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetInputPortDirectFeedThrough(S, 0, 1);
if (!ssSetNumOutputPorts(S,1)) return;
ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);
ssSetNumSampleTimes(S, 1);
/* Take care when specifying exception free code - see sfuntmpl_doc.c */
ssSetOptions(S,
SS_OPTION_WORKS_WITH_CODE_REUSE |
SS_OPTION_EXCEPTION_FREE_CODE |
SS_OPTION_USE_TLC_WITH_ACCELERATOR |
SS_OPTION_CALL_TERMINATE_ON_EXIT);
}
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* Specifiy that we inherit our sample time from the driving block.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
#define MDL_START /* Change to #undef to remove function */
#if defined(MDL_START)
/* Function: mdlStart =======================================================
* Abstract:
* This function is called once at start of model execution. If you
* have states that should be initialized once, this is the place
* to do it.
*/
static void mdlStart(SimStruct *S)
{
HINSTANCE hDll = LoadLibrary("mmcdlltest.dll");
// mmcdlltest lpmmcdlltest;
lpmmcdlltest=(mmcdlltest)GetProcAddress(hDll,"mmcdlltest");
}
#endif /* MDL_START */
/* Function: mdlOutputs =======================================================
* Abstract:
* y = 2*u
*/
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);
typedef double (*mmcdlltest)(double num);
for (i=0; i<width; i++) {
/*
* This example does not implement complex signal handling.
* To find out see an example about how to handle complex signal in
* S-function, see sdotproduct.c for details.
*/
// *y++ = 2.0 *(*uPtrs[i]);
*y = mmcdlltest(*uPtrs[i]);
}
}
/* Function: mdlTerminate =====================================================
* Abstract:
* No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
FreeLibrary(hDll);
HINSTANCE hDll = NULL;
lpmmcdlltest = NULL;
}
#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif
#ifdef __cplusplus
} // end of extern "C" scope
#endif
the problem is 'lpmmcdlltest=(mmcdlltest)GetProcAddress(hDll,"mmcdlltest");' in mdlStart function shows error, which is mmcdlltest is undeclared identifier. i don't know how to handle this. also, "missing ';' before identifier GetProcAddress" occurs. how can i handle this error?
one more thing, HINSTANCE in mdlTerminate shows an error saying "illegal use of this type as an expression"
mmcdlltest is a function pointer data type and lpmmcdlltest is a
function pointer variable. When you call a function using a function
pointer you cannot do that using the type itself, you need to use the
variable. Why have you commented out the line where you declare
lpmmcdlltest as a variable of type mmcdlltest?
The way you're using the function pointer to make the function call is
similar to writing
typedef int myDType;
myDType = 5;
instead of
typedef int myDType;
myDType i = 5;
You need to read some C programming tutorials on the use of function
pointers, google it, there are lots of them out there.