exponenta event banner

Развертывание созданных автономных исполняемых программ на целевом оборудовании

По умолчанию программа Embedded Coder ® создает автономные исполняемые программы, для которых не требуется внешняя исполнительная или операционная система реального времени. Автономная программа требует минимальной модификации для адаптации к целевым аппаратным средствам. Архитектура автономной программы поддерживает выполнение моделей с единичной или множественной частотой дискретизации.

Создание автономной программы

Чтобы создать автономную программу, выполните следующие действия.

  1. Выберите параметр конфигурации модели Generate a example main program. При этом включается меню Целевая операционная система.

  2. В меню Целевая операционная система выберите BareBoardExample.

  3. Создать код.

Для многоскоростных моделей генерируется различный код в зависимости от следующих факторов:

  • Выполняется ли модель в однозадачном или многозадачном режиме.

  • Создается ли повторно используемый код.

Эти факторы могут влиять на алгоритмы планирования, используемые в сгенерированном коде, и в некоторых случаях влиять на API для функций точки входа модели. Эти варианты рассматриваются в следующих разделах.

Автономные компоненты программы

Ядром автономной программы является основной цикл. В каждой итерации основной цикл выполняет фоновую или нулевую задачу и проверяет условие завершения.

Основная петля периодически прерывается таймером. Функция rt_OneStep устанавливается в качестве подпрограммы обслуживания прерываний таймера (ISR) или вызывается из ISR таймера на каждом шаге синхронизации.

Драйвер выполнения, rt_OneStep, последовательности вызовов model_step функции. Работа rt_OneStep различается в зависимости от того, является ли генерирующая модель односкоростной или многоскоростной. В односкоростной модели rt_OneStep просто вызывает model_step функция. В многоскоростной модели rt_OneStep определяет приоритеты и планирует выполнение блоков в соответствии со скоростью, с которой они выполняются.

Основная программа

Обзор работы

Следующий псевдокод показывает выполнение основной программы.

main()
{
  Initialization (including installation of rt_OneStep as an 
    interrupt service routine for a real-time clock)
  Initialize and start timer hardware
  Enable interrupts
  While(not Error) and (time < final time)
    Background task
  EndWhile
  Disable interrupts (Disable rt_OneStep from executing)
  Complete any background tasks
  Shutdown
}

Псевдокод является конструкцией программы кабельных трасс для управления моделью. Основная программа лишь частично реализует эту конструкцию. Необходимо изменить его в соответствии со своими спецификациями.

Рекомендации по изменению основной программы

В этом разделе описываются минимальные изменения, которые необходимо внести в производственную версию основного программного модуля для реализации программы кабельных трасс.

  1. Звонить model_initialize.

  2. Инициализация целевых структур данных и аппаратных средств, таких как ADC или DAC.

  3. Установить rt_OneStep в качестве таймера ISR.

  4. Инициализация оборудования таймера.

  5. Включите прерывания таймера и запустите таймер.

    Примечание

    rtModel находится в недопустимом состоянии до model_initialize был вызван. Обслуживание прерываний таймера не должно начинаться до model_initialize был вызван.

  6. Дополнительно можно вставить фоновые вызовы задач в основной цикл.

  7. По окончании основного контура (если применимо):

    • Деактивизация прерываний таймера.

    • Выполните очистку целевого объекта, например обнуление DAC.

    • Обнаружение и обработка ошибок. Обратите внимание, что даже если программа предназначена для работы в течение неопределенного времени, может потребоваться обработка серьезных ошибок, таких как перебои по таймеру.

      Можно использовать макросы rtmGetErrorStatus и rtmSetErrorStatus для обнаружения и сигнализации ошибок.

rt_OneStep и планирование

Обзор работы

Работа rt_OneStep зависит от

  • Является ли модель односкоростной или многоскоростной. В односкоростной модели время выборки всех блоков в модели и фиксированный размер шага модели одинаковы. Модель, в которой время выборки и размер шага не соответствуют этим условиям, называется многоскоростной.

  • Режим решателя модели (SingleTasking против MultiTasking)

Разрешенные режимы решателя для встроенных целевых файлов системы реального времени суммирует разрешенные режимы решателя для односкоростных и многоскоростных моделей. Обратите внимание, что только для односкоростной модели SingleTasking разрешен режим решателя.

Разрешенные режимы решателя для встроенных целевых файлов системы реального времени

СпособОдноскоростнойМультиуровень

SingleTasking

Позволенный

Позволенный

MultiTasking

Отвергнутый

Позволенный

Auto

Позволенный

(по умолчанию: SingleTasking)

Позволенный

(по умолчанию: MultiTasking)

Созданный код для rt_OneStep (и связанные структуры временных данных и функции поддержки) адаптируется к количеству скоростей в модели и к режиму решателя. В следующих разделах рассматривается каждый возможный случай.

Односкоростная однозадачная операция

Единственным допустимым режимом решателя для односкоростной модели является SingleTasking. Такие модели работают в режиме «одной скорости».

Следующий псевдокод показывает дизайн rt_OneStep в односкоростной программе.

rt_OneStep()
{
  Check for interrupt overflow or other error
  Enable "rt_OneStep" (timer) interrupt
  Model_Step()  -- Time step combines output,logging,update
}

Для односкоростного варианта создается model_step функция -

void model_step(void)

Односкоростной rt_OneStep предназначен для выполнения model_step в течение одного периода времени. Чтобы применить это ограничение синхронизации, rt_OneStep поддерживает и проверяет флаг переполнения таймера. При вводе прерывания таймера отключаются до тех пор, пока не будет проверен флаг переполнения и другие условия ошибки. Если флаг переполнения снят, rt_OneStep устанавливает флаг и продолжает работу с включенными прерываниями таймера.

Флаг переполнения снимается только при успешном возврате из model_step. Следовательно, если rt_OneStep повторно прерывается перед завершением model_step, повторное прерывание обнаруживается посредством флага переполнения.

Повторное прерывание rt_OneStep таймером является условием ошибки. При обнаружении этого состояния rt_OneStep сигнализирует об ошибке и немедленно возвращается. (Обратите внимание, что это поведение можно изменить, если требуется обработать условие по-другому.)

Обратите внимание, что конструкция rt_OneStep предполагает, что прерывания отключены до rt_OneStep вызывается. rt_OneStep должен быть бесперебойным до тех пор, пока не будет проверен флаг переполнения прерывания.

Многоскоростная многозадачная работа

