S-функции включают устаревший код С

Обзор

C MEX S-функции позволяют вам вызывать существующие Коды С в Simulink® модели. Для примера рассмотрим простую функцию C doubleIt.c это выводит значение в два раза больше значения входного параметра функции.

double doubleIt(double u)
{
    return(u * 2.0);
}

Можно создать S-функцию, которая вызывает doubleIt.c по одному из следующих:

  • Написание оболочки S-функции. Используя этот метод, вы вручную записываете новую S-функцию C и связанный с ней файл TLC. Этот метод требует наибольших знаний о структуре S-функции C.

  • Использование блока S-Function Builder. Используя этот метод, вы вводите характеристики S-функции в диалоговое окно блока. Этот метод не требует никаких знаний о записи S-функций. Однако базовое понимание структуры S-функции может сделать диалоговое окно S-Function Builder более простым в использовании.

  • Использование Legacy Code Tool. Используя этот метод командной строки, вы определяете характеристики вашей S-функции в структуре данных в MATLAB® рабочей области. Этот метод требует наименьшего объема знаний о S-функциях.

Можно также вызвать внешний код С из модели Simulink с помощью блока MATLAB Function. Для получения дополнительной информации смотрите Интегрировать код С Используя Блок MATLAB function.

В следующих разделах описывается, как создать S-функции для использования в симуляции Simulink и с генерацией кода Simulink Coder™, используя предыдущие три метода. Модель примера содержит блоки, которые используют эти S-функции. Если вы планируете создать модель, скопируйте файлы doubleIt.c и doubleIt.h из папки docroot/toolbox/simulink/examples в рабочую папку.

Использование написанной вручную S-функции для включения Унаследованного кода

S-функция wrapsfcn.c вызывает унаследованную функцию doubleIt.c в своей mdlOutputs способ. Сохраните wrapsfcn.c файл в рабочую папку, если вы планируете создать модель примера.

Чтобы включить унаследованный код в S-функцию, wrapsfcn.c начинается путем объявления doubleIt.c следующей линией:

extern real_T doubleIt(real_T u);

После объявления S-функция может использовать doubleIt.c в своей mdlOutputs способ. Для примера:

/* Function: mdlOutputs =======================================
 * Abstract:
 *    Calls the doubleIt.c function to multiple the input by 2.
 */
static void mdlOutputs(SimStruct *S, int tid){   
	 InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
	 real_T            *y    = ssGetOutputPortRealSignal(S,0);   

	 *y = doubleIt(*uPtrs[0]);
}

Чтобы скомпилировать wrapsfcn.c S-функция, запустите следующую mex команда. Убедитесь, что doubleIt.c файл находится в рабочей папке.

mex wrapsfcn.c doubleIt.c

Чтобы сгенерировать код для S-функции с помощью генератора кода Simulink Coder, необходимо написать файл Target Language Compiler (TLC). Следующий файл TLC wrapsfcn.tlc использует BlockTypeSetup функция для объявления прототипа функции для doubleIt.c. Файл TLC Outputs затем функция сообщает генератору кода Simulink Coder, как вписать вызов в doubleIt.c. Для примера:

%implements "wrapsfcn" "C"
%% File    : wrapsfcn.tlc
%% Abstract:
%%      Example tlc file for S-function wrapsfcn.c
%%

%% Function: BlockTypeSetup ================================
%% Abstract:
%%      Create function prototype in model.h as:
%%	    "extern double doubleIt(double u);" 
%%

%function BlockTypeSetup(block, system) void
  %openfile buffer

  %% PROVIDE ONE LINE OF CODE AS A FUNCTION PROTOTYPE
  extern double doubleIt(double u);

  %closefile buffer
  %<LibCacheFunctionPrototype(buffer)>
  %%endfunction %% BlockTypeSetup

%% Function: Outputs =======================================
%% Abstract:
%%      CALL LEGACY FUNCTION: y = doubleIt( u );
%%

