По умолчанию Embedded Coder® программное обеспечение генерирует автономные исполняемые программы, которые не требуют внешней исполнительной или операционной системы в реальном времени. Автономная программа требует минимальной модификации, чтобы быть адаптированной к целевому компьютеру. Автономная архитектура программы поддерживает выполнение моделей с одной или несколькими скоростями дискретизации.
Чтобы сгенерировать автономную программу:
Выберите параметр конфигурации модели Сгенерируйте пример основной программы. Это включает меню Target operating system.
В Target operating system меню выберите BareBoardExample
.
Сгенерируйте код.
Для многоскоростных моделей генерируется различный код в зависимости от этих факторов:
Выполняется ли модель в однозадачном или многозадачном режиме.
Генерируется ли повторно используемый код.
Эти факторы могут влиять на алгоритмы планирования, используемые в сгенерированном коде, и в некоторых случаях влияют на API для функций точки входа модели. В следующих разделах рассматриваются эти варианты.
Ядро автономной программы является основным циклом. На каждой итерации основной цикл выполняет фоновую или нулевую задачу и проверяет условие завершения.
Основной цикл периодически прерывается таймером. Функция rt_OneStep
устанавливается или как стандартная программа обработки прерывания таймера (ISR), или вызывается из ISR таймера на каждом шаге синхроимпульса.
Драйвер выполнения, rt_OneStep
, вызовы последовательностей в
функций. Область операции model
_steprt_OneStep
отличается в зависимости от того, является ли генерирующая модель односкоростной или мультирейтовой. В односкоростной модели rt_OneStep
просто вызывает
функция. В многоскоростную модель, model
_steprt_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 }
Псевдокод является проектом для обвязки программы, чтобы управлять вашей моделью. Основная программа лишь частично реализует этот проект. Вы должны изменить его в соответствии с вашими спецификациями.
В этом разделе описываются минимальные изменения, которые необходимо внести в производственную версию основного программного модуля для реализации программы обвязок.
Функции
.model
_initialize
Инициализируйте целевые структуры данных и оборудование, такие как АЦП или DAC.
Установка rt_OneStep
как таймер ISR.
Инициализируйте оборудование таймера.
Включите прерывания таймера и запустите таймер.
Примечание
rtModel
находится не в допустимом состоянии, пока
был вызван. Обслуживание прерываний таймера не должно начинаться до model
_initialize
был вызван.model
_initialize
При необходимости вставьте фоновые вызовы задач в основной цикл.
При отключении основного цикла (если применимо):
Отключите прерывания таймера.
Выполните специфическую для цели очистку, такую как обнуление DAC.
Обнаружение и обработка ошибок. Обратите внимание, что даже если ваша программа предназначена для бессрочного выполнения, вам может потребоваться обработать серьезные условия ошибки, такие как переполнение прерывания таймера.
Можно использовать макросы rtmGetErrorStatus
и rtmSetErrorStatus
для обнаружения и сигнализации ошибок.
Область операции rt_OneStep
зависит от
Является ли ваша модель односкоростной или мультирейтовой. В односкоростной модели шаги расчета всех блоков в модели и фиксированный размер шага модели одинаковы. Модель, в которой шаги расчета и размер шага не соответствуют этим условиям, называется мультиратом.
Режим решателя вашей модели (SingleTasking
от MultiTasking
)
Разрешенные режимы решателя для Embedded Real-Time System Target Files суммирует разрешенные режимы решателя для односкоростных и многоскоростных моделей. Обратите внимание, что для модели с одной скоростью только SingleTasking
разрешен режим решателя.
Разрешенные режимы решателя для встроенных целевых файлов системы реального времени
Способ | Односкоростной | Мультиуровень |
---|---|---|
|
Позволенный |
Позволенный |
|
Отвергнутый |
Позволенный |
|
Позволенный (по умолчанию равен |
Позволенный (по умолчанию равен |
Сгенерированный код для 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
_steprt_OneStep
поддерживает и проверяет флаг переполнения таймера. При вводе прерывания таймера отключаются до тех пор, пока не будет проверен флаг переполнения и другие условия ошибки. Если флаг переполнения снят, rt_OneStep
устанавливает флаг и продолжает с включенными прерываниями таймера.
Флаг переполнения очищается только после успешного возврата из
. Поэтому, если model
_steprt_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
_step0rt_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
_steprate_scheduler
(а не rate_monotonic_scheduler
). Планировщик поддерживает счетчики планирования на каждом такте. Существует один счетчик для каждой частоты дискретизации в модели. Счетчики реализованы в массиве (индексируется на tid) в Timing
структура внутри rtModel
.
Счетчики являются делителями тактовой частоты, которые подсчитывают период дискретизации, сопоставленный с каждой задачей подрейса. Когда счетчик указывает, что период дискретизации для заданной скорости прошел, rate_scheduler
очищает счетчик. Это условие указывает, что блоки, работающие с такой скоростью, должны выполняться при следующем вызове
, который отвечает за проверку счетчиков.model
_step
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.cNonreusable function
упаковка интерфейса кода.
- Поддерживает matlabroot
/ rtw/c/src/common/rt_malloc_main.cReusable 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.cppC++ class
упаковка интерфейса кода.
Статический основной модуль не является частью сгенерированного кода; он предоставляется как базис для пользовательских модификаций и для использования в симуляции. Если существующие приложения зависят от статического ert_main.c
(разработан в релизах до R2012b), rt_main.c
, rt_malloc_main.c
, или rt_cppclass_main.cpp
может потребоваться продолжить использование статического основного программного модуля.
При разработке приложений с помощью статического основного модуля следует скопировать модуль в рабочую папку и переименовать его перед внесением изменений. Например, можно переименовать rt_main.c
на
. Кроме того, необходимо изменить настройки шаблона make-файла или набора инструментальных средств таким образом, чтобы процесс сборки создавал соответствующий файл объекта, такой как model
_rt_main.c
(в UNIX®, model
_rt_main.obj
), в папке сборки.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
чтобы указать, что генерируются только основанные на скорости функции шага, без функции оболочки. Если ваш целевой объект вызывает совместимую группировку скоростей
function непосредственно, set model
_step<reservedrangesplaceholder0 >
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
.hrtmodel.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
модуль. Для получения дополнительной информации см. разделы «Группировка ставок» и «Статическая основная программа».
Встроенный 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)
Чтобы сконфигурировать модель для генерации кода, который задает и использует этот макрос:
Создайте модель примера.
Создайте пакет, содержащий определения класса данных и класса памяти.
Чтобы создать класс памяти, используйте Custom Storage Class Designer.
Задайте класс, чтобы сохранить настройки свойств для класса памяти.
Напишите код Target Language Компилятора (TLC), который излучает правильный Код С для класса памяти.
Определите класс для входных данных.
Загрузите пакет в словарь Embedded Coder.
Сконфигурируйте входной порт корневого уровня модели, чтобы использовать класс памяти.
Сгенерируйте и проверьте код.
Пример, в котором показано, как использовать 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; }