Развертывание сгенерированных Независимых исполняемых файлов программ на Целевой компьютер

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

Сгенерируйте автономную программу

Чтобы сгенерировать автономную программу:

  1. Выберите параметр конфигурации модели Сгенерируйте пример основной программы. Это включает меню Target operating system.

  2. В Target operating system меню выберите 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. Инициализируйте целевые структуры данных и оборудование, такие как АЦП или DAC.

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

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

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

    Примечание

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

  6. При необходимости вставьте фоновые вызовы задач в основной цикл.

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

    • Отключите прерывания таймера.

    • Выполните специфическую для цели очистку, такую как обнуление DAC.

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

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

rt_OneStep и составление расписаний Факторов

Обзор операции

Область операции rt_OneStep зависит от

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

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

Разрешенные режимы решателя для Embedded Real-Time System Target Files суммирует разрешенные режимы решателя для односкоростных и многоскоростных моделей. Обратите внимание, что для модели с одной скоростью только 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_step<reservedrangesplaceholder0 > функция выполняет блоки, разделяющие tid N; другими словами, блочный код, который выполняется в N задач сгруппирован в связанный model_step<reservedrangesplaceholder0 > функция.

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

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

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

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

Прерывание.  Обратите внимание, что проект 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_step<reservedrangesplaceholder0 > в цикле подрейта.

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

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

В многоразовых rt_OneStep, можно улучшить эффективность путем разворачивания for и while циклы.

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

Также соблюдайте следующие осторожные рекомендации:

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

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

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

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

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

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

Обзор

В большинстве случаев самой легкой стратегией развертывания сгенерированного кода является использование опции Generate a example main program для генерации ert_main.c или .cpp модуль (см. «Генерация автономной программы»).

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

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

  • matlabroot/ rtw/c/src/common/rt_malloc_main.c - Поддерживает Reusable function упаковка интерфейса кода. Вы должны выбрать Use dynamic memory allocation for model initialization параметра конфигурации модели и задать Pass root-level I/O as параметра равным 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. Кроме того, необходимо изменить настройки шаблона make-файла или набора инструментальных средств таким образом, чтобы процесс сборки создавал соответствующий файл объекта, такой как model_rt_main.obj (в UNIX®, model_rt_main.o), в папке сборки.

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

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

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

Для односкоростных моделей операция rt_OneStep и основная функция по существу совпадает в статическом основном модуле, так же как и в автоматически сгенерированной версии, описанной в Deploy Generated Независимый Исполняемый Файл Programs To Target Hardware. Однако для многозадачных моделей с многозадачностью статический и сгенерированный код немного отличаются. В следующем разделе описывается этот случай.

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

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

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

void model_step(int_T tid);

По умолчанию, когда опция Сгенерировать пример основной программы отключена, цель ERT генерирует model_step «обертка» для многозадачных, многозадачных моделей. Цель оболочки состоит в том, чтобы взаимодействовать со специфичным для скорости model_step<reservedrangesplaceholder0 > функций к вызову старого стиля. Код обертки отправляется в model_step<reservedrangesplaceholder0 > вызов с помощью 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_step<reservedrangesplaceholder0 > function непосредственно, set 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.

  • Когда опция Generate a example main program отключена, 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:

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

    • Вызов MODEL_TERMINATE

  • Для rt_main.c (не применяется к rt_cppclass_main.cpp): Если вы не хотите объединять выходы и обновленные функции, очистите параметр конфигурации <reservedrangesplaceholder1> модели и внесите следующие изменения в вашу производственную версию 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 упаковка интерфейса кода, но параметр конфигурации модели Use dynamic memory allocation for model initialization очищен, данные образца модели должны быть выделены статически или динамически вызывающим основным кодом. Указатели на отдельные структуры данных моделей (такие как Блоки IO, DWork и Параметры) должны быть настроены в структуре модели реального времени данных верхнего уровня.

