S-функция sfun_rtwdwork.c показывает, как настроить вектор DWork для использования с продуктом Simulink ® Coder™. Модель Simulinksfcndemo_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);
}В следующем примере для хранения значения статической переменной используется вектор scratch 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 обрабатывает scratch 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-функция использовала общий вектор DWork вместо царапинного вектора DWork, генерация кода с тем же TLC-файлом привела бы к включению вектора DWork в структуру данных следующим образом:
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]);
}
}