В многоскоростной многозадачной системе генерация кода использует приоритетную, упреждающую многозадачную схему для выполнения различных скоростей выборки в модели.

Следующий псевдокод показывает дизайн rt_OneStep в многоскоростной многозадачной программе.

rt_OneStep()
{
  Check for base-rate interrupt overrun
  Enable "rt_OneStep" interrupt
  Determine which rates need to run this time step

  Model_Step0()        -- run base-rate time step code

  For N=1:NumTasks-1   -- iterate over sub-rate tasks
    If (sub-rate task N is scheduled)
    Check for sub-rate interrupt overrun
      Model_StepN()    -- run sub-rate time step code
    EndIf
  EndFor
}

Идентификаторы задач.  Выполнение блоков, имеющих различные частоты дискретизации, разбивается на задачи. Каждому блоку, который выполняется с заданной частотой дискретизации, назначается идентификатор задачи (tid), которая связывает его с задачей, которая выполняется с такой скоростью. Где есть NumTasks задачи в системе, диапазон идентификаторов задач - 0..NumTasks-1.

Определение приоритетов задач базовой и субскоростной скорости.  Задачи определяются в порядке убывания по скорости. Задача базовой скорости - это задача, которая выполняется с самой высокой скоростью в системе (тактовая частота аппаратных средств). Задача базовой скорости имеет наивысший приоритет (tid 0). Следующая самая быстрая задача (tid 1) имеет следующий наивысший приоритет и так далее до самой медленной задачи с наименьшим приоритетом (tid NumTasks-1).

Более медленные задачи, выполняющиеся с кратной базовой скоростью, называются субскоростными задачами.

Группировка тарифов и специфичные для тарифов функции model_step.  В односкоростной модели вычисления блочного выхода выполняются в пределах одной функции. model_step. Для многоскоростных многозадачных моделей генератор кода пытается использовать другую стратегию. Эта стратегия называется группировкой тарифов. Группировка тарифов создает отдельные model_step функции для задачи базовой скорости и каждой подскоростной задачи в модели. Соглашение об именовании функций для этих функций:

model_stepN

где N является идентификатором задачи. Например, для модели с именем my_model , который имеет три скорости, создаются следующие функции:

void my_model_step0 (void);
void my_model_step1 (void);
void my_model_step2 (void);

Каждый model_stepN функция выполняет совместное использование блоков tid N; другими словами, блочный код, который выполняется в рамках задачи N группируется в связанные model_stepN функция.

Планирование выполнения model_stepN.  На каждом тике часов, rt_OneStep поддерживает счетчики планирования и флаги событий для каждой подчиненной задачи. Счетчики реализованы как taskCounter массивы, индексированные на tid. Флаги событий реализуются как массивы, индексированные на tid.

Ведение счетчиков планирования и флагов задач для субскоростей выполняется rt_OneStep. Счетчики планирования в основном являются делителями тактовой частоты, которые подсчитывают период выборки, связанный с каждой задачей подскоростной передачи. Пара задач, которые обмениваются данными, поддерживает флаг взаимодействия с более высокой скоростью. Флаги взаимодействия задач указывают, что запланированы быстрые и медленные задачи.

Флаги событий указывают, планируется ли выполнение данной задачи. rt_OneStep поддерживает флаги событий на основе счетчика задач, который поддерживается кодом в основном программном модуле для модели. Когда счетчик указывает, что период выборки задачи истек, основной код устанавливает флаг события для этой задачи.

При каждом вызове, rt_OneStep обновляет свои структуры данных планирования и выполняет задачу базовой скорости (rt_OneStep требования model_step0 поскольку задача базовой скорости должна выполняться на каждом шаге синхронизации). Затем, rt_OneStep выполняет итерации над флагами планирования в tid порядок, безусловный вызов model_stepN для любой задачи, флаг которой установлен. Задачи выполняются в порядке приоритета.

Преимущественное освобождение.  Обратите внимание, что конструкция rt_OneStep предполагает, что прерывания отключены до rt_OneStep вызывается. rt_OneStep должен быть бесперебойным до тех пор, пока не будет проверен флаг переполнения прерывания базовой скорости (см. псевдокод выше).

Массив флагов событий и переменные цикла, используемые rt_OneStep хранятся в виде локальных (стековых) переменных. Поэтому rt_OneStep повторно входит. Если rt_OneStep прерывается, задачи с более высоким приоритетом прерывают задачи с более низким приоритетом. По возвращении из прерывания задачи с более низким приоритетом возобновляются в ранее запланированном порядке.

Обнаружение переполнения.  Мультиуровень rt_OneStep также поддерживает массив флагов переполнения таймера. rt_OneStep обнаруживает переполнение таймера для каждой задачи по той же логике, что и односкоростной rt_OneStep.

Примечание

Если вы разработали многоскоростные S-функции или используете настроенный статический основной программный модуль, см. раздел Проблемы соответствия и совместимости группирования тарифов для получения информации о том, как адаптировать код для совместимости группирования тарифов. Эта адаптация позволяет многоскоростным многозадачным моделям создавать более эффективный код.

Многоскоростная однозадачная операция

В многоскоростной однозадачной программе, по определению, время выборки в модели должно быть целым кратным размеру фиксированного шага модели.

В многоскоростной однозадачной программе блоки выполняются с разными скоростями, но с одним и тем же идентификатором задачи. Работа rt_OneStep, в этом случае является упрощенной версией многозадачной работы. Группировка тарифов не используется. Единственной задачей является задача базовой скорости. Поэтому только один model_step генерируется функция:

void model_step(void)

На каждом тике часов, rt_OneStep проверяет флаг переполнения и вызовы model_step. Функция планирования для многоскоростной однозадачной программы rate_scheduler (а не rate_monotonic_scheduler). Планировщик поддерживает счетчики планирования для каждого такта. Существует один счетчик для каждой частоты выборки в модели. Счетчики реализованы в массиве (индексированном на tid) в пределах Timing структура в пределах rtModel.

Счетчики представляют собой делители тактовой частоты, которые подсчитывают период выборки, связанный с каждой задачей подскоростной передачи. Когда счетчик указывает, что период выборки для данной скорости истек, rate_scheduler очищает счетчик. Это условие указывает, что блоки, работающие на этой скорости, должны выполняться при следующем вызове model_step, который отвечает за проверку счетчиков.

Рекомендации по изменению rt_OneStep

