sfun_rtwdwork.c
S-функции показывает, как сконфигурировать вектор DWork для использования с продуктом Simulink® Coder™. Модель Simulink sfcndemo_sfun_rtwdwork
использует эту S-функцию, чтобы реализовать простой аккумулятор.
Следующий фрагмент метода mdlInitializeSizes
инициализирует вектор DWork и все свойства генерации кода, сопоставленные с ним.
ssSetNumDWork(S, 1); ssSetDWorkWidth(S, 0, 1); ssSetDWorkDataType(S, 0, SS_DOUBLE); /* Identifier; free any old setting and update */ id = ssGetDWorkRTWIdentifier(S, 0); if (id != NULL) { free(id); } id = malloc(80); mxGetString(ID_PARAM(S), id, 80); ssSetDWorkRTWIdentifier(S, 0, id); /* Type Qualifier; free any old setting and update */ tq = ssGetDWorkRTWTypeQualifier(S, 0); if (tq != NULL) { free(tq); } tq = malloc(80); mxGetString(TQ_PARAM(S), tq, 80); ssSetDWorkRTWTypeQualifier(S, 0, tq); /* Storage class */ sc = ((int_T) *((real_T*) mxGetPr(SC_PARAM(S)))) - 1; ssSetDWorkRTWStorageClass(S, 0, sc);
S-функция инициализирует вектор DWork в mdlInitializeConditions
.
#define MDL_INITIALIZE_CONDITIONS /* Function: mdlInitializeConditions ============================ * Abstract: * Initialize both continuous states to zero */ static void mdlInitializeConditions(SimStruct *S) { real_T *x = (real_T*) ssGetDWork(S,0); /* Initialize the dwork to 0 */ x[0] = 0.0; }
Метод mdlOutputs
присваивает векторное значение DWork S-функции вывод.
/* Function: mdlOutputs ======================================== * Abstract: * y = x */ static void mdlOutputs(SimStruct *S, int_T tid) { real_T *y = ssGetOutputPortRealSignal(S,0); real_T *x = (real_T*) ssGetDWork(S,0); /* Return the current state as the output */ y[0] = x[0]; }
Метод mdlUpdate
постепенно увеличивает значение DWork входом.
#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 = (real_T*) ssGetDWork(S,0); InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); /* * Increment the state by the input * U is defined as U(element) (*uPtrs[element]) */ x[0] += U(0); }
Следующий пример использует царапину вектор DWork, чтобы сохранить значение статической переменной. Метод mdlInitializeSizes
конфигурирует ширину и тип данных вектора DWork. Макрос ssSetDWorkUsageType
затем указывает, что вектор DWork является вектором царапины.
ssSetNumDWork(S, 1); ssSetDWorkWidth(S, 0, 1); ssSetDWorkDataType(S, 0, SS_DOUBLE); ssSetDWorkUsageType(S,0, SS_DWORK_USED_AS_SCRATCH);
Остаток от S-функции использует царапину вектор DWork точно, когда это было бы любой другой тип вектора DWork. Метод InitializeConditions
устанавливает начальное значение, и метод mdlOutputs
использует значение, сохраненное в векторе DWork.
#define MDL_INITIALIZE_CONDITIONS /* Function: mdlInitializeConditions ================================ */ static void mdlInitializeConditions(SimStruct *S) { real_T *x = (real_T*) ssGetDWork(S,0); /* Initialize the dwork to 0 */ x[0] = 0.0; } /* Function: mdlOutputs ============================================= */ static void mdlOutputs(SimStruct *S, int_T tid) { real_T *y = ssGetOutputPortRealSignal(S,0); real_T *x1 = (real_T*) ssGetDWork(S,1); x[0] = 2000; y[0] = x[0] * 2; }
Если у вас есть Simulink Coder, указатели программного обеспечения Simulink Coder царапают DWork по-другому по сравнению с другими векторами DWork при генерации кода для встроенной S-функции. Чтобы встроить S-функцию, создайте следующий файл Компилятора выходного языка (TLC), чтобы описать метод mdlOutputs
.
%implements sfun_dscratch "C" %% Function: Outputs ========================================================== %% /* dscratch Block: %<Name> */ %<LibBlockDWork(DWork[0], "", "", 0)> = 2000.0; %<LibBlockOutputSignal(0,"","",0)> = %<LibBlockDWork(DWork[0],"","", 0)> * 2;
Когда программное обеспечение Simulink Coder генерирует код для модели, это встраивает S-функцию и объявляет второй вектор DWork как локальный вектор царапины. Например, образцовая выходная функция содержит следующие строки:
/* local scratch DWork variables */ real_T SFunction_DWORK1; SFunction_DWORK1 = 2000.0;
Если бы S-функция использовала вектор генерала Дуорка вместо царапины, вектор ДУОРКА, генерируя код с тем же файлом TLC привел бы к вектору ДУОРКА, включаемому в структуру данных, можно следующим образом:
sfcndemo_dscratch_DWork.SFunction_DWORK1 = 2000.0;
Этот пример переписывает пример S-функции dsfunc.c
, чтобы использовать вектор DState вместо явного вектора дискретного состояния. Макрос mdlInitializeSizes
инициализирует количество дискретных состояний как нуль и, вместо этого, инициализирует один вектор DWork.
Метод mdlInitializeSizes
затем конфигурирует вектор DWork как вектор DState с помощью вызова ssSetDWorkUsedAsDState
. Это эквивалентно вызову макроса ssSetDWorkUsageType
со значением SS_DWORK_USED_AS_DSTATE
. Метод mdlInitializeSizes
устанавливает ширину и тип данных вектора DState и дает состоянию имя с помощью ssSetDWorkName
.
Векторы DWork, сконфигурированные как векторы DState, должны быть присвоены имя для механизма Simulink, чтобы указать вектор как дискретные состояния. Функциональный
возвращает присвоенное имя в поле Simulink.BlockDiagram.getInitialStates(mdl)
label
для начальных состояний.
static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams(S, 0); /* Number of expected parameters */ if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { return; /* Parameter mismatch reported by the Simulink engine */ } ssSetNumContStates(S, 0); ssSetNumDiscStates(S, 0); if (!ssSetNumInputPorts(S, 1)) return; ssSetInputPortWidth(S, 0, 2); ssSetInputPortDirectFeedThrough(S, 0, 1); if (!ssSetNumOutputPorts(S, 1)) return; ssSetOutputPortWidth(S, 0, 2); ssSetNumSampleTimes(S, 1); ssSetNumRWork(S, 0); ssSetNumIWork(S, 0); ssSetNumPWork(S, 0); ssSetNumModes(S, 0); ssSetNumNonsampledZCs(S, 0); ssSetNumDWork(S, 1); ssSetDWorkUsedAsDState(S, 0, SS_DWORK_USED_AS_DSTATE); ssSetDWorkWidth(S, 0, 2); ssSetDWorkDataType(S, 0, SS_DOUBLE); ssSetDWorkName(S, 0, "SfunStates"); ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE); }
Метод mdlInitializeConditions
инициализирует векторные значения DState с помощью указателя, возвращенного ssGetDWork
.
#define MDL_INITIALIZE_CONDITIONS /* Function: mdlInitializeConditions =============================== * Abstract: * Initialize both discrete states to one. */ static void mdlInitializeConditions(SimStruct *S) { real_T *x0 = (real_T*) ssGetDWork(S, 0); int_T lp; for (lp=0;lp<2;lp++) { *x0++=1.0; } }
Метод mdlOutputs
затем использует значения, сохраненные в векторе DState, чтобы вычислить вывод дискретного уравнения пространства состояний.
/* Function: mdlOutputs ======================================== * Abstract: * y = Cx + Du */ static void mdlOutputs(SimStruct *S, int_T tid) { real_T *y = ssGetOutputPortRealSignal(S,0); real_T *x = (real_T*) ssGetDWork(S, 0); InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); UNUSED_ARG(tid); /* not used in single tasking mode */ /* 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); }
Наконец, метод mdlUpdate
обновляет вектор DState с новыми значениями для дискретных состояний.
#define MDL_UPDATE /* Function: mdlUpdate ============================================ * Abstract: * xdot = Ax + Bu */ static void mdlUpdate(SimStruct *S, int_T tid) { real_T tempX[2] = {0.0, 0.0}; real_T *x = (real_T*) ssGetDWork(S, 0); InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); UNUSED_ARG(tid); /* not used in single tasking mode */ /* 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]; }
Этот пример переписывает S-функцию, sfun_zc.c
, чтобы использовать вектор режима DWork вместо явного режима работает вектор (см., что Элементарные Векторы работы для получения дополнительной информации о режиме работают векторы). Эта S-функция реализует блок абсолютного значения.
Метод mdlInitializeSizes
определяет номер векторов DWork и векторов пересечения нулем (см. Нулевые Пересечения) к DYNAMICALLY_SIZED
. Установка DYNAMICALLY_SIZED
позволяет механизму Simulink задерживать определение размеров вектора работы, пока это не знает размерностей входа, позволяя S-функции поддержать вход с произвольной шириной.
static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams(S, 0); if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { return; /* Parameter mismatch reported by the Simulink engine */ } ssSetNumContStates( S, 0); ssSetNumDiscStates( S, 0); 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); ssSetNumRWork(S, 0); ssSetNumIWork(S, 0); ssSetNumPWork(S, 0); ssSetNumDWork(S, 1); ssSetNumModes(S, 0); /* Initializes the zero-crossing and DWork vectors */ ssSetDWorkWidth(S,0,DYNAMICALLY_SIZED); ssSetNumNonsampledZCs(S, DYNAMICALLY_SIZED); /* Take care when specifying exception free code - see sfuntmpl_doc.c */ ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE); }
Механизм Simulink инициализирует количество векторов пересечения нулем и векторов DWork к числу элементов в сигнале, входя в первый порт S-входного-параметра-функции. Механизм затем вызывает метод mdlSetWorkWidths
, который использует ssGetNumDWork
, чтобы определить, сколько векторов DWork было инициализировано, и затем устанавливает свойства для каждого вектора DWork.
#define MDL_SET_WORK_WIDTHS static void mdlSetWorkWidths(SimStruct *S) { int_T numdw = ssGetNumDWork(S); int_T i; for (i = 0; i < numdw; i++) { ssSetDWorkUsageType(S, i, SS_DWORK_USED_AS_MODE); ssSetDWorkDataType(S, i, SS_BOOLEAN); ssSetDWorkComplexSignal(S, i, COMPLEX_NO); } }
Метод mdlOutputs
использует значение, сохраненное в векторе режима DWork, чтобы определить, должен ли выходной сигнал быть равен входному сигналу или абсолютному значению входного сигнала.
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); boolean_T *mode = ssGetDWork(S,0); UNUSED_ARG(tid); /* not used in single tasking mode */ if (ssIsMajorTimeStep(S)) { for (i = 0; i < width; i++) { mode[i] = (boolean_T)(*uPtrs[i] >= 0.0); } } for (i = 0; i < width; i++) { y[i] = mode[i]? (*uPtrs[i]): -(*uPtrs[i]); } }