%function Outputs(block, system) Output

  /* %<Type> Block: %<Name> */

  %assign u = LibBlockInputSignal(0, "", "", 0)
  %assign y = LibBlockOutputSignal(0, "", "", 0)

  %% PROVIDE THE CALLING STATEMENT FOR "doubleIt"
  %<y> = doubleIt( %<u> );

%endfunction %% Outputs

Для получения дополнительной информации о TLC, смотрите Основы компилятора целевого языка (Simulink Coder).

Использование S-Function Builder для включения унаследованного кода

S-Function Builder автоматизирует создание S-функций и файлов TLC, которые включают унаследованный код. Для этого примера в дополнение к doubleIt.c, вам нужен заголовочный файл doubleIt.h который объявляет doubleIt.c формат функции, следующим образом:

extern real_T doubleIt(real_T in1);

Используйте блок S-Function Builder, чтобы сконфигурировать диалоговое окно блока, чтобы вызвать устаревшую функцию doubleIt.c. В диалоговом окне S-Function Builder:

  • Поле S-function name на панели Parameters задает имя builder_wrapsfcn для сгенерированной S-функции.

  • Панель Data Properties называет порты входа и выхода следующими in1 и out1, соответственно.

  • Панель Libraries предоставляет интерфейс для унаследованного кода.

    • Поле Library/Object/Source files содержит имя исходного файла doubleIt.c.

    • Поле Includes содержит следующую линию для включения файла заголовка, который объявляет унаследованную функцию:

      #include <doubleIt.h>
  • Панель Outputs вызывает устаревшую функцию со линиями:

    /* Call function that multiplies the input by 2 */
    
          *out1 = doubleIt(*in1);
  • Панель Build Info выбирает опцию Generate wrapper TLC.

Когда вы нажимаете Build, S-Function Builder генерирует три файла.

Имя файлаОписание
builder_wrapsfcn.cОсновная S-функция.
builder_wrapsfcn_wrapper.cФайл, содержащий отдельные функции для кода, введенные в Outputs, Continuous Derivatives и Discrete Updates панелях S-Function Builder.
builder_wrapsfcn.tlcФайл TLC S-функции.

The builder_wrapsfcn.c файл следует стандартному формату:

  • Файл начинается с набора #define операторы, которые включают информацию из S-Function Builder. Для примера следующие линии определяют первый порт входа:

    #define NUM_INPUTS          1
    /* Input Port  0 */
    #define IN_PORT_0_NAME      in1
    #define INPUT_0_WIDTH       1
    #define INPUT_DIMS_0_COL    1
    #define INPUT_0_DTYPE       real_T
    #define INPUT_0_COMPLEX     COMPLEX_NO
    #define IN_0_FRAME_BASED    FRAME_NO
    #define IN_0_DIMS           1-D
    #define INPUT_0_FEEDTHROUGH 1
  • Далее в файле объявляются все функции оболочки, найденные в builder_wrapsfcn_wrapper.c файл. Этот пример требует только функцию обертки для кода Outputs.

    extern void builder_wrapsfcn_Outputs_wrapper(const real_T *in1,
                              real_T *out1);
  • Следуя этим определениям и объявлениям, файл содержит методы S-функции, такие как mdlInitializeSizes, которые инициализируют входные порты S-функции, выходные порты и параметры. Смотрите Представление процесса для получения списка методов, которые вызываются во время фазы инициализации S-функции.

  • The mdlOutputs метод файла вызывает builder_wrapsfcn_wrapper.c функция. Метод использует входные и выходные имена in1 и out1, как определено на панели Data Properties, при вызове функции обертки. Для примера:

    /* Function: mdlOutputs =============================================
     *
    */
    static void mdlOutputs(SimStruct *S, int_T tid)
    {
        const real_T   *in1  = (const real_T*) ssGetInputPortSignal(S,0);
        real_T        *out1  = (real_T *)ssGetOutputPortRealSignal(S,0);
    
        builder_wrapsfcn_Outputs_wrapper(in1, out1);
    }
  • Файл builder_wrapsfcn.c заключает с необходимым mdlTerminate способ.