rt_OneStep не требует значительных изменений. Единственное необходимое изменение состоит в повторном включении прерываний после проверки флагов переполнения и условий ошибок. Если применимо, следует также

  • Сохранение и восстановление контекста FPU при входе и выходе в rt_OneStep.

  • Задание входных данных модели, связанных с базовой скоростью перед вызовом model_step0.

  • Получение выходных данных модели, связанных с базовой скоростью после вызова model_step0.

    Примечание

    При изменении rt_OneStep для считывания значения из порта непрерывного вывода после каждого шага модели базовой скорости см. соответствующее предостерегающее руководство ниже.

  • В многоскоростной многозадачной модели перед вызовом задайте входные данные модели, связанные с подрейтами model_stepN в цикле субскоростей.

  • В многоскоростной многозадачной модели получить выходные данные модели, связанные с субрейтами, после вызова model_stepN в цикле субскоростей.

Комментарии в rt_OneStep укажите место для добавления кода.

В многоскоростном режиме rt_OneStep, вы можете повысить производительность, развернув for и while петли.

Кроме того, можно изменить поведение переполнения, чтобы продолжить выполнение после завершения восстановления после ошибки.

Соблюдайте также следующие предостерегающие указания:

  • Не следует изменять способ установки счетчиков, флагов событий или других структур временных данных в rt_OneStepили в функциях, вызываемых из rt_OneStep. rt_OneStep структуры временных данных (включая rtModel) и логика имеют решающее значение для работы генерируемой программы.

  • Если вы настроили основной программный модуль для считывания выходных данных модели после каждого шага модели базовой скорости, помните, что выбор параметров конфигурации модели Поддержка: непрерывное время и функция единого вывода/обновления вместе могут привести к считыванию выходных значений из main чтобы порт непрерывного вывода немного отличался от соответствующих выходных значений в записанных данных модели. Это связано с тем, что, хотя записанные данные являются снимком выходных данных на основных этапах времени, выходные данные считываются из main после шага модели базовой скорости потенциально отражает промежуточные второстепенные временные шаги. Чтобы устранить расхождение, либо разделите сгенерированные функции вывода и обновления (очистите параметр Single output/update function), либо установите блок удержания нулевого порядка перед портом непрерывного вывода.

  • Можно наблюдать несоответствие между результатами моделирования и записанными в журнал результатами MAT из сгенерированного кода, если не устанавливать входные данные модели перед каждым вызовом функции шага модели. В созданном примере основной программы в следующих комментариях показаны расположения для настройки входных данных и пошаговой настройки модели с помощью кода:

    /* Set model inputs here */
    /* Step the model */

    Если в модели применяется повторное использование сигнала и используется MatFileLogging для сравнения результатов моделирования с сгенерированным кодом, изменить rt_OneStep для записи входных данных модели на каждом временном шаге в соответствии с этими комментариями. Кроме того, для проверки можно выбрать SIL или PIL-подход.

Статический основной программный модуль

Обзор

В большинстве случаев наиболее простой стратегией развертывания сгенерированного кода является использование опции Создать пример основной программы для создания ert_main.c или .cpp (см. раздел Создание автономной программы).

Однако при отключении опции Создать пример основной программы можно использовать статический основной модуль в качестве примера или шаблона для разработки встроенных приложений. Статические основные модули, поставляемые MathWorks ®, включают:

  • matlabroot/rtw/c/src/common/rt_main.c - Опоры Nonreusable function упаковка интерфейса кода.

  • matlabroot/rtw/c/src/common/rt_malloc_main.c - Опоры Reusable function упаковка интерфейса кода. Необходимо выбрать параметр конфигурации модели Использовать динамическое выделение памяти для инициализации модели и задать параметр Pass root-level I/O как Part of model data structure.

  • matlabroot/rtw/c/src/common/rt_cppclass_main.cpp - Опоры C++ class упаковка интерфейса кода.

Статический основной модуль не является частью генерируемого кода; он предоставляется в качестве основы для пользовательских изменений и для использования в моделировании. Если существующие приложения зависят от статики ert_main.c (разработаны в версиях до R2012b), rt_main.c, rt_malloc_main.c, или rt_cppclass_main.cpp, возможно, потребуется продолжить использование статического основного программного модуля.

При разработке приложений с использованием статического основного модуля необходимо скопировать модуль в рабочую папку и переименовать его перед внесением изменений. Например, можно переименовать rt_main.c кому model_rt_main.c. Кроме того, необходимо изменить файл шаблона или настройки цепочки инструментов таким образом, чтобы в процессе построения создавался соответствующий файл объекта, например model_rt_main.obj (в UNIX ® ,model_rt_main.o), в папке построения.

Статический основной модуль содержит

  • rt_OneStep, подпрограмму обслуживания прерываний таймера (ISR). rt_OneStep требования model_step для выполнения обработки в течение одного тактового периода модели.

  • Скелетное main функция. Как предусмотрено, main полезен только при моделировании. Необходимо изменить main для выполнения на основе прерываний в реальном времени.

Для односкоростных моделей - работа rt_OneStep и основные функции по существу те же в статическом основном модуле, что и в автоматически созданной версии, описанной в разделе Развертывание созданных автономных исполняемых программ на целевом оборудовании. Однако для многоскоростных многозадачных моделей статический и сгенерированный код немного отличаются. В следующем разделе описывается этот случай.

Группировка тарифов и статическая основная программа

Цели, основанные на цели ERT, иногда используют статический основной модуль и запрещают использование опции Создать пример основной программы. Это делается потому, что в статический основной модуль были добавлены изменения, специфичные для цели, и эти изменения не будут сохранены, если основная программа будет восстановлена.

Статический основной модуль может использовать или не использовать совместимую группировку тарифов model_stepN функции. Если основной модуль основан на статическом rt_main.c, rt_malloc_main.c, или rt_cppclass_main.cpp модуль, он не использует специфичный для скорости model_stepN вызовы функций. Он использует старый стиль model_step функция, передача идентификатора задачи:

void model_step(int_T tid);

По умолчанию, когда опция Создать пример основной программы отключена, цель ERT генерирует model_step «обертка» для многоскоростных многозадачных моделей. Целью обертки является сопряжение специфичных для скорости model_stepN функции для вызова старого стиля. Код оболочки отправляется в model_stepN вызов с помощью switch оператор, как в следующем примере:

void mymodel_step(int_T tid) /* Sample time:  */
{

  switch(tid) {
   case 0 :
    mymodel_step0();
    break;
   case 1 :
    mymodel_step1();
    break;
   case 2 :
    mymodel_step2();
    break;
   default :
    break;
  }
}

