Создайте S-функции, которые работают беспрепятственно с Simulink® и продуктами генератора кода при помощи концепции обертки. Вы можете:
Соедините интерфейсом со своими алгоритмами в моделях Simulink путем записи оберток S-функции MEX (
).sfunction.mex
Направьте генератор кода, чтобы вставить ваш алгоритм в сгенерированный код путем создания обертки S-функции TLC (
).sfunction.tlc
Создание S-функций при помощи обертки S-функции позволяет вам вставить алгоритмы C/C++ кода в модели Simulink и сгенерированный код с минимальным изменением в вашей исходной функции C/C++. Оберткой S-функции MEX является S-функция, которая вызывает код, который находится в другом модуле.
Используйте обертку S-функции MEX только в версии MATLAB®, в которой вы создали обертку.
Предположим, что у вас есть алгоритм (то есть, функция C) названный my_alg
, который находится в файле my_alg.c
. Можно интегрировать my_alg
в модель Simulink путем создания обертки S-функции MEX (например, wrapsfcn.c
). Модель Simulink может затем вызвать my_alg
от Блока s-function. S-функция Simulink содержит набор пустых функций, которых механизм Simulink требует в различных связанных с API целях. Например, несмотря на то, что только mdlOutputs
вызывает my_alg
, механизм вызывает mdlTerminate
, даже при том, что эта стандартная программа S-функции не выполняет действия.
Можно встроить вызов my_alg
в сгенерированном коде путем создания обертки S-функции TLC (например, wrapsfcn.tlc
). Можно устранить пустые вызовы функции. Можно избежать издержек выполнения функции mdlOutputs
, и можно затем устранить функцию my_alg
.
S-функции обертки полезны, когда вы создаете алгоритмы, которые являются процедурными или когда вы интегрируете унаследованный код в модель Simulink. Если вы хотите создать код, который является:
Интерпретирующий по своей природе (то есть, высоко параметризованный рабочими режимами)
В большой степени оптимизированный (то есть, никакие дополнительные тесты, чтобы решить то, чем режим код управляет в),
затем необходимо создать полностью встроенный файл TLC для S-функции.
Следующие данные показывают концепцию S-функции обертки.
Используя обертку S-функции, чтобы импортировать алгоритмы в вашу модель Simulink означает, что S-функция служит интерфейсом, который вызывает ваши алгоритмы C/C++ от mdlOutputs
. Можно быстро интегрировать большие автономные программы C/C++ в модель, не имея необходимость изменять код.
Эта демонстрационная модель включает обертку S-функции.
Два файла сопоставлены с блоком wrapsfcn
: обертка S-функции и код C/C++, который содержит алгоритм. Первые три оператора:
Задайте имя S-функции (что вы вводите в диалоговое окно Блока s-function Simulink).
Укажите, что S-функция использует формат уровня 2.
Обеспечьте доступ к структуре данных SimStruct
. SimStruct
содержит указатели на данные, используемые во время симуляции и генерации кода, и задает макросы, которые хранят данные в и получают данные от SimStruct
.
#define S_FUNCTION_NAME wrapsfcn #define S_FUNCTION_LEVEL 2 #include "simstruc.h" extern real_T my_alg(real_T u); /* Declare my_alg as extern */ /* * mdlInitializeSizes - initialize the sizes array */ static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams( S, 0); /*number of input arguments*/ if (!ssSetNumInputPorts(S, 1)) return; ssSetInputPortWidth(S, 0, 1); ssSetInputPortDirectFeedThrough(S, 0, 1); if (!ssSetNumOutputPorts(S,1)) return; ssSetOutputPortWidth(S, 0, 1); ssSetNumSampleTimes( S, 1); } /* * mdlInitializeSampleTimes - indicate that this S-function runs * at the rate of the source (driving block) */ static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); ssSetOffsetTime(S, 0, 0.0); } /* * mdlOutputs - compute the outputs by calling my_alg, which * resides in another module, my_alg.c */ static void mdlOutputs(SimStruct *S, int_T tid) { InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); real_T *y = ssGetOutputPortRealSignal(S,0); *y = my_alg(*uPtrs[0]); /* Call my_alg in mdlOutputs */ } /* * mdlTerminate - called when the simulation is terminated. */ 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
Для получения дополнительной информации смотрите Шаблоны для S-функций C (Simulink).
mdlOutputs
стандартной программы S-функции содержит вызов функции к my_alg
, который является функцией C, содержащей алгоритм, который выполняет S-функция. Для my_alg.c
код:
#ifdef MATLAB_MEX_FILE #include "tmwtypes.h" #else #include "rtwtypes.h" #endif real_T my_alg(real_T u) { return(u * 2.0); }
Для получения дополнительной информации смотрите, Управляют Зависимостями от Файла Процесса сборки.
wrapsfcn
S-функции обертки вызывает my_alg
, который вычисляет u * 2.0
. Чтобы создать wrapsfcn.mex
, используйте эту команду:
mex wrapsfcn.c my_alg.c
Обертка S-функции TLC является файлом TLC, который задает, как генератор кода вызывает ваш код. Например, можно встроить вызов my_alg
в разделе mdlOutputs
сгенерированного кода. В примере Обертки S-функции MEX вызов my_alg
встраивается в раздел mdlOutputs
как:
*y = my_alg(*uPtrs[0]);
Когда вы создаете обертку S-функции TLC, цель состоит в том, чтобы встроить тот же тип вызова в сгенерированном коде.
Посмотрите на то, как генератор кода выполняет S-функции, которые не встраиваются. Невстроенная S-функция идентифицирована отсутствием файла
и существование sfunction.tlc
. При генерации кода для невстроенной S-функции генератор кода производит вызов sfunction.mex
mdlOutputs
через указатель функции что, в этом примере, затем вызывает my_alg
.
Пример обертки содержит одну S-функцию, wrapsfcn.mex
. Необходимо скомпилировать и соединить дополнительный модуль, my_alg
, со сгенерированным кодом. В команде MATLAB запрашивают, введите:
set_param('wrapper/S-Function','SFunctionModules','my_alg')
Код, сгенерированный при использовании grt.tlc
как системный конечный файл без
wrapsfcn.tlc
:
<Generated code comments for wrapper model with noninlined wrapsfcn S-function> #include <math.h> #include <string.h> #include "wrapper.h" #include "wrapper.prm" /* Start the model */ void mdlStart(void) { /* (start code not required) */ } /* Compute block outputs */ void mdlOutputs(int_T tid) { /* Sin Block: <Root>/Sin */ rtB.Sin = rtP.Sin.Amplitude * sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase); /* Level2 S-Function Block: <Root>/S-Function (wrapsfcn) */ { /* Noninlined S-functions create a SimStruct object and * generate a call to S-function routine mdlOutputs */ SimStruct *rts = ssGetSFunction(rtS, 0); sfcnOutputs(rts, tid); } /* Outport Block: <Root>/Out */ rtY.Out = rtB.S_Function; } /* Perform model update */ void mdlUpdate(int_T tid) { /* (update code not required) */ } /* Terminate function */ void mdlTerminate(void) { /* Level2 S-Function Block: <Root>/S-Function (wrapsfcn) */ { /* Noninlined S-functions require a SimStruct object and * the call to S-function routine mdlTerminate */ SimStruct *rts = ssGetSFunction(rtS, 0); sfcnTerminate(rts); } } #include "wrapper.reg" /* [EOF] wrapper.c */
Сгенерированный файл wrapper.reg
содержит инициализацию SimStruct
для Блока s-function обертки. Существует один дочерний SimStruct
для каждого Блока s-function в вашей модели. Можно значительно уменьшать эти издержки путем создания обертки TLC для S-функции.
Сгенерированный код выполняет вызов к вашей S-функции, wrapsfcn.c
, в mdlOutputs
при помощи этого кода:
SimStruct *rts = ssGetSFunction(rtS, 0); sfcnOutputs(rts, tid);
Этот вызов имеет вычислительные издержки, сопоставленные с ним. Механизм Simulink создает структуру данных SimStruct
для Блока s-function. Генератор кода создает вызов через указатель функции, чтобы выполнить mdlOutputs
, затем mdlOutputs
вызывает my_alg
. Путем встраивания вызова алгоритма C/C++, my_alg
, можно устранить и SimStruct
и дополнительный вызов функции, таким образом, повысив эффективность и уменьшая размер сгенерированного кода.
Встраивание S-функции обертки требует файла
для S-функции. Файл TLC должен содержать вызов функции к sfunction.tlc
my_alg
. Данные показывают отношения между алгоритмом, S-функцией обертки и файлом
.sfunction.tlc
Чтобы встроить вызов my_alg
, поместите свой вызов функции в файл
с тем же именем как S-функция (в этом примере, sfunction.tlc
wrapsfcn.tlc
). Компилятор Выходного языка заменяет метод по умолчанию размещения вызовов вашей S-функции в сгенерированном коде.
Этот код является файлом TLC wrapsfcn.tlc
, который встраивает wrapsfcn.c
:
%% File : wrapsfcn.tlc %% Abstract: %% Example inlined tlc file for S-function wrapsfcn.c %% %implements "wrapsfcn" "C" %% Function: BlockTypeSetup ==================================================== %% Abstract: %% Create function prototype in model.h as: %% "extern real_T my_alg(real_T u);" %% %function BlockTypeSetup(block, system) void %openfile buffer extern real_T my_alg(real_T u); /* This line is placed in wrapper.h */ %closefile buffer %<LibCacheFunctionPrototype(buffer)> %endfunction %% BlockTypeSetup %% Function: Outputs =========================================================== %% Abstract: %% y = my_alg( u ); %% %function Outputs(block, system) Output /* %<Type> Block: %<Name> */ %assign u = LibBlockInputSignal(0, "", "", 0) %assign y = LibBlockOutputSignal(0, "", "", 0) %% PROVIDE THE CALLING STATEMENT FOR "algorithm" %% The following line is expanded and placed in mdlOutputs within wrapper.c %<y> = my_alg(%<u>); %endfunction %% Outputs
Первый раздел этого кода встраивает Блок s-function wrapsfcn
и генерирует код в C:
%implements "wrapsfcn" "C"
Следующая задача состоит в том, чтобы сообщить генератору кода, что стандартный my_alg
должен быть объявлен как внешний в сгенерированном файле wrapper.h
для любых Блоков s-function wrapsfcn
в модели. Сделайте это объявление однажды для всех Блоков s-function wrapsfcn
при помощи функции BlockTypeSetup
. В этой функции вы направляете Компилятор Выходного языка, чтобы создать буфер и кэшировать my_alg
, когда extern
в wrapper.h
сгенерировал заголовочный файл.
Последний шаг является встраиванием вызова функционального my_alg
. Функция Outputs
встраивает вызов. В этой функции вы получаете доступ к вводу и выводу блока и помещаете прямой вызов my_alg
. Вызов встраивается в wrapper.c
.
Код, сгенерированный, когда вы встраиваете свою S-функцию обертки, подобен сгенерированному коду по умолчанию. Функция mdlTerminate
не содержит вызов пустой функции, и функция mdlOutputs
теперь непосредственно вызывает my_alg
.
void mdlOutputs(int_T tid) { /* Sin Block: <Root>/Sin */ rtB.Sin = rtP.Sin.Amplitude * sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase); /* S-Function Block: <Root>/S-Function */ rtB.S_Function = my_alg(rtB.Sin); /* Inlined call to my_alg */ /* Outport Block: <Root>/Out */ rtY.Out = rtB.S_Function; }
wrapper.reg
не создает дочерний SimStruct
для S-функции, потому что сгенерированный код вызывает my_alg
непосредственно, устраняя более чем 1 Кбайт использования памяти.