Чтобы поддержать основные изменения, процесс сборки генерирует подмножество следующих моделей реального времени макросов (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 упаковка интерфейса кода, Use dynamic memory allocation for model initialization очищенная Pass root-level I/O as установленная на Individual argumentsи Optimization панель опции Remove root level I/O zero initialization удалена. Создание модели генерирует следующие структуры данных моделей и точки входа модели в 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 an 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 a example main program отключена, генерация кода производит немного другой код группировки скоростей для совместимости со старым статическим ert_main.c модуль. Для получения дополнительной информации см. разделы «Группировка ставок» и «Статическая основная программа».

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

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

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

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

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

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

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

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

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

  • Только код, охраняемый tid перешел к OutputsForTID сгенерирован. The 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. Чтобы создать класс памяти, используйте Custom Storage Class Designer.

  4. Задайте класс, чтобы сохранить настройки свойств для класса памяти.

  5. Напишите код Target Language Компилятора (TLC), который излучает правильный Код С для класса памяти.

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

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

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

  9. Сгенерируйте и проверьте код.

Пример, в котором показано, как использовать Custom Storage Class Designer без записи кода TLC, см. в разделе Создание и применение Класса памяти.

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

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

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

0x8675309

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

(uint16_T *)0x8675309

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

(volatile const uint16_T *)0x8675309

Разменяйте адрес.

*(volatile const uint16_T *)0x8675309

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

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

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

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

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

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

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

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

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

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

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

Создайте класс памяти

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

Откройте Custom Storage Class designer в расширенном режиме. Чтобы спроектировать класс памяти, который работает через пользовательский код TLC, необходимо использовать расширенный режим.

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

В Custom Storage Class Designer нажмите кнопку New. Новый пользовательский класс памяти, NewCSC_1, появляется в списке определений классов памяти.

Переименуйте класс памяти в MemoryMappedAddress.

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

  • Введите в Other. Класс памяти может работать через пользовательский TLC код, который вы записываете позже.

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

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

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

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

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

После завершения выбора параметров нажмите «Применить» и «нажатие кнопки».

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

Задайте класс, чтобы сохранить настройки свойств для класса памяти

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

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

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

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

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

Написание кода TLC, который излучает правильный код С

Напишите TLC код, который использует атрибуты класса памяти, такие как HeaderFile и MemoryAddress, чтобы сгенерировать правильный код С для каждого элемента данных.

В +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, найдите фрагмент, которая управляет генерацией объявлений данных кода С.

    %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

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

The declare случай (%case) создает возврат значение (%return), который генератор кода излучает в заголовочный файл, который вы задаете для каждого элемента данных. Чтобы управлять Кодом С, которая объявляет каждый элемент данных, измените значение возврата в 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

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

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

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

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

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

MemoryMappedAddress приводит к появлению макроса в сгенерированном коде, поэтому вы используете declare корпус вместо define case, чтобы создать и издать макрос. Чтобы предотвратить define case от излучения повторяющегося определения макроса, новый код 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

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

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

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

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

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

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

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

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

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

Откройте Custom Storage Class Designer еще раз.

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

Для MemoryMappedAddress, на вкладке Другие атрибуты, установите:

  • Имя файла, в memory_map_csc.tlc.

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

Нажмите Применить и Сохранить.

Задайте класс данных сигнала

Чтобы применить класс памяти к элементу данных в модели, в 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 словаря

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

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

Откройте словарь Embedded Coder. На вкладке Кода С выберите Код Interface > Embedded Coder Dictionary.

В словаре Embedded Coder Dictionary нажмите Manage Packages.

В диалоговом окне «Управление пакетом» нажмите «Обновить». Когда обновление будет завершено, выберите пакет MemoryMap. Нажмите кнопку Загрузка.

Закройте словарь Embedded Coder.

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

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

Откройте редактор Отображения. На вкладке C Code выберите Code Interface > Individual Element Code Mappings.

В редакторе отображений кода на вкладке Inports выберите inport In1. Установите класс памяти для In1 на MemoryMappedAddress.

В Property Inspector в разделе Code задайте значение свойства HeaderFile memory_mapped_addresses.h и свойство MemoryAddress, для 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;
}

Похожие темы