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

Обзор

C S-функции MEX позволяют вам вызывать существующий код С в своих моделях 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™, с помощью предыдущих трех методов. Модель sfcndemo_choosing_sfun содержит блоки, которые используют эти S-функции. Скопируйте эту модель и файлы doubleIt.c и doubleIt.h от папки docroot /toolbox/simulink/sfg/examples в вашу рабочую папку, если вы планируете продвинуться через примеры.

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

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

Чтобы включить унаследованный код в 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]);
}

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

mex wrapsfcn.c doubleIt.c

Чтобы сгенерировать код для S-функции с помощью генератора кода Simulink Coder, необходимо записать файл Компилятора выходного языка (TLC). Следующий файл TLC wrapsfcn.tlc использует функцию BlockTypeSetup, чтобы объявить прототипа функции для doubleIt.c. Функция Outputs файла TLC затем говорит генератор кода 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-функции, чтобы включить унаследованный код

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

extern real_T doubleIt(real_T in1);

Блок S-Function Builder в sfcndemo_choosing_sfun показывает, как сконфигурировать диалоговое окно блока, чтобы вызвать устаревший функциональный 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 генерирует три файла.

FileName Описание
builder_wrapsfcn.cОсновная S-функция.
builder_wrapsfcn_wrapper.cФайл обертки, содержащий отдельные функции для кода, введенного в Outputs, Continuous Derivatives и панелях Discrete Updates Разработчика S-функции.
builder_wrapsfcn.tlcФайл S-функции TLC.

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

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

    #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-функции.

  • Файл вызовы метода 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 имеет три части:

  • Раздел 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 */
    
  • Раздел External References содержит информацию от поля External reference declarations на панели Libraries. Этот пример не использует этот раздел.

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

    /*
     * 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-функции помещает вызов наследия C функция вниз дополнительный уровень через файл обертки builder_wrapsfcn_wrapper.c.

Файл TLC builder_wrapsfcn.tlc, сгенерированный Разработчиком S-функции, подобен предыдущей рукописной версии. Файл объявляет устаревшую функцию в 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, чтобы включить унаследованный код

Раздел Integrate C Functions into Simulink Models with 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. Метод 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-функции можно следующим образом:

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

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

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

Файл TLC legacy_wrapsfcn.tlc поддерживает выражение, сворачивающееся путем определения функций BlockOutputSignal и BlockInstanceSetup. Файл 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