Следующий псевдокод показывает, как rt_OneStep требования model_step из статической основной программы в многоскоростной многозадачной модели.

rt_OneStep()
{
  Check for base-rate interrupt overflow
  Enable "rt_OneStep" interrupt
  Determine which rates need to run this time step

  ModelStep(tid=0)     --base-rate time step

  For N=1:NumTasks-1  -- iterate over sub-rate tasks
    Check for sub-rate interrupt overflow
    If (sub-rate task N is scheduled)
      ModelStep(tid=N)    --sub-rate time step
    EndIf
  EndFor
}

Можно использовать переменную TLC RateBasedStepFcn чтобы указать, что создаются только функции шага на основе скорости без функции оболочки. Если цель вызывает совместимую группировку тарифов model_stepN функция непосредственно, установка RateBasedStepFcn кому 1. В этом случае функция обертки не генерируется.

Необходимо установить RateBasedStepFcn до %include "codegenentry.tlc" в системном целевом файле. Кроме того, можно задать RateBasedStepFcn в вашем target_settings.tlc файл.

Изменение статической основной программы

Как и в случае созданного ert_main.c или .cpp, вы должны внести несколько изменений в основной цикл и rt_OneStep. См. раздел Рекомендации по изменению основной программы и рекомендации по изменению rt_OneStep.

Кроме того, следует заменить rt_OneStep вызов в основном цикле с вызовом фоновой задачи или оператором null.