Функция обертки builder_wrapsfcn_wrapper.c имеет три части:

  • The Include Files раздел включает в себя doubleIt.h файл, наряду со стандартными файлами заголовков S-функций:

    /*
     * Include Files
     *
     */
    #if defined(MATLAB_MEX_FILE)
    #include "tmwtypes.h"
    #include "simstruc_types.h"
    #else
    #include "rtwtypes.h"
    #endif
    /* %%%-SFUNWIZ_wrapper_includes_Changes_BEGIN --- EDIT HERE TO _END */
    #include <math.h>
    #include <doubleIt.h>
    /* %%%-SFUNWIZ_wrapper_includes_Changes_END --- EDIT HERE TO _BEGIN */
    
  • The External References раздел содержит информацию из поля External reference declarations на панели Libraries. Этот пример не использует этот раздел.

  • The Output functions раздел объявляет функцию builder_wrapfcn_Outputs_wrapper, который содержит код, введенный в Outputs панели диалогового окна S-Function Builder:

    /*
     * Output functions
     *
     */
    void builder_wrapfcn_Outputs_wrapper(const real_T *in1,
                              real_T *out1)
    {
    /* %%%-SFUNWIZ_wrapper_Outputs_Changes_BEGIN --- EDIT HERE TO _END */
    /* Call function that multiplies the input by 2 */
    
          *out1 = doubleIt(*in1);
    /* %%%-SFUNWIZ_wrapper_Outputs_Changes_END --- EDIT HERE TO _BEGIN */
    }

Примечание

По сравнению с рукописной S-функцией, S-Function Builder помещает вызов унаследованной функции C на дополнительный уровень вниз через файл оболочки builder_wrapsfcn_wrapper.c.

Файл TLC builder_wrapsfcn.tlc сгенерирован S-Function Builder аналогично предыдущей рукописной версии. Файл объявляет унаследованную функцию в BlockTypeSetup и вызывает его в Outputs способ.

%implements  builder_wrapsfcn "C"
%% Function: BlockTypeSetup ====================================
%%
%% Purpose:
%%      Set up external references for wrapper functions in the 
%%      generated code.
%%
%function BlockTypeSetup(block, system) Output
 %openfile externs
    
 extern void builder_wrapsfcn_Outputs_wrapper(const real_T *in1,
                          real_T *out1);
 %closefile externs
 %<LibCacheExtern(externs)>
 %%
%endfunction

%% Function: Outputs ===========================================
%%
%% Purpose:
%%      Code generation rules for mdlOutputs function.
%%
%function Outputs(block, system) Output
   /* S-Function "builder_wrapsfcn_wrapper" Block: %<Name> */

 %assign pu0 = LibBlockInputSignalAddr(0, "", "", 0)
 %assign py0 = LibBlockOutputSignalAddr(0, "", "", 0)
 %assign py_width = LibBlockOutputSignalWidth(0)
 %assign pu_width = LibBlockInputSignalWidth(0)
 builder_wrapsfcn_Outputs_wrapper(%<pu0>, %<py0> );

 %%
%endfunction

Использование инструмента Legacy Code Tool для включения унаследованного кода

В разделе Интеграция функций C в модели Simulink с помощью Legacy Code Tool в «Написание S-функций в C» показано, как использовать Legacy Code Tool для создания S-функции, которая включает doubleIt.c. Для скрипта, который выполняет шаги в этом примере, скопируйте файл lct_wrapsfcn.m в рабочую папку. Убедитесь, что doubleIt.c и doubleIt.h файлы находятся в рабочей папке, затем запустите скрипт путем ввода lct_wrapsfcn в командной строке MATLAB. Скрипт создает и компилирует S-функцию legacy_wrapsfcn.c и создает файл TLC legacy_wrapsfcn.tlc с помощью следующих команд.

% Create the data structure
def = legacy_code('initialize');

% Populate the data struture
def.SourceFiles = {'doubleIt.c'};
def.HeaderFiles = {'doubleIt.h'};
def.SFunctionName = 'legacy_wrapsfcn';
def.OutputFcnSpec = 'double y1 = doubleIt(double u1)';
def.SampleTime = [-1,0];

