diff --git a/Makefile b/Makefile index 1b8a0692becacbcd7cf856764dddc63e31d5e54f..32b5f3c28b58a6b288d61dfa309e13051fb2d3b7 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,12 @@ LIBRARIES=libmoberg.so CCFLAGS+=-Wall -Werror -I$(shell pwd) -g LDFLAGS+=-L$(shell pwd)/build/ -lmoberg PLUGINS:=$(wildcard plugins/*) +ADAPTORS:=$(wildcard adaptors/*) export CCFLAGS LDFLAGS LDFLAGS_parse_config=-ldl #-export-dynamic -all: $(LIBRARIES:%=build/%) $(PLUGINS) +all: $(LIBRARIES:%=build/%) $(PLUGINS) $(ADAPTORS) echo $(PLUGINS) echo $(CCFLAGS) @@ -27,8 +28,8 @@ build/lib/%.o: %.c Makefile | build/lib $(CC) $(CCFLAGS) -c -fPIC -o $@ $< -.PHONY: $(PLUGINS) -$(PLUGINS): +.PHONY: $(PLUGINS) $(ADAPTORS) +$(ADAPTORS) $(PLUGINS): $(MAKE) -C $@ @@ -37,7 +38,7 @@ test: all $(MAKE) -C test test clean: - find build -type f -delete + rm -f build/*.so build/*.mex* rm -f *~ make -C test clean diff --git a/adaptors/matlab/Makefile b/adaptors/matlab/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..c4e6c3d8405fdabe5a92b8e56d8f676aa5aadf56 --- /dev/null +++ b/adaptors/matlab/Makefile @@ -0,0 +1,20 @@ +LIBRARIES=libmoberg4simulink.so +SFUNC=analogin analogout digitalin digitalout encoderin +MATLAB_VERSION= +MEX=MATLAB_VERSION=$(MATLAB_VERSION) mex +MEX_SUFFIX=$(shell $(MEX) -v -n analogin.c \ + | sed -e 's/^.*LDEXTENSION.*[.]\(mex.*\)/\1/p;d') + +CCFLAGS+=-Wall -Werror -I. -I../.. -g + +all: $(LIBRARIES:%=../../build/%) $(SFUNC:%=../../build/%.$(MEX_SUFFIX)) + echo $(SUFFIX) + +../../build/libmoberg4simulink.so: moberg4simulink.c Makefile + $(CC) -o $@ $(CCFLAGS) -L../../build -shared -fPIC -lmoberg $< + +../../build/libmoberg4simulink.so: ../../moberg.h + +../../build/%.$(MEX_SUFFIX): %.c Makefile + $(MEX) CFLAGS="$(CCFLAGS) -fPIC" \ + -outdir ../../build -L../../build -lmoberg4simulink $< diff --git a/adaptors/matlab/analogin.c b/adaptors/matlab/analogin.c new file mode 100644 index 0000000000000000000000000000000000000000..6c8d6c50e87716cccf659c09a012fbdad6535abe --- /dev/null +++ b/adaptors/matlab/analogin.c @@ -0,0 +1,170 @@ +/* + analogin.c, + a MEX file for reading analog input via Moberg + + Copyright (C) 2019 Anders Blomdell <anders.blomdell@control.lth.se> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#define S_FUNCTION_LEVEL 2 +#define S_FUNCTION_NAME analogin + +#include <uchar.h> /* for CHAR16_T typedef in tmwtypes.h */ +#include "simstruc.h" +#include <moberg4simulink.h> + +/* + Usage of work vectors: + + PWork: 0 moberg_analog_in pointer[0] + 1 moberg_analog_in pointer[1] + ... + + */ + +#define MDL_CHECK_PARAMETERS +static void mdlCheckParameters(SimStruct *S) +{ + /* 1st parameter: sampling interval */ + { + if (!mxIsDouble(ssGetSFcnParam(S,0)) || + mxGetNumberOfElements(ssGetSFcnParam(S,0)) != 1) { + ssSetErrorStatus(S, "sampling time must be a scalar"); + return; + } + } + + /* 2nd parameter: input channels */ + { + int number_of_dims = mxGetNumberOfDimensions(ssGetSFcnParam(S,1)); + + if (!mxIsDouble(ssGetSFcnParam(S,1)) || + number_of_dims != 2 || mxGetM(ssGetSFcnParam(S,1)) != 1) { + ssSetErrorStatus(S, "input channels must be a scalar or a vector"); + return; + } + } +} + +static void mdlInitializeSizes(SimStruct *S) +{ + int channelCount; + + ssSetNumSFcnParams(S, 2); + if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) { + mdlCheckParameters(S); + if (ssGetErrorStatus(S) != NULL) { return; } + } else { + return; + } + + channelCount = mxGetN(ssGetSFcnParam(S,1)); + + ssSetNumContStates(S, 0); + ssSetNumDiscStates(S, 0); + + if (!ssSetNumInputPorts(S, 1)) { return; } + ssSetInputPortWidth(S, 0, 1); + ssSetInputPortDirectFeedThrough(S, 0, 1); + + if (!ssSetNumOutputPorts(S, 2)) { return; } + ssSetOutputPortWidth(S, 0, 1); + ssSetOutputPortWidth(S, 1, channelCount); + + ssSetNumSampleTimes(S, 1); + ssSetNumPWork(S, channelCount); /* 0: moberg_analog_in pointer[0] + 1: moberg_analog_in pointer[1] + ... + */ + ssSetNumModes(S, 0); + ssSetNumNonsampledZCs(S, 0); + + ssSetOptions(S, 0); +} + +static void mdlInitializeSampleTimes(SimStruct *S) +{ + ssSetSampleTime(S, 0, mxGetScalar(ssGetSFcnParam(S, 0))); + ssSetOffsetTime(S, 0, 0.0); +} + +#define MDL_INITIALIZE_CONDITIONS +static void mdlInitializeConditions(SimStruct *S) +{ + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + int i; + + for (i = 0 ; i < channel_count ; i++) { + pwork[i] = moberg4simulink_analog_in_open(channel[i]); + if (! pwork[i]) { + static char error[256]; + sprintf(error, "Failed to open analogin #%d", (int)channel[i]); + ssSetErrorStatus(S, error); + } + } +} + +static void mdlOutputs(SimStruct *S, int_T tid) +{ + void **pwork = ssGetPWork(S); + + { + /* Propagate the dummy sorting signal */ + InputRealPtrsType up = ssGetInputPortRealSignalPtrs(S,0); + real_T *y = ssGetOutputPortRealSignal(S, 0); + y[0] = *up[0]+1; + } + { + int i; + real_T *y = ssGetOutputPortRealSignal(S, 1); + + for (i = 0 ; i < ssGetNumPWork(S) ; i++) { + struct moberg_analog_in *ain = (struct moberg_analog_in*)pwork[i]; + ain->read(ain->context, &y[i]); + } + } +} + +static void mdlTerminate(SimStruct *S) +{ + int i; + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + + for (i = 0 ; i < channel_count ; i++) { + if (pwork[i]) { + struct moberg_analog_in *ain = (struct moberg_analog_in*)pwork[i]; + moberg4simulink_analog_in_close(channel[i], ain); + } + } +} + +/*======================================================* + * See sfuntmpl.doc for the optional S-function methods * + *======================================================*/ + +/*=============================* + * 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 diff --git a/adaptors/matlab/analogout.c b/adaptors/matlab/analogout.c new file mode 100644 index 0000000000000000000000000000000000000000..ba1b0918f1a1c076142774593ae761072934938c --- /dev/null +++ b/adaptors/matlab/analogout.c @@ -0,0 +1,171 @@ +/* + analogout.c, + a MEX file for writing analog output via Moberg. + + Copyright (C) 2019 Anders Blomdell <anders.blomdell@control.lth.se> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#define S_FUNCTION_LEVEL 2 +#define S_FUNCTION_NAME analogout + +#include <uchar.h> /* for CHAR16_T typedef in tmwtypes.h */ +#include "simstruc.h" +#include <moberg4simulink.h> + +/* + Usage of work vectors: + + PWork: 0 moberg_analog_out pointer[0] + 1 moberg_analog_out pointer[1] + ... + + */ + +#define MDL_CHECK_PARAMETERS +static void mdlCheckParameters(SimStruct *S) +{ + /* 1st parameter: sampling interval */ + { + if (!mxIsDouble(ssGetSFcnParam(S,0)) || + mxGetNumberOfElements(ssGetSFcnParam(S,0)) != 1) { + ssSetErrorStatus(S, "sampling time must be a scalar"); + return; + } + } + + /* 2nd parameter: input channels */ + { + int number_of_dims = mxGetNumberOfDimensions(ssGetSFcnParam(S,1)); + + if (!mxIsDouble(ssGetSFcnParam(S,1)) || + number_of_dims != 2 || mxGetM(ssGetSFcnParam(S,1)) != 1) { + ssSetErrorStatus(S, "output channels must be a scalar or a vector"); + return; + } + } +} + +static void mdlInitializeSizes(SimStruct *S) +{ + int channelCount; + + ssSetNumSFcnParams(S, 2); + if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) { + mdlCheckParameters(S); + if (ssGetErrorStatus(S) != NULL) { return; } + } else { + return; + } + + channelCount = mxGetN(ssGetSFcnParam(S,1)); + + ssSetNumContStates(S, 0); + ssSetNumDiscStates(S, 0); + + if (!ssSetNumInputPorts(S, 2)) return; + ssSetInputPortWidth(S, 0, 1); + ssSetInputPortDirectFeedThrough(S, 0, 1); + ssSetInputPortWidth(S, 1, channelCount); + ssSetInputPortDirectFeedThrough(S, 1, 1); + + if (!ssSetNumOutputPorts(S, 1)) return; + ssSetOutputPortWidth(S, 0, 1); + + ssSetNumSampleTimes(S, 1); + ssSetNumPWork(S, channelCount); /* 0: moberg_analog_out pointer[0] + 1: moberg_analog_out pointer[1] + ... + */ + ssSetNumModes(S, 0); + ssSetNumNonsampledZCs(S, 0); + + ssSetOptions(S, 0); +} + +static void mdlInitializeSampleTimes(SimStruct *S) +{ + ssSetSampleTime(S, 0, mxGetScalar(ssGetSFcnParam(S, 0))); + ssSetOffsetTime(S, 0, 0.0); +} + +#define MDL_INITIALIZE_CONDITIONS +static void mdlInitializeConditions(SimStruct *S) +{ + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + int i; + + for (i = 0 ; i < channel_count ; i++) { + pwork[i] = moberg4simulink_analog_out_open(channel[i]); + if (! pwork[i]) { + static char error[256]; + sprintf(error, "Failed to open analogout #%d", (int)channel[i]); + ssSetErrorStatus(S, error); + } + } +} + +static void mdlOutputs(SimStruct *S, int_T tid) +{ + void **pwork = ssGetPWork(S); + + { + /* Propagate the dummy sorting signal */ + InputRealPtrsType up = ssGetInputPortRealSignalPtrs(S,0); + real_T *y = ssGetOutputPortRealSignal(S, 0); + y[0] = *up[0]+1; + } + { + InputRealPtrsType up = ssGetInputPortRealSignalPtrs(S,1); + int i; + + for (i = 0 ; i < ssGetNumPWork(S) ; i++) { + struct moberg_analog_out *aout = (struct moberg_analog_out*)pwork[i]; + aout->write(aout->context, *up[i]); + } + } +} + +static void mdlTerminate(SimStruct *S) +{ + int i; + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + + for (i = 0 ; i < channel_count ; i++) { + if (pwork[i]) { + struct moberg_analog_out *aout = (struct moberg_analog_out*)pwork[i]; + moberg4simulink_analog_out_close(channel[i], aout); + } + } +} + +/*======================================================* + * See sfuntmpl.doc for the optional S-function methods * + *======================================================*/ + +/*=============================* + * 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 diff --git a/adaptors/matlab/digitalin.c b/adaptors/matlab/digitalin.c new file mode 100644 index 0000000000000000000000000000000000000000..55674db261ded49d9ae3ee7190139a071209b4ee --- /dev/null +++ b/adaptors/matlab/digitalin.c @@ -0,0 +1,173 @@ +/* + digitalin.c, + a MEX file for reading digital input via Moberg + + Copyright (C) 2019 Anders Blomdell <anders.blomdell@control.lth.se> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#define S_FUNCTION_LEVEL 2 +#define S_FUNCTION_NAME digitalin + +#include <uchar.h> /* for CHAR16_T typedef in tmwtypes.h */ +#include "simstruc.h" +#include <moberg4simulink.h> + +/* + Usage of work vectors: + + PWork: 0 moberg_digital_in pointer[0] + 1 moberg_digital_in pointer[1] + ... + + + */ + +#define MDL_CHECK_PARAMETERS +static void mdlCheckParameters(SimStruct *S) +{ + /* 1st parameter: sampling interval */ + { + if (!mxIsDouble(ssGetSFcnParam(S,0)) || + mxGetNumberOfElements(ssGetSFcnParam(S,0)) != 1) { + ssSetErrorStatus(S, "sampling time must be a scalar"); + return; + } + } + + /* 2nd parameter: input channels */ + { + int number_of_dims = mxGetNumberOfDimensions(ssGetSFcnParam(S,1)); + + if (!mxIsDouble(ssGetSFcnParam(S,1)) || + number_of_dims != 2 || mxGetM(ssGetSFcnParam(S,1)) != 1) { + ssSetErrorStatus(S, "input channels must be a scalar or a vector"); + return; + } + } +} + +static void mdlInitializeSizes(SimStruct *S) +{ + int channelCount; + + ssSetNumSFcnParams(S, 2); + if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) { + mdlCheckParameters(S); + if (ssGetErrorStatus(S) != NULL) { return; } + } else { + return; + } + + channelCount = mxGetN(ssGetSFcnParam(S,1)); + + ssSetNumContStates(S, 0); + ssSetNumDiscStates(S, 0); + + if (!ssSetNumInputPorts(S, 1)) { return; } + ssSetInputPortWidth(S, 0, 1); + ssSetInputPortDirectFeedThrough(S, 0, 1); + + if (!ssSetNumOutputPorts(S, 2)) { return; } + ssSetOutputPortWidth(S, 0, 1); + ssSetOutputPortWidth(S, 1, channelCount); + + ssSetNumSampleTimes(S, 1); + ssSetNumPWork(S, channelCount); /* 0: moberg_digital_in pointer[0] + 1: moberg_digital_in pointer[1] + ... + */ + ssSetNumModes(S, 0); + ssSetNumNonsampledZCs(S, 0); + + ssSetOptions(S, 0); +} + +static void mdlInitializeSampleTimes(SimStruct *S) +{ + ssSetSampleTime(S, 0, mxGetScalar(ssGetSFcnParam(S, 0))); + ssSetOffsetTime(S, 0, 0.0); +} + +#define MDL_INITIALIZE_CONDITIONS +static void mdlInitializeConditions(SimStruct *S) +{ + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + int i; + + for (i = 0 ; i < channel_count ; i++) { + pwork[i] = moberg4simulink_digital_in_open(channel[i]); + if (! pwork[i]) { + static char error[256]; + sprintf(error, "Failed to open digitalin #%d", (int)channel[i]); + ssSetErrorStatus(S, error); + } + } +} + +static void mdlOutputs(SimStruct *S, int_T tid) +{ + void **pwork = ssGetPWork(S); + + { + /* Propagate the dummy sorting signal */ + InputRealPtrsType up = ssGetInputPortRealSignalPtrs(S,0); + real_T *y = ssGetOutputPortRealSignal(S, 0); + y[0] = *up[0]+1; + } + { + int i; + real_T *y = ssGetOutputPortRealSignal(S, 1); + + for (i = 0 ; i < ssGetNumPWork(S) ; i++) { + struct moberg_digital_in *din = (struct moberg_digital_in*)pwork[i]; + int value; + din->read(din->context, &value); + y[i] = value; + } + } +} + +static void mdlTerminate(SimStruct *S) +{ + int i; + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + + for (i = 0 ; i < channel_count ; i++) { + if (pwork[i]) { + struct moberg_digital_in *ain = (struct moberg_digital_in*)pwork[i]; + moberg4simulink_digital_in_close(channel[i], ain); + } + } +} + +/*======================================================* + * See sfuntmpl.doc for the optional S-function methods * + *======================================================*/ + +/*=============================* + * 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 diff --git a/adaptors/matlab/digitalout.c b/adaptors/matlab/digitalout.c new file mode 100644 index 0000000000000000000000000000000000000000..801573bdf718413070f795dbaa3fdc4b26f5c666 --- /dev/null +++ b/adaptors/matlab/digitalout.c @@ -0,0 +1,171 @@ +/* + digitalout.c, + a MEX file for writing digital output via Moberg. + + Copyright (C) 2019 Anders Blomdell <anders.blomdell@control.lth.se> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#define S_FUNCTION_LEVEL 2 +#define S_FUNCTION_NAME digitalout + +#include <uchar.h> /* for CHAR16_T typedef in tmwtypes.h */ +#include "simstruc.h" +#include <moberg4simulink.h> + +/* + Usage of work vectors: + + PWork: 0 moberg_digital_out pointer[0] + 1 moberg_digital_out pointer[1] + ... + + */ + +#define MDL_CHECK_PARAMETERS +static void mdlCheckParameters(SimStruct *S) +{ + /* 1st parameter: sampling interval */ + { + if (!mxIsDouble(ssGetSFcnParam(S,0)) || + mxGetNumberOfElements(ssGetSFcnParam(S,0)) != 1) { + ssSetErrorStatus(S, "sampling time must be a scalar"); + return; + } + } + + /* 2nd parameter: input channels */ + { + int number_of_dims = mxGetNumberOfDimensions(ssGetSFcnParam(S,1)); + + if (!mxIsDouble(ssGetSFcnParam(S,1)) || + number_of_dims != 2 || mxGetM(ssGetSFcnParam(S,1)) != 1) { + ssSetErrorStatus(S, "output channels must be a scalar or a vector"); + return; + } + } +} + +static void mdlInitializeSizes(SimStruct *S) +{ + int channelCount; + + ssSetNumSFcnParams(S, 2); + if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) { + mdlCheckParameters(S); + if (ssGetErrorStatus(S) != NULL) { return; } + } else { + return; + } + + channelCount = mxGetN(ssGetSFcnParam(S,1)); + + ssSetNumContStates(S, 0); + ssSetNumDiscStates(S, 0); + + if (!ssSetNumInputPorts(S, 2)) return; + ssSetInputPortWidth(S, 0, 1); + ssSetInputPortDirectFeedThrough(S, 0, 1); + ssSetInputPortWidth(S, 1, channelCount); + ssSetInputPortDirectFeedThrough(S, 1, 1); + + if (!ssSetNumOutputPorts(S, 1)) return; + ssSetOutputPortWidth(S, 0, 1); + + ssSetNumSampleTimes(S, 1); + ssSetNumPWork(S, channelCount); /* 0: moberg_digital_out pointer[0] + 1: moberg_digital_out pointer[1] + ... + */ + ssSetNumModes(S, 0); + ssSetNumNonsampledZCs(S, 0); + + ssSetOptions(S, 0); +} + +static void mdlInitializeSampleTimes(SimStruct *S) +{ + ssSetSampleTime(S, 0, mxGetScalar(ssGetSFcnParam(S, 0))); + ssSetOffsetTime(S, 0, 0.0); +} + +#define MDL_INITIALIZE_CONDITIONS +static void mdlInitializeConditions(SimStruct *S) +{ + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + int i; + + for (i = 0 ; i < channel_count ; i++) { + pwork[i] = moberg4simulink_digital_out_open(channel[i]); + if (! pwork[i]) { + static char error[256]; + sprintf(error, "Failed to open digitalout #%d", (int)channel[i]); + ssSetErrorStatus(S, error); + } + } +} + +static void mdlOutputs(SimStruct *S, int_T tid) +{ + void **pwork = ssGetPWork(S); + + { + /* Propagate the dummy sorting signal */ + InputRealPtrsType up = ssGetInputPortRealSignalPtrs(S,0); + real_T *y = ssGetOutputPortRealSignal(S, 0); + y[0] = *up[0]+1; + } + { + InputRealPtrsType up = ssGetInputPortRealSignalPtrs(S,1); + int i; + + for (i = 0 ; i < ssGetNumPWork(S) ; i++) { + struct moberg_digital_out *dout = (struct moberg_digital_out*)pwork[i]; + dout->write(dout->context, *up[i]); + } + } +} + +static void mdlTerminate(SimStruct *S) +{ + int i; + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + + for (i = 0 ; i < channel_count ; i++) { + if (pwork[i]) { + struct moberg_digital_out *dout = (struct moberg_digital_out*)pwork[i]; + moberg4simulink_digital_out_close(channel[i], dout); + } + } +} + +/*======================================================* + * See sfuntmpl.doc for the optional S-function methods * + *======================================================*/ + +/*=============================* + * 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 diff --git a/adaptors/matlab/encoderin.c b/adaptors/matlab/encoderin.c new file mode 100644 index 0000000000000000000000000000000000000000..9a7be54b892b5a57559b78c09a7469962bdbc61f --- /dev/null +++ b/adaptors/matlab/encoderin.c @@ -0,0 +1,172 @@ +/* + encoderin.c, + a MEX file for reading encoder input via Moberg + + Copyright (C) 2019 Anders Blomdell <anders.blomdell@control.lth.se> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#define S_FUNCTION_LEVEL 2 +#define S_FUNCTION_NAME encoderin + +#include <uchar.h> /* for CHAR16_T typedef in tmwtypes.h */ +#include "simstruc.h" +#include <moberg4simulink.h> + +/* + Usage of work vectors: + + PWork: 0 moberg_encoder_in pointer[0] + 1 moberg_encoder_in pointer[1] + ... + + */ + +#define MDL_CHECK_PARAMETERS +static void mdlCheckParameters(SimStruct *S) +{ + /* 1st parameter: sampling interval */ + { + if (!mxIsDouble(ssGetSFcnParam(S,0)) || + mxGetNumberOfElements(ssGetSFcnParam(S,0)) != 1) { + ssSetErrorStatus(S, "sampling time must be a scalar"); + return; + } + } + + /* 2nd parameter: input channels */ + { + int number_of_dims = mxGetNumberOfDimensions(ssGetSFcnParam(S,1)); + + if (!mxIsDouble(ssGetSFcnParam(S,1)) || + number_of_dims != 2 || mxGetM(ssGetSFcnParam(S,1)) != 1) { + ssSetErrorStatus(S, "input channels must be a scalar or a vector"); + return; + } + } +} + +static void mdlInitializeSizes(SimStruct *S) +{ + int channelCount; + + ssSetNumSFcnParams(S, 2); + if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) { + mdlCheckParameters(S); + if (ssGetErrorStatus(S) != NULL) { return; } + } else { + return; + } + + channelCount = mxGetN(ssGetSFcnParam(S,1)); + + ssSetNumContStates(S, 0); + ssSetNumDiscStates(S, 0); + + if (!ssSetNumInputPorts(S, 1)) { return; } + ssSetInputPortWidth(S, 0, 1); + ssSetInputPortDirectFeedThrough(S, 0, 1); + + if (!ssSetNumOutputPorts(S, 2)) { return; } + ssSetOutputPortWidth(S, 0, 1); + ssSetOutputPortWidth(S, 1, channelCount); + + ssSetNumSampleTimes(S, 1); + ssSetNumPWork(S, channelCount); /* 0: moberg_encoder_in pointer[0] + 1: moberg_encoder_in pointer[1] + ... + */ + ssSetNumModes(S, 0); + ssSetNumNonsampledZCs(S, 0); + + ssSetOptions(S, 0); +} + +static void mdlInitializeSampleTimes(SimStruct *S) +{ + ssSetSampleTime(S, 0, mxGetScalar(ssGetSFcnParam(S, 0))); + ssSetOffsetTime(S, 0, 0.0); +} + +#define MDL_INITIALIZE_CONDITIONS +static void mdlInitializeConditions(SimStruct *S) +{ + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + int i; + + for (i = 0 ; i < channel_count ; i++) { + pwork[i] = moberg4simulink_encoder_in_open(channel[i]); + if (! pwork[i]) { + static char error[256]; + sprintf(error, "Failed to open encoderin #%d", (int)channel[i]); + ssSetErrorStatus(S, error); + } + } +} + +static void mdlOutputs(SimStruct *S, int_T tid) +{ + void **pwork = ssGetPWork(S); + + { + /* Propagate the dummy sorting signal */ + InputRealPtrsType up = ssGetInputPortRealSignalPtrs(S,0); + real_T *y = ssGetOutputPortRealSignal(S, 0); + y[0] = *up[0]+1; + } + { + int i; + real_T *y = ssGetOutputPortRealSignal(S, 1); + + for (i = 0 ; i < ssGetNumPWork(S) ; i++) { + struct moberg_encoder_in *ein = (struct moberg_encoder_in*)pwork[i]; + long value; + ein->read(ein->context, &value); + y[i] = value; + } + } +} + +static void mdlTerminate(SimStruct *S) +{ + int i; + void **pwork = ssGetPWork(S); + double *channel = mxGetPr(ssGetSFcnParam(S,1)); + int channel_count = mxGetN(ssGetSFcnParam(S,1)); + + for (i = 0 ; i < channel_count ; i++) { + if (pwork[i]) { + struct moberg_encoder_in *ein = (struct moberg_encoder_in*)pwork[i]; + moberg4simulink_encoder_in_close(channel[i], ein); + } + } +} + +/*======================================================* + * See sfuntmpl.doc for the optional S-function methods * + *======================================================*/ + +/*=============================* + * 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 diff --git a/adaptors/matlab/moberg4simulink.c b/adaptors/matlab/moberg4simulink.c new file mode 100644 index 0000000000000000000000000000000000000000..736edd0235574339026b47ba399be6b02e3f4910 --- /dev/null +++ b/adaptors/matlab/moberg4simulink.c @@ -0,0 +1,194 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <moberg.h> +#include <moberg4simulink.h> + +static struct channel { + struct channel *next; + struct channel *prev; + union { + struct moberg_analog_in analog_in; + struct moberg_analog_out analog_out; + struct moberg_digital_in digital_in; + struct moberg_digital_out digital_out; + struct moberg_encoder_in encoder_in; + }; +} analog_in_list={.next=&analog_in_list, + .prev=&analog_in_list}, + analog_out_list={.next=&analog_out_list, + .prev=&analog_out_list}, + digital_in_list={.next=&digital_in_list, + .prev=&digital_in_list}, + digital_out_list={.next=&digital_out_list, + .prev=&digital_out_list}, + encoder_in_list={.next=&encoder_in_list, + .prev=&encoder_in_list}; + +struct { + int count; + struct moberg *moberg; +} g_moberg = { 0, NULL }; + +static int up() +{ + if (g_moberg.count <= 0) { + g_moberg.moberg = moberg_new(NULL); + } + g_moberg.count++; + return 0; +} + +static int down() +{ + g_moberg.count--; + if (g_moberg.count <= 0) { + moberg_free(g_moberg.moberg); + g_moberg.moberg = NULL; + } + return 0; +} + +void list_insert(struct channel *list, + struct channel *element) +{ + element->next = list; + element->prev = list->prev; + element->prev->next = element; + element->next->prev = element; +} + +void list_remove(struct channel *element) +{ + element->prev->next = element->next; + element->next->prev = element->prev; +} + +struct moberg_analog_in *moberg4simulink_analog_in_open(int index) +{ + up(); + struct channel *result = malloc(sizeof(*result)); + if (result && moberg_analog_in_open(g_moberg.moberg, index, + &result->analog_in)) { + list_insert(&analog_in_list, result); + return &result->analog_in; + } else { + down(); + return NULL; + } +} + +void moberg4simulink_analog_in_close(int index, + struct moberg_analog_in *analog_in) +{ + struct channel *channel = + (void*)analog_in - offsetof(struct channel, analog_in); + moberg_analog_in_close(g_moberg.moberg, index, channel->analog_in); + list_remove(channel); + free(channel); + down(); +} + +struct moberg_analog_out *moberg4simulink_analog_out_open(int index) + { + up(); + struct channel *result = malloc(sizeof(*result)); + if (result && moberg_analog_out_open(g_moberg.moberg, index, + &result->analog_out)) { + list_insert(&analog_out_list, result); + return &result->analog_out; + } else { + down(); + return NULL; + } +} + +void moberg4simulink_analog_out_close(int index, + struct moberg_analog_out *analog_out) +{ + struct channel *channel = + (void*)analog_out - offsetof(struct channel, analog_out); + moberg_analog_out_close(g_moberg.moberg, index, channel->analog_out); + list_remove(channel); + free(channel); + down(); +} + +struct moberg_digital_in *moberg4simulink_digital_in_open(int index) +{ + up(); + struct channel *result = malloc(sizeof(*result)); + if (result && moberg_digital_in_open(g_moberg.moberg, index, + &result->digital_in)) { + list_insert(&digital_in_list, result); + return &result->digital_in; + } else { + down(); + return NULL; + } +} + + +void moberg4simulink_digital_in_close(int index, + struct moberg_digital_in *digital_in) +{ + struct channel *channel = + (void*)digital_in - offsetof(struct channel, digital_in); + moberg_digital_in_close(g_moberg.moberg, index, channel->digital_in); + list_remove(channel); + free(channel); + down(); +} + +struct moberg_digital_out *moberg4simulink_digital_out_open(int index) +{ + up(); + struct channel *result = malloc(sizeof(*result)); + if (result && moberg_digital_out_open(g_moberg.moberg, index, + &result->digital_out)) { + list_insert(&digital_out_list, result); + return &result->digital_out; + } else { + down(); + return NULL; + } +} + + +void moberg4simulink_digital_out_close(int index, + struct moberg_digital_out *digital_out) +{ + struct channel *channel = + (void*)digital_out - offsetof(struct channel, digital_out); + moberg_digital_out_close(g_moberg.moberg, index, channel->digital_out); + list_remove(channel); + free(channel); + down(); +} + +struct moberg_encoder_in *moberg4simulink_encoder_in_open(int index) +{ + up(); + struct channel *result = malloc(sizeof(*result)); + if (result && moberg_encoder_in_open(g_moberg.moberg, index, + &result->encoder_in)) { + list_insert(&encoder_in_list, result); + return &result->encoder_in; + } else { + down(); + return NULL; + } +} + + +void moberg4simulink_encoder_in_close(int index, + struct moberg_encoder_in *encoder_in) +{ + struct channel *channel = + (void*)encoder_in - offsetof(struct channel, encoder_in); + moberg_encoder_in_close(g_moberg.moberg, index, channel->encoder_in); + list_remove(channel); + free(channel); + down(); +} + diff --git a/adaptors/matlab/moberg4simulink.h b/adaptors/matlab/moberg4simulink.h new file mode 100644 index 0000000000000000000000000000000000000000..2b26a67d27677d3bc7b8b9a2386c3b05954e11d6 --- /dev/null +++ b/adaptors/matlab/moberg4simulink.h @@ -0,0 +1,31 @@ +#ifndef __MOBERG4SIMULINK_H__ +#define __MOBERG4SIMULINK_H__ + +#include <moberg.h> + +struct moberg_analog_in *moberg4simulink_analog_in_open(int index); + +void moberg4simulink_analog_in_close(int index, + struct moberg_analog_in *analog_in); + +struct moberg_analog_out *moberg4simulink_analog_out_open(int index); + +void moberg4simulink_analog_out_close(int index, + struct moberg_analog_out *analog_out); + +struct moberg_digital_in *moberg4simulink_digital_in_open(int index); + +void moberg4simulink_digital_in_close(int index, + struct moberg_digital_in *digital_in); + +struct moberg_digital_out *moberg4simulink_digital_out_open(int index); + +void moberg4simulink_digital_out_close(int index, + struct moberg_digital_out *digital_out); + +struct moberg_encoder_in *moberg4simulink_encoder_in_open(int index); + +void moberg4simulink_encoder_in_close(int index, + struct moberg_encoder_in *encoder_in); + +#endif diff --git a/moberg.c b/moberg.c index e302fce82d51740f6d13b03dee2f2671fcdc2002..a0b6e37458371c66281a18828c5afaacf9147919 100644 --- a/moberg.c +++ b/moberg.c @@ -212,8 +212,12 @@ static int install_config(struct moberg *moberg) .context=moberg, .channel=install_channel }; - return moberg_config_install_channels(moberg->config, &install); - /* TODO cleanup unused devices...*/ + if (! moberg->config) { + fprintf(stderr, "No moberg configuration found\n"); + return 0; + } else { + return moberg_config_install_channels(moberg->config, &install); + } } struct moberg *moberg_new( @@ -310,7 +314,109 @@ int moberg_analog_in_close(struct moberg *moberg, return 1; } +int moberg_analog_out_open(struct moberg *moberg, + int index, + struct moberg_analog_out *analog_out) +{ + struct moberg_channel *channel = NULL; + channel_list_get(&moberg->analog_out, index, &channel); + if (channel) { + channel->open(channel); + *analog_out = channel->action.analog_out; + return 1; + } + return 0; +} + +int moberg_analog_out_close(struct moberg *moberg, + int index, + struct moberg_analog_out analog_out) +{ + struct moberg_channel *channel = NULL; + channel_list_get(&moberg->analog_out, index, &channel); + if (channel && channel->action.analog_out.context == analog_out.context) { + channel->close(channel); + } + return 1; +} + +int moberg_digital_in_open(struct moberg *moberg, + int index, + struct moberg_digital_in *digital_in) +{ + struct moberg_channel *channel = NULL; + channel_list_get(&moberg->digital_in, index, &channel); + if (channel) { + channel->open(channel); + *digital_in = channel->action.digital_in; + return 1; + } + return 0; +} + +int moberg_digital_in_close(struct moberg *moberg, + int index, + struct moberg_digital_in digital_in) +{ + struct moberg_channel *channel = NULL; + channel_list_get(&moberg->digital_in, index, &channel); + if (channel && channel->action.digital_in.context == digital_in.context) { + channel->close(channel); + } + return 1; +} + +int moberg_digital_out_open(struct moberg *moberg, + int index, + struct moberg_digital_out *digital_out) +{ + struct moberg_channel *channel = NULL; + channel_list_get(&moberg->digital_out, index, &channel); + if (channel) { + channel->open(channel); + *digital_out = channel->action.digital_out; + return 1; + } + return 0; +} + +int moberg_digital_out_close(struct moberg *moberg, + int index, + struct moberg_digital_out digital_out) +{ + struct moberg_channel *channel = NULL; + channel_list_get(&moberg->digital_out, index, &channel); + if (channel && channel->action.digital_out.context == digital_out.context) { + channel->close(channel); + } + return 1; +} + +int moberg_encoder_in_open(struct moberg *moberg, + int index, + struct moberg_encoder_in *encoder_in) +{ + struct moberg_channel *channel = NULL; + channel_list_get(&moberg->encoder_in, index, &channel); + if (channel) { + channel->open(channel); + *encoder_in = channel->action.encoder_in; + return 1; + } + return 0; +} +int moberg_encoder_in_close(struct moberg *moberg, + int index, + struct moberg_encoder_in encoder_in) +{ + struct moberg_channel *channel = NULL; + channel_list_get(&moberg->encoder_in, index, &channel); + if (channel && channel->action.encoder_in.context == encoder_in.context) { + channel->close(channel); + } + return 1; +} /* System init functionality (systemd/init/...) */ diff --git a/moberg.h b/moberg.h index 1ffa2c3f6f4162e3e47e30d994d20757ccf17e42..ffe541059aabe3b283ca95fa7bb9d185fb50e9e7 100644 --- a/moberg.h +++ b/moberg.h @@ -47,6 +47,38 @@ int moberg_analog_in_close(struct moberg *moberg, int index, struct moberg_analog_in analog_in); +int moberg_analog_out_open(struct moberg *moberg, + int index, + struct moberg_analog_out *analog_out); + +int moberg_analog_out_close(struct moberg *moberg, + int index, + struct moberg_analog_out analog_out); + +int moberg_digital_in_open(struct moberg *moberg, + int index, + struct moberg_digital_in *digital_in); + +int moberg_digital_in_close(struct moberg *moberg, + int index, + struct moberg_digital_in digital_in); + +int moberg_digital_out_open(struct moberg *moberg, + int index, + struct moberg_digital_out *digital_out); + +int moberg_digital_out_close(struct moberg *moberg, + int index, + struct moberg_digital_out digital_out); + +int moberg_encoder_in_open(struct moberg *moberg, + int index, + struct moberg_encoder_in *encoder_in); + +int moberg_encoder_in_close(struct moberg *moberg, + int index, + struct moberg_encoder_in encoder_in); + /* System init functionality (systemd/init/...) */ int moberg_start( diff --git a/plugins/comedi/Makefile b/plugins/comedi/Makefile index 543d53481278a8d3db8a4bd2fec4fb6ee2555d5e..5aa40f2e6a0af8f8961e3b35034c0054fcc8fc93 100644 --- a/plugins/comedi/Makefile +++ b/plugins/comedi/Makefile @@ -1,10 +1,9 @@ LIBRARIES=libmoberg_comedi.so CCFLAGS+=-Wall -Werror -I../.. -g -MODULES=$(wildcard modules/*) all: $(LIBRARIES:%=../../build/%) ../../build/libmoberg_comedi.so: comedi.c Makefile - $(CC) -o $@ $(CCFLAGS) -shared -fPIC -lcomedi $< + $(CC) -o $@ $(CCFLAGS) -shared -fPIC -lcomedi -L../../build -lmoberg $< ../../build/libmoberg_comedi.so: ../../moberg_module.h diff --git a/plugins/serial2002/Makefile b/plugins/serial2002/Makefile index e6b34fb65ed69f754252b043941e922ebc2891da..9d13537c2362bf126e44833a792ceb233bf005ad 100644 --- a/plugins/serial2002/Makefile +++ b/plugins/serial2002/Makefile @@ -6,6 +6,6 @@ MODULES=$(wildcard modules/*) all: $(LIBRARIES:%=../../build/%) ../../build/libmoberg_serial2002.so: serial2002.c Makefile - $(CC) -o $@ $(CCFLAGS) -shared -fPIC $< + $(CC) -o $@ $(CCFLAGS) -shared -fPIC -L../../build -lmoberg $< ../../build/libmoberg_serial2002.so: ../../moberg_module.h diff --git a/test/Makefile b/test/Makefile index 1515606bacfd15381a175c6ff02586059c656589..1c0ca426f1ec5f310dda47fe46db5ca010a88fbb 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,6 +1,9 @@ -TEST = test_start_stop test_io - +TEST = test_start_stop test_io test_moberg4simulink +CCFLAGS += -Wall -Werror -I$(shell pwd) -g +LDFLAGS += -L$(shell pwd)/build/ -lmoberg ENV_TEST = LD_LIBRARY_PATH=../build HOME=. +LDFLAGS_test_moberg4simulink = -lmoberg4simulink +CCFLAGS_test_moberg4simulink = -I../adaptors/matlab -Wall -Werror -I$(shell pwd) -g all: @@ -14,7 +17,7 @@ run_%: build/% .PRECIOUS: build/% build/%: %.c | build - $(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $< + $(CC) $(CCFLAGS) $(CCFLAGS_$*) $(LDFLAGS) $(LDFLAGS_$*) -o $@ $< build: mkdir build