Другие изменения, которые могут потребоваться:

  • Если применимо, следуйте комментариям в коде относительно того, куда добавить код для чтения/записи модели ввода/вывода и сохранения/восстановления контекста FPU.

    Примечание

    При изменении rt_main.c, rt_malloc_main.c, или rt_cppclass_main.cpp для считывания значения из порта непрерывного вывода после каждого шага модели базовой скорости см. соответствующие предостерегающие рекомендации в Руководстве по изменению rt_OneStep.

  • Если опция Создать пример основной программы отключена, rtmodel.h генерируется для обеспечения интерфейса между основным модулем и сгенерированным кодом модели. При создании собственного статического основного программного модуля обычно включается rtmodel.h.

    Кроме того, можно подавить создание rtmodel.h, и включить model.h непосредственно в основном модуле. Подавление генерации rtmodel.h, используйте следующую инструкцию в системном целевом файле:

    %assign AutoBuildProcedure = 0
  • Если опция Требуется завершить функцию (Terminate function required) снята, удалите или прокомментируйте следующее в вашей производственной версии rt_main.c, rt_malloc_main.c, или rt_cppclass_main.cpp:

    • #if TERMFCN... проверка ошибок во время компиляции

    • Вызов MODEL_TERMINATE

  • Для rt_main.c (неприменимо к rt_cppclass_main.cppЕсли вы не хотите объединять функции вывода и обновления, очистите параметр конфигурации модели Single output/update function и внесите следующие изменения в вашу производственную версию rt_main.c:

    • Заменить вызовы на MODEL_STEP с вызовами к MODEL_OUTPUT и MODEL_UPDATE.

    • Удалить #if ONESTEPFCN... проверка ошибок.

  • Статическое rt_main.c модуль не поддерживает Reusable function упаковка интерфейса кода. Следующая проверка ошибок вызывает ошибку времени компиляции, если Reusable function кодовая упаковка интерфейса используется незаконно.

    #if MULTI_INSTANCE_CODE==1

Изменение статической магистрали для назначения и доступа к данным экземпляра модели

Если используется статический основной программный модуль, и модель настроена для Reusable function пакет интерфейса кода, но параметр конфигурации модели Использовать динамическое выделение памяти для инициализации модели очищен, данные экземпляра модели должны назначаться статически или динамически вызывающим основным кодом. Указатели на отдельные структуры данных модели (такие как Block IO, DWork и Parameters) должны быть настроены в структуре данных модели верхнего уровня в режиме реального времени.

Для поддержки основных модификаций процесс сборки генерирует подмножество следующих макросов модели в реальном времени (RTM) на основе требований к данным модели в model.h.

Синтаксис макроса RTMОписание
rtmGetBlockIO(rtm)

Получение блочной структуры данных ввода-вывода

rtmSetBlockIO(rtm,val)

Установка блочной структуры данных ввода-вывода

rtmGetContStates(rtm)

Получение структуры данных о непрерывных состояниях

rtmSetContStates(rtm,val)

Установка структуры данных непрерывных состояний

rtmGetDefaultParam(rtm)

Получение структуры данных параметров по умолчанию

rtmSetDefaultParam(rtm,val)

Установка структуры данных параметров по умолчанию

rtmGetPrevZCSigState(rtm)

Получение предыдущей структуры данных о состоянии сигнала пересечения нуля

rtmSetPrevZCSigState(rtm,val)

Установка предыдущей структуры данных о состоянии сигнала пересечения нуля

rtmGetRootDWork(rtm)

Получение структуры данных DWork

rtmSetRootDWork(rtm,val)

Установка структуры данных DWork

rtmGetU(rtm)

Получение корневой структуры входных данных (когда корневые входные данные передаются как часть структуры данных модели)

rtmSetU(rtm,val)

Установка корневой структуры входных данных (когда корневые входные данные передаются как часть структуры данных модели)

rtmGetY(rtm)

Получить структуру данных корневых выходов (когда корневые выходы передаются как часть структуры данных модели)

rtmSetY(rtm,val)

Установка структуры данных корневых выходов (когда корневые выходы передаются как часть структуры данных модели)

Эти макросы используются в статической основной программе для доступа к отдельным структурам данных модели в структуре данных RTM. Например, предположим, что пример модели rtwdemo_reusable сконфигурирован с Reusable function упаковка интерфейса кода, Использование динамического выделения памяти для инициализации модели очищено, Передача ввода-вывода на корневом уровне, если установлено значение Individual argumentsи параметр панели оптимизации Удалить нулевую инициализацию ввода-вывода корневого уровня. При построении модели создаются следующие структуры данных модели и точки входа модели в rtwdemo_reusable.h:

/* Block states (auto storage) for system '<Root>' */
typedef struct {
  real_T Delay_DSTATE;                 /* '<Root>/Delay' */
} D_Work;

/* Parameters (auto storage) */
struct Parameters_ {
  real_T k1;                           /* Variable: k1
                                        * Referenced by: '<Root>/Gain'
                                        */
};

/* Model entry point functions */
extern void rtwdemo_reusable_initialize(RT_MODEL *const rtM, real_T *rtU_In1,
  real_T *rtU_In2, real_T *rtY_Out1);
extern void rtwdemo_reusable_step(RT_MODEL *const rtM, real_T rtU_In1, real_T
  rtU_In2, real_T *rtY_Out1);

Кроме того, если параметр конфигурации модели Generate a example main program не выбран для модели, rtwdemo_reusable.h содержит определения для макросов RTM rtmGetDefaultParam, rtmsetDefaultParam, rtmGetRootDWork, и rtmSetRootDWork.

Также, для справки, сгенерированные rtmodel.h файл содержит пример определения параметра с начальными значениями (неисполнительный код):

#if 0

/* Example parameter data definition with initial values */
static Parameters rtP = {
  2.0                                  /* Variable: k1
                                        * Referenced by: '<Root>/Gain'
                                        */
};                                     /* Modifiable parameters */

#endif

В разделе определений статического основного файла можно использовать следующий код для статического распределения структур данных модели в реальном времени и аргументов для rtwdemo_reusable модель:

static RT_MODEL rtM_;
static RT_MODEL *const rtM = &rtM_;    /* Real-time model */
static Parameters rtP = {
  2.0                                  /* Variable: k1
                                        * Referenced by: '<Root>/Gain'
                                        */
};                                     /* Modifiable parameters */

static D_Work rtDWork;                 /* Observable states */

/* '<Root>/In1' */
static real_T rtU_In1;

/* '<Root>/In2' */
static real_T rtU_In2;

/* '<Root>/Out1' */
static real_T rtY_Out1;

В основной функции можно использовать следующие вызовы макросов RTM для настройки параметров модели и данных DWork в структуре данных модели в реальном времени:

int_T main(int_T argc, const char *argv[])
{
...
/* Pack model data into RTM */

rtmSetDefaultParam(rtM, &rtP);
rtmSetRootDWork(rtM, &rtDWork);

/* Initialize model */
rtwdemo_reusable_initialize(rtM, &rtU_In1, &rtU_In2, &rtY_Out1);
...
}

Используйте аналогичный подход для настройки нескольких экземпляров данных модели, где структура данных модели в реальном времени для каждого экземпляра имеет свои собственные данные. В частности, структура параметров (rtP) должны быть инициализированы для каждого экземпляра до требуемых значений либо статически как часть rtP определение данных или во время выполнения.

Проблемы с соответствием и совместимостью группирования тарифов

Совместимость основной программы

Если опция Generate an example main program (Создать пример основной программы) отключена, при создании кода создается несколько другая модификационная константа тарифа для совместимости со старой статикой. ert_main.c модуль. Для получения дополнительной информации см. раздел Группирование тарифов и статическая основная программа.

Обеспечение соответствия группировки S-функций

Встроенные блоки Simulink ®, а также блоки DSP System Toolbox™ соответствуют требованиям для создания групповых кодов тарифов. Однако написанные пользователем многоскоростные встроенные S-функции могут быть несовместимы с группировкой скоростей. Несоответствующие блоки генерируют менее эффективный код, но в остальном совместимы с группировкой скоростей. Чтобы в полной мере воспользоваться преимуществами группирования тарифов, необходимо обновить многоскоростные встроенные S-функции, чтобы они полностью соответствовали группированию тарифов. Необходимо обновить реализации S-функций TLC, как описано в этом разделе.

Использование несоответствующих многоскоростных блоков для формирования кода группирования скорости генерирует мертвый код. Это может вызвать две проблемы:

  • Снижение эффективности кода.

  • Предупреждающие сообщения, выдаваемые во время компиляции. Такие предупреждения возникают, когда мертвый код ссылается на временные переменные перед инициализацией. Поскольку мертвый код не выполняется, эта проблема не влияет на поведение сгенерированного кода во время выполнения.

Для обеспечения совместимости группировки тарифов S-функций можно использовать следующие функции TLC для генерации ModelOutputs и ModelUpdate код, соответственно:

OutputsForTID(block, system, tid)
UpdateForTID(block, system, tid)

Нижеприведенные списки кодов иллюстрируют генерацию выходных вычислений без группировки тарифов (листинг 1) и с группировкой тарифов (листинг 2). Обратите внимание на следующее:

  • tid аргумент является идентификатором задачи (0..NumTasks-1).

  • Только код, охраняемый tid передано в OutputsForTID генерируется. if (%<LibIsSFcnSampleHit(portName)>) тест не используется в OutputsForTID.

  • При создании модификационной константы тарифа OutputsForTID и/или UpdateForTID вызывается во время генерации кода. При создании нескоростной модификационной константы Outputs и/или Update вызывается.

  • В коде, совместимом с группировкой тарифов, верхний уровень Outputs и/или Update вызов функций OutputsForTID и/или UpdateForTID функции для каждой скорости (tid) участвует в блоке. Код, возвращенный OutputsForTID и/или UpdateForTID должны охраняться соответствующим tid охрана:

    if (%<LibIsSFcnSampleHit(portName)>)

    как в Листинге 2.

Список 1: Вывод генерации кода без группировки ставок

%% multirate_blk.tlc

%implements "multirate_blk" "C"


%% Function: mdlOutputs =====================================================
%% Abstract:
%%
%%  Compute the two outputs (input signal decimated by the
%%  specified parameter). The decimation is handled by sample times.
%%  The decimation is only performed if the block is enabled.
%%  Each port has a different rate.
%%
%%  Note, the usage of the enable should really be protected such that
%%  each task has its own enable state. In this example, the enable
%% occurs immediately which may or may not be the expected behavior.
%%
  %function Outputs(block, system) Output
  /* %<Type> Block: %<Name> */
  %assign enable = LibBlockInputSignal(0, "", "", 0)
  {
    int_T *enabled = &%<LibBlockIWork(0, "", "", 0)>;

    %if LibGetSFcnTIDType("InputPortIdx0") == "continuous"
      %% Only check the enable signal on a major time step.
      if (%<LibIsMajorTimeStep()> && ...
                           %<LibIsSFcnSampleHit("InputPortIdx0")>) {
        *enabled = (%<enable> > 0.0);
      }
    %else
      if (%<LibIsSFcnSampleHit("InputPortIdx0")>) {
        *enabled = (%<enable> > 0.0);
      }
    %endif

    if (*enabled) {
      %assign signal = LibBlockInputSignal(1, "", "", 0)
      if (%<LibIsSFcnSampleHit("OutputPortIdx0")>) {
        %assign y = LibBlockOutputSignal(0, "", "", 0)   
        %<y> = %<signal>;
      }
      if (%<LibIsSFcnSampleHit("OutputPortIdx1")>) {
        %assign y = LibBlockOutputSignal(1, "", "", 0)
        %<y> = %<signal>;
      }
    }
  }

  %endfunction
%% [EOF] sfun_multirate.tlc

Список 2: Вывод генерации кода с группированием ставок

%% example_multirateblk.tlc

%implements "example_multirateblk" "C"


  %% Function: mdlOutputs =====================================================
  %% Abstract:
  %%
  %% Compute the two outputs (the input signal decimated by the
  %% specified parameter). The decimation is handled by sample times.
  %% The decimation is only performed if the block is enabled.  
  %% All ports have different sample rate.
  %%
  %% Note: the usage of the enable should really be protected such that
  %% each task has its own enable state. In this example, the enable
  %% occurs immediately which may or may not be the expected behavior.
  %%
  %function Outputs(block, system) Output



  %assign portIdxName = ["InputPortIdx0","OutputPortIdx0","OutputPortIdx1"]
  %assign portTID     = [%<LibGetGlobalTIDFromLocalSFcnTID("InputPortIdx0")>, ...
                        %<LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx0")>, ...
                        %<LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx1")>]
  %foreach i = 3
    %assign portName = portIdxName[i]
    %assign tid      = portTID[i]
    if (%<LibIsSFcnSampleHit(portName)>) {
                       %<OutputsForTID(block,system,tid)>
    }
  %endforeach

  %endfunction

  %function OutputsForTID(block, system, tid) Output
  /* %<Type> Block: %<Name> */
  %assign enable = LibBlockInputSignal(0, "", "", 0)  
  %assign enabled = LibBlockIWork(0, "", "", 0)  
  %assign signal = LibBlockInputSignal(1, "", "", 0)

  %switch(tid)
    %case LibGetGlobalTIDFromLocalSFcnTID("InputPortIdx0") 
                         %if LibGetSFcnTIDType("InputPortIdx0") == "continuous"
                           %% Only check the enable signal on a major time step.
                           if (%<LibIsMajorTimeStep()>) {  
                             %<enabled> = (%<enable> > 0.0);
                           }
                         %else
                           %<enabled> = (%<enable> > 0.0);
                         %endif
                         %break
    %case LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx0") 
                         if (%<enabled>) {
                           %assign y = LibBlockOutputSignal(0, "", "", 0)
                           %<y> = %<signal>;
                         }
                         %break
    %case LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx1") 
                         if (%<enabled>) {
                           %assign y = LibBlockOutputSignal(1, "", "", 0)
                           %<y> = %<signal>;
                         }
                         %break
    %default 
                         %% error it out
  %endswitch

  %endfunction

%% [EOF] sfun_multirate.tlc

Создать код, который удаляет данные из литерального адреса памяти

В этом примере показано, как создать код, считывающий значение сигнала, путем отмены ссылки на указанный адрес памяти. С помощью этой техники можно сгенерировать алгоритм управления, взаимодействующий с памятью, которую заполняет аппаратное обеспечение (например, память, которая хранит выход аналого-цифрового преобразователя в микроконтроллере).

В этом примере создается алгоритм, который получает входные данные из 16-разрядного блока памяти по адресу 0x8675309. Предположим, что аппаратное устройство асинхронно заполняет только нижние 10 битов адреса. Алгоритм должен рассматривать адрес как доступный только для чтения (const), волатильный (volatile) данных и игнорировать верхние 6 битов адреса.

Созданный код может получить доступ к данным, определив макрос, который отменяет ссылки 0x8675309 и маскирует ненужные биты:

#define A2D_INPUT ((*(volatile const uint16_T *)0x8675309)&0x03FF)

Чтобы настроить модель для создания кода, определяющего и использующего этот макрос, выполните следующие действия.

  1. Создайте пример модели.

  2. Создание пакета, содержащего определения класса данных и класса хранения.

  3. Создайте класс хранения с помощью конструктора пользовательских классов хранения.

  4. Определите класс для хранения параметров свойств класса хранения.

  5. Запись кода компилятора целевого языка (TLC), который выдает правильный код C для класса хранения.

  6. Определите класс для данных ввода.

  7. Загрузите пакет в словарь встроенного кодера.

  8. Настройте вход на корневом уровне модели для использования класса хранения.

  9. Создайте и проверьте код.

Пример использования конструктора пользовательских классов хранения без записи кода TLC см. в разделе Создание и применение класса хранения.

В качестве альтернативы написанию кода TLC можно использовать разделы памяти для создания кода, включающего прагматики. В зависимости от цепочки инструментов построения можно использовать прагматики для указания адреса литеральной памяти для хранения глобальной переменной. Дополнительные сведения о разделах памяти см. в разделе Управление данными и размещением функций в памяти путем вставки прагматиков.

Деривация синтаксиса макроса

В этом примере генерируемый код настраивается для определения и использования макроса отмены ссылки. Чтобы определить правильный синтаксис макроса, начните с записи целевого адреса.

0x8675309

Приведите адрес как указатель на 16-битное целое число. Использовать имя типа данных Simulink Coder uint16_T.

(uint16_T *)0x8675309

Добавление квалификатора типа склада const так как созданный код не должен записываться на адрес. Добавить volatile поскольку аппаратные средства могут заполнять адрес в произвольное время.

(volatile const uint16_T *)0x8675309

Отмените привязку адреса.

*(volatile const uint16_T *)0x8675309

После операции отмены привязки примените маску, чтобы сохранить только 10 битов, которые заполняет оборудование. Используйте явные круглые скобки для управления порядком операций.

(*(volatile const uint16_T *)0x8675309)&0x03FF

В качестве безопасной практики кодирования оберните всю конструкцию в другой слой скобок.

((*(volatile const uint16_T *)0x8675309)&0x03FF)

Создание модели-примера

Создание модели-примера ex_memmap_simple.

Для блока Inport установите тип выходных данных в uint16. Присвойте сигналу имя A2D_INPUT. Inport блок и сигнальная линия представляют данные, которые заполняются аппаратными средствами.

Для блока усиления установите тип выходных данных равным double.

Создание пакета, содержащего определения класса данных и класса хранения

В текущей папке создайте папку с именем +MemoryMap. Папка определяет пакет с именем MemoryMap.

Чтобы сделать пакет доступным для использования вне текущей папки, можно добавить папку, содержащую +MemoryMap к пути MATLAB.

Создать класс хранения

Создание кода, определяющего и считывающего A2D_INPUT в качестве макроса необходимо создать класс хранения, который можно применить к входу на корневом уровне модели. Позже будет создан TLC-код, дополняющий класс хранения.

Откройте конструктор пользовательских классов хранения в расширенном режиме. Чтобы создать класс хранения, который работает с помощью пользовательского кода TLC, необходимо использовать расширенный режим.

cscdesigner('MemoryMap','-advanced');

В конструкторе пользовательских классов хранения нажмите кнопку Создать. Новый пользовательский класс хранения, NewCSC_1, появляется в списке определений классов хранения.

Переименование класса хранения в MemoryMappedAddress.

Для MemoryMappedAddress, на вкладке Общие установите:

  • Введите для Other. Класс хранения может работать с помощью пользовательского кода TLC, который будет записан позже.

  • Область данных для Exported. Для элементов данных, использующих этот пользовательский класс хранения, Simulink Coder генерирует определение (например, #define оператор, определяющий макрос).

  • Инициализация данных в None. Кодер Simulink не генерирует код, инициализирующий элемент данных. Используйте этот параметр, поскольку этот пользовательский класс хранения представляет данные, доступные только для чтения. Вы не выбираете Macro поскольку конструктор классов настраиваемых хранилищ не позволяет использовать Macro для данных сигнала.

  • Файл определения для Specify (оставьте текстовое поле пустым). Для элементов данных, которые потребляют память в сгенерированном коде, в файле определения указывается .c исходный файл, выделяющий память. Этот класс хранения дает макрос, который не требует памяти. Как правило, заголовочные файлы (.h), не .c файлы, определите макросы. Установка для файла определения значения Specify вместо Instance specific запрещает пользователям класса хранения без необходимости указывать файл определения.

  • Заголовочный файл в Instance specific. Чтобы управлять размещением файла определения макроса, пользователь класса хранения должен указать файл заголовка для каждого элемента данных, использующего этот класс хранения.

  • Владелец для Specify (оставьте текстовое поле пустым). Владелец применяется только к элементам данных, которые потребляют память.

По завершении выбора параметров нажмите кнопку «Применить» и «Сохранить».

Теперь при применении класса хранения к элементу данных, например, к входу In1, можно указать файл заголовка, содержащий созданное определение макроса. Вы пока не можете указать адрес памяти для элемента данных. Чтобы включить спецификацию адреса памяти, создайте класс пользовательских атрибутов, который можно связать с MemoryMappedAddress класс хранения.

Определение параметров свойств класса для хранения для класса хранения

Определите класс MATLAB для хранения дополнительной информации для элементов данных, использующих класс хранения. В этом случае дополнительной информацией является адрес памяти.

В MemoryMap пакет ( +MemoryMap папка), создайте папку с именем @MemoryMapAttribs.

В @MemoryMapAttribs папка, создайте файл с именем MemoryMapAttribs. Файл определяет класс, производный от встроенного класса Simulink.CustomStorageClassAttributes.

classdef MemoryMapAttribs < Simulink.CustomStorageClassAttributes
    properties( PropertyType = 'char' )
        MemoryAddress = '';
    end
end

Позже этот класс MATLAB будет связан с MemoryMappedAddress класс хранения. Затем при применении класса хранения к элементу данных можно указать адрес памяти.

Запись кода TLC, выдающего правильный код C

Запись кода TLC, использующего атрибуты класса хранения данных, например HeaderFile и MemoryAddress, для генерации правильного кода C для каждого элемента данных.

В +MemoryMap папка, создайте папку с именем tlc.

Перейдите к новой папке.

Проверьте встроенный файл шаблона TLC, TEMPLATE_v1.tlc.

edit(fullfile(matlabroot,...
    'toolbox','rtw','targets','ecoder','csc_templates','TEMPLATE_v1.tlc'))

Сохранить копию TEMPLATE_v1.tlc в tlc папка. Переименование копии memory_map_csc.tlc.

В memory_map_csc.tlcнайдите часть, которая управляет генерацией объявлений данных C-кода.

    %case "declare"

      %% LibDefaultCustomStorageDeclare is the default declare function to
      %% declares a global variable whose identifier is the name of the data.
      %return "extern %<LibDefaultCustomStorageDeclare(record)>"
      %%break

    %% ==========================================================================

declare дело (%case) создает возвращаемое значение (%return), который генератор кода выдает в файл заголовка, указанный для каждого элемента данных. Для управления кодом C, который объявляет каждый элемент данных, настройте возвращаемое значение в declare дело.

Заменить существующий %case содержимое с этим новым кодом, который указывает другое возвращаемое значение:

%case "declare"

        %% In TLC code, a 'record' is a data item (for example, a signal line).
        %% 'LibGetRecordIdentifier' returns the name of the data item.
        %assign id = LibGetRecordIdentifier(record)

        %assign dt = LibGetRecordCompositeDataTypeName(record)

        %% The 'CoderInfo' property of a data item stores a
        %% 'Simulink.CoderInfo' object, which stores code generation settings
        %% such as the storage class or custom storage class that you specify
        %% for the item.
        %assign ci = record.Object.ObjectProperties.CoderInfo
        %% The 'ci' variable now stores the 'Simulink.CoderInfo' object.
        
        %% By default, the 'CustomAttributes' property of a 'Simulink.CoderInfo'
        %% object stores a 'Simulink.CustomStorageClassAttributes' object.
        %% This nested object stores specialized code generation settings
        %% such as the header file and definition file that you specify for
        %% the data item.
        %%
        %% The 'MemoryMap' package derives a new class, 
        %% 'MemoryMapAttribs', from 'Simulink.CustomStorageClassAttributes'.
        %% The new class adds a property named 'MemoryAddress'.
        %% This TLC code determines the memory address of the data item by
        %% acquiring the value of the 'MemoryAddress' property.
        %assign ca = ci.Object.ObjectProperties.CustomAttributes
        %assign address = ca.Object.ObjectProperties.MemoryAddress

        %assign width = LibGetDataWidth(record)

        %% This TLC code constructs the full macro, with correct C syntax,
        %% based on the values of TLC variables such as 'address' and 'dt'.
        %% This TLC code also asserts that the data item must be a scalar.
        %if width == 1
                %assign macro = ...
                    "#define %<id> ((*(volatile const %<dt>*)%<address>) & 0x03FF)"
        %else
                %error( "Non scalars are not supported yet." )
        %endif

        %return "%<macro>"
      %%break

    %% ==========================================================================

Новый код TLC использует встроенные документированные функции TLC, такие как LibGetRecordIdentifierи другие команды и операции TLC для доступа к информации о элементе данных. Временные переменные, такие как dt и address сохранить эту информацию. Код TLC создает полный макрос с правильным синтаксисом C путем расширения переменных и сохраняет макрос в переменной. macro.

В том же файле найдите часть, которая управляет созданием определений данных.

    %case "define"

      %% LibDefaultCustomStorageDefine is the default define function to define
      %% a global variable whose identifier is the name of the data.  If the
      %% data is a parameter, the definition is also statically initialized to
      %% its nominal value (as set in MATLAB).
      %return "%<LibDefaultCustomStorageDefine(record)>"
      %%break

    %% ==========================================================================

define случай выводит возвращаемое значение, которое генератор кода выдает в .c , определяющий элементы данных, которые потребляют память.

Заменить существующий %case содержимое с этим новым содержимым:

    %case "define"
      %return ""
      %%break

    %% ==========================================================================

MemoryMappedAddress вырабатывает макрос в сгенерированном коде, поэтому используется declare дело вместо define случай для построения и излучения макроса. Для предотвращения define В случае создания повторяющегося определения макроса новый код TLC возвращает пустую строку.

Найдите часть, которая управляет созданием кода, инициализирующего данные.

    %case "initialize"

      %% LibDefaultCustomStorageInitialize is the default initialization
      %% function that initializes a scalar element of a global variable to 0. 
      %return LibDefaultCustomStorageInitialize(record, idx, reim)
      %%break

    %% ==========================================================================

initialize case генерирует код, инициализирующий элементы данных (например, в model_initialize функция).

Заменить существующий %case содержимое с этим новым содержимым:

    %case "initialize"
      %return ""
      %%break

    %% ==========================================================================

MemoryMappedAddress выдает макрос, поэтому созданный код не должен пытаться инициализировать значение макроса. Новый код TLC возвращает пустую строку.

Завершение определения класса хранения

Ваш новый класс MATLAB, MemoryMapAttribs, позволяет пользователям класса хранения данных, MemoryMappedAddress, чтобы указать адрес памяти для каждого элемента данных. Чтобы разрешить эту спецификацию, свяжите MemoryMapAttribs с MemoryMappedAddress. Чтобы создать правильный код C на основе информации, указанной для каждого элемента данных, свяжите настроенный файл TLC, memory_map_csc.tlc, с MemoryMappedAddress.

Перейдите к папке, содержащей +MemoryMap папка.

Снова откройте конструктор классов настраиваемых хранилищ.

cscdesigner('MemoryMap','-advanced');

Для MemoryMappedAddress, на вкладке Другие атрибуты (Other Attributes) задайте:

  • Имя файла TLC для memory_map_csc.tlc.

  • Класс атрибутов CSC для MemoryMap.MemoryMapAttribs.

Щелкните Применить (Apply) и Сохранить (Save).

Определение класса данных сигнала

Чтобы применить класс хранения к элементу данных в модели, в MemoryMap пакет, необходимо создать класс MATLAB, производный от Simulink.Signal. При конфигурировании сигнала в модели вместо класса по умолчанию выбирается новый класс данных. Simulink.Signal.

В MemoryMap пакет, создайте папку с именем @Signal.

В @Signal папка, создайте файл с именем Signal.m.

classdef Signal < Simulink.Signal
    methods
        function setupCoderInfo( this )
            useLocalCustomStorageClasses( this, 'MemoryMap' );
            return;
        end
    end
end

Файл определяет класс с именем MemoryMap.Signal. Определение класса переопределяет setupCoderInfo метод, который Simulink.Signal класс уже реализует. Новая реализация определяет, что объекты MemoryMap.Signal класс использует пользовательские классы хранения данных из MemoryMap пакет (вместо классов хранения из Simulink пакет). При конфигурировании сигнала в модели путем выбора MemoryMap.Signal класс, можно выбрать новый пользовательский класс хранения, MemoryMappedAddress.

Загрузить пакет в словарь встроенного кодера

Перейдите в папку, содержащую пример модели, и откройте модель.

Откройте приложение Embedded Coder.

Откройте словарь встроенного кодера. На вкладке C Code выберите Code Interface > Embedded Coder Dictionary.

В словаре встроенного кодера щелкните Управление пакетами.

В диалоговом окне «Управление пакетом» нажмите кнопку «Обновить». По завершении обновления выберите пакет MemoryMap. Щелкните Загрузить (Load).

Закройте словарь встроенного кодера.

Настройка порта корневого уровня для использования класса хранения

В приложении Embedded Coder используйте редактор сопоставлений кода и инспектор свойств для настройки Inport In1 для использования определенного вами класса хранения.

Откройте редактор сопоставлений кода. На вкладке Код C выберите Интерфейс кода > Сопоставления кодов отдельных элементов.

В редакторе сопоставлений кодов на вкладке «Inports» выберите inport In1. Задать класс хранения для In1 кому MemoryMappedAddress.

В инспекторе свойств в группе «Код» задайте для свойства HeaseFile значение memory_mapped_addresses.h и свойство MemureAddress для 0x8675309.

Сохраните модель.

Создание и проверка кода

Создайте код из модели.

### Starting build procedure for: ex_memmap_simple
### Successful completion of build procedure for: ex_memmap_simple

Build Summary

Top model targets built:

Model             Action                       Rebuild Reason                                    
=================================================================================================
ex_memmap_simple  Code generated and compiled  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 12.194s

Проверка созданного файла заголовка memory_mapped_addresses.h. Файл определяет макрос A2D_INPUT, которая соответствует сигнальной линии в модели.

/* Declaration of data with custom storage class MemoryMappedAddress */
#define A2D_INPUT                      ((*(volatile const uint16_T*)0x8675309) & 0x03FF)

Проверка созданного файла ex_memmap_simple.c. Сгенерированный алгоритмический код (соответствующий блоку Gain) вычисляет выходной сигнал модели, rtY.Out1, путем работы на A2D_INPUT.

/* Model step function */
void step(void)
{
  /* Outport: '<Root>/Out1' incorporates:
   *  Gain: '<Root>/Gain'
   *  Inport: '<Root>/In1'
   */
  rtY.Out1 = 42.0 * (real_T)A2D_INPUT;
}

Связанные темы