% Generate the S-function
legacy_code('sfcn_cmex_generate', def);

% Compile the MEX-file
legacy_code('compile', def);

% Generate a TLC-file
legacy_code('sfcn_tlc_generate', def);

S-функция legacy_wrapsfcn.c сгенерированный Legacy Code Tool начинается с включения doubleIt.h заголовочный файл. The mdlOutputs затем метод непосредственно вызывает doubleIt.c функцию следующим образом:

static void mdlOutputs(SimStruct *S, int_T tid)
{
  /*
   * Get access to Parameter/Input/Output/DWork/size information
   */
  real_T *u1 = (real_T *) ssGetInputPortSignal(S, 0);
  real_T *y1 = (real_T *) ssGetOutputPortSignal(S, 0);

  /*
   * Call the legacy code function
   */
  *y1 = doubleIt( *u1);
}

S-функция, сгенерированная Legacy Code Tool, отличается от S-функции, сгенерированной S-Function Builder следующим образом:

  • S-функция, сгенерированная S-Function Builder, вызывает устаревшую функцию doubleIt.c через функцию обертки builder_wrapsfcn_wrapper.c. S-функция, сгенерированная Legacy Code Tool, непосредственно вызывает doubleIt.c от его mdlOutputs способ.

  • S-Function Builder использует входные и выходные имена, введенные в панель Data Properties, что позволяет настраивать эти имена в S-функции. Legacy Code Tool использует имена по умолчанию y и u для выходов и входов, соответственно. Вы не можете задать настраиваемые имена для использования в сгенерированной S-функции при использовании Legacy Code Tool.

  • S-Function Builder и Legacy Code Tool по умолчанию задают унаследованный шаг расчета. Однако S-Function Builder использует время смещения 0.0 в то время как Legacy Code Tool задает, что время смещения фиксировано в незначительных временных шагах.

Файл TLC legacy_wrapsfcn.tlc поддерживает складывание выражений путем определения BlockInstanceSetup и BlockOutputSignal функций. TLC- файла также содержит BlockTypeSetup функция для объявления прототипа функции для doubleIt.c и Outputs функция, чтобы сообщить генератору кода Simulink Coder, как вписать вызов в doubleIt.c.:

%% Function: BlockTypeSetup ===============================================
%%
%function BlockTypeSetup(block, system) void
  %%
  %% The Target Language must be C
  %if ::GenCPP==1
    %<LibReportFatalError("This S-Function generated by the Legacy Code Tool 
         must be only used with the C Target Language")>
  %endif
  %<LibAddToCommonIncludes("doubleIt.h")>
  %<LibAddToModelSources("doubleIt")>
%%
%endfunction

%% Function: BlockInstanceSetup ===========================================
%%
%function BlockInstanceSetup(block, system) void
  %%
  %<LibBlockSetIsExpressionCompliant(block)>
  %%
%endfunction

%% Function: Outputs ======================================================
%%
%function Outputs(block, system) Output
  %%
    %if !LibBlockOutputSignalIsExpr(0)
      %assign u1_val = LibBlockInputSignal(0, "", "", 0)
      %assign y1_val = LibBlockOutputSignal(0, "", "", 0)
    %%
      %<y1_val = doubleIt( %<u1_val>);
    %endif 
  %%
%endfunction

%% Function: BlockOutputSignal ============================================
%%
%function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void
  %%
  %assign u1_val = LibBlockInputSignal(0, "", "", 0)
  %assign y1_val = LibBlockOutputSignal(0, "", "", 0)
  %%
  %switch retType
    %case "Signal"
      %if portIdx == 0
        %return "doubleIt( %<u1_val>)"
      %else
        %assign errTxt = "Block output port index not supported: %<portIdx>"
      %endif
    %default
      %assign errTxt = "Unsupported return type: %<retType>"
      %<LibBlockReportError(block,errTxt)>
  %endswitch
  %%
%endfunction