Simulink S-Function
S-Function是system-function的缩写。S-Function就是用MATLAB所提供的模块不能完全满足用户,而提供给用户自己编写程序来满足自己要求模型的接口,这样提供了扩充和灵活性。
S-Function可以用M、C/C++、Ada、Fortran 语言以MEX(Matlab Executable,MATLAB可执行文件,在Windows系统中就是其为DLL)文件的形式编写。
通过S-Function,用户可以将自己的模块加入Simulink模型中。从而可以实现用户自定义的算法或者与硬件设备交互等。
本文将使用C Mex的S-Function进行Joystick的输入解析。
Windows平台的S-Function
Windows平台的S-Function在MathWorks官网上有几个版本:
1.Per Hillerborg (2022). sfun_joystick.zip (https://www.mathworks.com/matlabcentral/fileexchange/47141-sfun_joystick-zip), MATLAB Central File Exchange
2.Johannes Soikkeli (2022). S-Function for reading joystick values on Simulink (https://www.mathworks.com/matlabcentral/fileexchange/111265-s-function-for-reading-joystick-values-on-simulink), MATLAB Central File Exchange.
其中2参考了1,函数很简单,读者可参考链接。
本文要在这个S-Function上适配Linux平台,为什么呢?因为项目要在linux平台上用。
代码
有了Windows源码,Linux部分更简单了。
直接上代码吧,这里略去一部分。
#include "simstruc.h"
#include <stdio.h>
#ifdef _WIN32
#include <Windows.h>
#elif __linux__
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/joystick.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#endif
#ifdef _WIN32
JOYINFOEX joy; // Struct into which joystick state is returned.
MMRESULT rc; // Return code of function.
#e/lif __linux__
int joystick_fd;
//int *axis = NULL;
//char *button = NULL;
#endif
/* Error handling
* --------------
*
* You should use the following technique to report errors encountered within
* an S-function:
*
* ssSetErrorStatus(S,"Error encountered due to ...");
* return;
*
* Note that the 2nd argument to ssSetErrorStatus must be persistent memory.
* It cannot be a local variable. For example the following will cause
* unpredictable errors:
*
* mdlOutputs()
* {
* char msg[256]; {ILLEGAL: to fix use "static char msg[256];"}
* sprintf(msg,"Error due to %s", string);
* ssSetErrorStatus(S,msg);
* return;
* }
*
*/
/*====================*
* S-function methods *
*====================*/
/* Function: mdlInitializeSizes ===============================================
* Abstract:
* The sizes information is used by Simulink to determine the S-function
* block's characteristics (number of inputs, outputs, states, etc.).
*/
static void mdlInitializeSizes(SimStruct *S)
{
...
}
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* This function is used to specify the sample time(s) for your
* S-function. You must register the same number of sample times as
* specified in ssSetNumSampleTimes.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
...
}
#undef MDL_INITIALIZE_CONDITIONS /* Change to #undef to remove function */
#if defined(MDL_INITIALIZE_CONDITIONS)
/* Function: mdlInitializeConditions ========================================
* Abstract:
* In this function, you should initialize the continuous and discrete
* states for your S-function block. The initial states are placed
* in the state vector, ssGetContStates(S) or ssGetRealDiscStates(S).
* You can also perform any other initialization activities that your
* S-function may require. Note, this routine will be called at the
* start of simulation and if it is present in an enabled subsystem
* configured to reset states, it will be call when the enabled subsystem
* restarts execution to reset the states.
*/
static void mdlInitializeConditions(SimStruct *S)
{
}
#endif /* MDL_INITIALIZE_CONDITIONS */
#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)
{
#ifdef _WIN32
joy.dwSize = sizeof(joy);
joy.dwFlags = JOY_RETURNALL;
#elif __linux__
{
const real_T *u = ssGetInputPortRealSignal(S,0);
int stick_id = (int)*u;
char joystick_name[20] = "/dev/input/js";
char id_char[5];
sprintf(id_char,"%d",stick_id +1);
strcat(joystick_name,id_char);
joystick_fd = open(joystick_name,O_RDONLY);
if(joystick_fd==-1)
{
ssWarning(S,"Joystick failed!");
return;
}
fcntl(joystick_fd,F_SETFL,O_NONBLOCK);
// int Nm_axis = 0, Nm_btn = 0;
// ioctl(joystick_fd,JSIOCGAXES,&Nm_axis);
// ioctl(joystick_fd,JSIOCGBUTTONS,&Nm_btn);
//
// axis = (int *)calloc(Nm_axis,sizeof(int));
// button = (char *)calloc(Nm_btn,sizeof(char));
}
#endif
}
#endif /* MDL_START */
/* Function: mdlOutputs =======================================================
* Abstract:
* In this function, you compute the outputs of your S-function
* block.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
}
#define MDL_UPDATE /* Change to #undef to remove function */
#if defined(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)
{
const real_T *u = ssGetInputPortRealSignal(S,0);
real_T *y = ssGetOutputPortSignal(S,0);
UNUSED_ARG(tid); /* not used in single tasking mode */
#ifdef _WIN32
...
#elif __linux__
if(joystick_fd!=-1)
{
struct js_event js;
while(read(joystick_fd,&js,sizeof(struct js_event))>0)
// if(js.type&JS_EVENT_INIT == JS_EVENT_INIT)
switch(js.type& ~JS_EVENT_INIT)
{
case JS_EVENT_AXIS:
// axis[js.number] = js.value;
if(js.number<4)
y[js.number] = js.value;
break;
case JS_EVENT_BUTTON:
// button[js.number] = js.value;
if(js.number<12)
y[js.number+4] = js.value;
break;
default:
break;
}
}
#endif
}
#endif /* MDL_UPDATE */
#undef MDL_DERIVATIVES /* Change to #undef to remove function */
#if defined(MDL_DERIVATIVES)
/* Function: mdlDerivatives =================================================
* Abstract:
* In this function, you compute the S-function block's derivatives.
* The derivatives are placed in the derivative vector, ssGetdX(S).
*/
static void mdlDerivatives(SimStruct *S)
{
}
#endif /* MDL_DERIVATIVES */
/* Function: mdlTerminate =====================================================
* Abstract:
* In this function, you should perform any actions that are necessary
* at the termination of a simulation. For example, if memory was
* allocated in mdlStart, this is the place to free it.
*/
static void mdlTerminate(SimStruct *S)
{
#ifdef __linux__
// free(axis);
// free(button);
close(joystick_fd);
#endif
}
/*=============================*
* Required S-function trailer *
*=============================*/
#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
引用
感谢二位给的源码。
[1]:Per Hillerborg (2022). sfun_joystick.zip (https://www.mathworks.com/matlabcentral/fileexchange/47141-sfun_joystick-zip), MATLAB Central File Exchange
[2]: Johannes Soikkeli (2022). S-Function for reading joystick values on Simulink (https://www.mathworks.com/matlabcentral/fileexchange/111265-s-function-for-reading-joystick-values-on-simulink), MATLAB Central File Exchange.