По умолчанию программное обеспечение Embedded Coder® генерирует программы независимого исполняемого файла, которые не требуют внешней исполнительной или операционной системы в реальном времени. Автономная программа требует, чтобы минимальная модификация была адаптирована к целевому компьютеру. Автономная архитектура программы поддерживает выполнение моделей или с одним или с несколько частот дискретизации.
Уровень, группирующий проблемы соответствия и проблемы совместимости
Сгенерируйте код, который разыменовывает данные из литерального адреса памяти
Сгенерировать автономную программу:
В разделе Custom templates Code Generation> панель Templates диалогового окна Configuration Parameters, выберите Generate пример основная опция программы (который включен по умолчанию). Это включает меню Target operating system.
Из меню Target operating system выберите BareBoardExample
(выбор по умолчанию).
Сгенерируйте код.
Различный код сгенерирован для многоскоростных моделей в зависимости от следующих факторов:
Выполняется ли модель в однозадачном или многозадачном режиме.
Генерируется ли повторно используемый код.
Эти факторы влияют на алгоритмы планирования, используемые в сгенерированном коде, и в некоторых случаях влияют на 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 }
Псевдокод является проектом для программы обвязки, чтобы управлять вашей моделью. Основная программа только частично реализует этот проект. Необходимо изменить его согласно спецификациям.
В этом разделе описываются минимальные модификации, которые необходимо сделать в производственной версии основного программного модуля, чтобы реализовать программу обвязки.
Вызовите
.model_initialize
Инициализируйте целевые структуры данных и оборудование, такие как ADCs или DACs.
Установите rt_OneStep
как ISR таймера.
Инициализируйте оборудование таймера.
Включите прерывания по таймеру и запустите таймер.
rtModel
не находится в допустимом состоянии, пока
не был назван. Обслуживание прерываний по таймеру не должно начинаться, пока model_initialize
не был назван.model_initialize
Опционально, вставьте вызовы фоновой задачи в основной цикл.
После завершения основного цикла (если применимо):
Отключите прерывания по таймеру.
Выполните целевую очистку, такую как обнуление DACs.
Обнаружьте и обработайте ошибки. Обратите внимание на то, что, даже если ваша программа разработана, чтобы запуститься неопределенно, вы, возможно, должны обработать серьезное состояние ошибки, такое как переполнения прерывания по таймеру.
Можно использовать макросы rtmGetErrorStatus
и rtmSetErrorStatus
, чтобы обнаружить и сигнализировать об ошибках.
Операция rt_OneStep
зависит от
Является ли ваша модель односкоростной или многоскоростной. В односкоростной модели шаги расчета всех блоков в модели и фиксированный размер шага модели, являются тем же самым. Модель, в которой шаги расчета и размер шага не удовлетворяют этим условиям, называют многоскоростной.
Режим решателя вашей модели (SingleTasking
по сравнению с MultiTasking
)
Разрешенные Режимы Решателя для Встроенных Конечных файлов Системы реального времени обобщают разрешенные режимы решателя для односкоростного и многоскоростных моделей. Обратите внимание на то, что для односкоростной модели, только режим решателя 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_step
rt_OneStep
поддерживает и проверяет таймер превышенный флаг. На записи отключены прерывания по таймеру, пока флаг переполнения и другое состояние ошибки не проверялись. Если флаг переполнения ясен, rt_OneStep
устанавливает флаг и возобновляет включенные прерывания по таймеру.
Флаг переполнения очищен только по успешному возврату из
. Поэтому, если model_step
rt_OneStep
повторно прерван прежде, чем завершить
, перепрерывание обнаруживается через флаг переполнения.model_step
Перепрерывание rt_OneStep
таймером является состоянием ошибки. Если это условие является обнаруженными сигналами rt_OneStep
ошибка и сразу возвращается. (Обратите внимание на то, что можно изменить это поведение, если вы хотите обработать условие по-другому.)
Обратите внимание на то, что проект rt_OneStep
принимает, что прерывания отключены, прежде чем rt_OneStep
называется. rt_OneStep
должен быть непрерываемым, пока флаг переполнения прерывания не проверялся.
В многоскоростной многозадачной системе генерация кода использует приоритизированную схему вытесняющей многозадачности выполнить различные частоты дискретизации в вашей модели.
Следующий псевдокод показывает проект rt_OneStep
в многоскоростной многозадачной программе.
rt_OneStep() { Check for base-rate interrupt overrun Enable "rt_OneStep" interrupt Determine which rates need to run this time step Model_Step0() -- run base-rate time step code For N=1:NumTasks-1 -- iterate over sub-rate tasks If (sub-rate task N is scheduled) Check for sub-rate interrupt overrun Model_StepN() -- run sub-rate time step code EndIf EndFor }
Идентификаторы задачи. Выполнение блоков, имеющих различные частоты дискретизации, повреждается в задачи. Каждый блок, который выполняется на уровне данной частоты дискретизации, присвоен идентификатор задачи (tid
), который сопоставляет его с задачей, которая выполняется на том уровне. Где существуют задачи NumTasks
в системе, область значений идентификаторов задачи 0.. NumTasks
-1.
Установление приоритетов Задач Тарифной ставки и Подуровня. Задачи приоритизированы, в порядке убывания, уровнем. Задачей тарифной ставки является задача, которая запускается на самом быстром уровне в системе (аппаратная тактовая частота). Задача тарифной ставки имеет самый высокий приоритет (tid
0). Следующая самая быстрая задача (tid
1) имеет следующий самый высокий приоритет, и так далее вниз к самой медленной, самой низкой приоритетной задаче (tid
NumTasks
-1).
Более медленные задачи, запускающиеся во множителях базовой ставки, называются задачами подуровня.
Группировка уровня и Специфичные для уровня Функции model_step. В односкоростной модели выходные вычисления блока выполняются в одной функции,
. Для многоскоростных, многозадачных моделей генератор кода пытается использовать различную стратегию. Эта стратегия называется группировкой уровня. Группировка уровня генерирует отдельные model_step
функции
для задачи базовой ставки и каждой задачи подуровня в модели. Функциональное соглашение о присвоении имен для этих функцийmodel_step
model_stepN
где
является идентификатором задачи. Например, для модели под названием N
my_model
, который имеет три уровня, следующие функции сгенерированы:
void my_model_step0 (void); void my_model_step1 (void); void my_model_step2 (void);
Каждая функция
выполняет блоки, совместно использующие model_stepN
; другими словами, блочный код, который выполняет в задаче tid N
, сгруппирован в связанную N
функцию
.model_stepN
Планирование model_stepN Выполнение. На каждом такте системных часов rt_OneStep
поддерживает счетчики планирования и флаги события для каждой задачи подуровня. Счетчики реализованы как массивы taskCounter
, индексированные на tid
. Флаги события реализованы как массивы, индексированные на tid
.
Счетчики планирования и флаги задачи для подуровней сохраняются rt_OneStep
. Счетчики планирования являются делителями в основном тактовой частоты, которые подсчитывают демонстрационный период, сопоставленный с каждой задачей подуровня. Пара задач, которая обменивается данными, поддерживает флаг взаимодействия на более быстром уровне. Флаги взаимодействия задачи указывают, что и быстро и медленные задачи, как планируют, запустятся.
Флаги события указывают, планируется ли данная задача для выполнения. rt_OneStep
поддерживает флаги события на основе счетчика задачи, который сохраняется кодом в основном программном модуле для модели. Когда счетчик указывает, что демонстрационный период задачи протек, основные кодовые наборы флаг события для той задачи.
На каждом вызове rt_OneStep
обновляет свои структуры данных планирования, и продвигаются задача тарифной ставки (rt_OneStep
вызывает
, потому что задача тарифной ставки должна выполниться на каждом шаге часов). Затем model_step0
rt_OneStep
выполняет итерации по флагам планирования в порядке tid
, безусловно вызывая
для любой задачи, флаг которой установлен. Задачи выполняются в порядке приоритета.model_stepN
Вытеснение. Обратите внимание на то, что проект rt_OneStep
принимает, что прерывания отключены, прежде чем rt_OneStep
называется. rt_OneStep
должен быть непрерываемым, пока флаг переполнения прерывания тарифной ставки не проверялся (см. псевдокод выше).
Переменные типа массив флага события и переменные цикла, используемые rt_OneStep
, хранятся как локальные (стек) переменные. Поэтому rt_OneStep
повторно используем. Если rt_OneStep
повторно прерван, более высокие приоритетные задачи вытесняют более низкие приоритетные задачи. По возврату из прерывания понизьте приоритетное резюме задач в ранее запланированном порядке.
Превысьте Обнаружение. Многоскоростной rt_OneStep
также поддерживает массив таймера превышенные флаги. rt_OneStep
обнаруживает превышенный таймер, на задачу, той же логикой как односкоростной rt_OneStep
.
Если вы разработали многоскоростные S-функции, или если вы используете индивидуально настраиваемый статический основной программный модуль, смотрите, что Уровень Группирует Проблемы соответствия и Проблемы совместимости для получения информации о том, как адаптировать ваш код к совместимости группировки уровня. Эта адаптация позволяет вашим многоскоростным, многозадачным моделям сгенерировать более эффективный код.
В многоскоростной однозадачной программе, по определению, шаги расчета в модели должны быть целочисленным кратным размер фиксированного шага модели.
В многоскоростной однозадачной программе блоки выполняются на различных уровнях, но под тем же идентификатором задачи. Операция rt_OneStep
, в этом случае, является упрощенной версией многоскоростной многозадачной операции. Группировка уровня не используется. Единственной задачей является задача тарифной ставки. Поэтому только одна функция
сгенерирована:model_step
void model_step(void)
На каждом такте системных часов rt_OneStep
проверяет флаг переполнения и вызывает
. Функцией планирования для многоскоростной однозадачной программы является model_step
rate_scheduler
(а не rate_monotonic_scheduler
). Планировщик поддерживает счетчики планирования на каждом такте системных часов. Существует один счетчик для каждой частоты дискретизации в модели. Счетчики реализованы в массиве (индексированный на tid) в структуре Timing
в rtModel
.
Счетчики являются делителями тактовой частоты, которые подсчитывают демонстрационный период, сопоставленный с каждой задачей подуровня. Когда счетчик указывает, что демонстрационный период для данного уровня протек, rate_scheduler
очищает счетчик. Это условие указывает, что блоки, запускающиеся на том уровне, должны выполниться на следующем вызове
, который ответственен за проверку счетчиков.model_step
rt_OneStep
не требует обширной модификации. Единственная необходимая модификация должна повторно включить прерывания после флагов переполнения, и состояние ошибки проверялось. Если применимо вы должны также
Сохраните и восстановите свой контекст FPU на входе и выходе к rt_OneStep
.
Установите образцовые входные параметры, сопоставленные с базовой ставкой прежде, чем вызвать
.model_step0
Получите образцовые выходные параметры, сопоставленные с базовой ставкой после вызова
.model_step0
Если вы изменяете rt_OneStep
, чтобы считать значение из непрерывного выходного порта после каждого шага модели тарифной ставки, см. соответствующую предостерегающую инструкцию ниже.
В многоскоростной, многозадачной модели, входные параметры модели набора, сопоставленные с подуровнями прежде, чем вызвать
в цикле подуровня.model_stepN
В многоскоростной, многозадачной модели получите образцовые выходные параметры, сопоставленные с подуровнями после вызова
в цикле подуровня.model_stepN
Комментарии в rt_OneStep
указывают на место, чтобы добавить ваш код.
В многоскоростном rt_OneStep
можно улучшать производительность путем разворачивания циклов while
и for
.
Кроме того, можно принять решение изменить поведение переполнения, чтобы продолжить выполнение после того, как восстановление после ошибки будет завершено.
Также наблюдайте следующие предостерегающие инструкции:
Вы не должны изменять путь, которым счетчики, флаги события или другие структуры данных синхронизации установлены в rt_OneStep
, или в функциях, вызванных от rt_OneStep
. 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 Approach для верификации.
В большинстве случаев самая легкая стратегия развертывания сгенерированного кода состоит в том, чтобы использовать Генерировать пример основная опция программы, чтобы сгенерировать ert_main.c
или модуль .cpp
(см., Генерируют Автономную Программу).
Однако, если вы поворачиваете Генерировать пример основная опция программы прочь, можно использовать статический основной модуль в качестве примера или шаблона для разработки встраиваемых приложений. Статические основные модули, обеспеченные MathWorks®, включают:
— Поддержки упаковка интерфейса matlabroot/rtw/c/src/common/rt_main.c
Nonreusable function
кода.
— Поддержки упаковка интерфейса matlabroot/rtw/c/src/common/rt_malloc_main.c
Reusable function
кода. Образцовая опция 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
к
. Кроме того, необходимо изменить 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
и основной функции является по существу тем же самым в статическом основном модуле, как они находятся в автоматически сгенерированной версии, описанной в, Развертывают Сгенерированные Программы Независимого исполняемого файла На Целевом компьютере. Для многоскоростных, многозадачных моделей, однако, статический и сгенерированный код немного отличается. Следующий раздел описывает этот случай.
Цели на основе цели ERT иногда используют статический основной модуль и запрещают использование Того, чтобы генерировать пример основная опция программы. Это сделано, потому что целевые модификации были добавлены к статическому основному модулю, и эти модификации не были бы сохранены, если бы основная программа была регенерирована.
Ваш статический основной модуль может или не может использовать уровень, группирующий совместимые функции
. Если ваш основной модуль основан на статическом model_stepN
rt_main.c
, rt_malloc_main.c
или модуле rt_cppclass_main.cpp
, это не использует специфичные для уровня вызовы функции
. Это использует model_stepN
функцию
старого стиляmodel_step
, передающую в идентификаторе задачи:
void model_step(int_T tid);
По умолчанию, когда Генерировать пример, основная опция программы выключена, цель ERT, генерирует
“обертка” для многоскоростных, многозадачных моделей. Цель обертки состоит в том, чтобы соединить интерфейсом со специфичными для уровня model_step
функциями
к вызову старого стиля. Код обертки отправляет model_stepN
вызову
с оператором model_stepN
switch
, как в следующем примере:
void mymodel_step(int_T tid) /* Sample time: */ { switch(tid) { case 0 : mymodel_step0(); break; case 1 : mymodel_step1(); break; case 2 : mymodel_step2(); break; default : break; } }
Следующий псевдокод показывает, как rt_OneStep
вызывает
из статической основной программы в многоскоростной, многозадачной модели.model_step
rt_OneStep() { Check for base-rate interrupt overflow Enable "rt_OneStep" interrupt Determine which rates need to run this time step ModelStep(tid=0) --base-rate time step For N=1:NumTasks-1 -- iterate over sub-rate tasks Check for sub-rate interrupt overflow If (sub-rate task N is scheduled) ModelStep(tid=N) --sub-rate time step EndIf EndFor }
Можно использовать переменную TLC RateBasedStepFcn
, чтобы указать, что только основанные на уровне ступенчатые функции сгенерированы без функции обертки. Если ваша цель вызывает уровень, группирующий совместимую функцию
непосредственно, установите model_stepN
RateBasedStepFcn
на 1
. В этом случае функция обертки не сгенерирована.
Необходимо установить RateBasedStepFcn
до оператора %include "codegenentry.tlc"
в системном конечном файле. Также можно установить RateBasedStepFcn
в файле target_settings.tlc
.
Как со сгенерированным ert_main.c
или .cpp
, необходимо сделать несколько модификаций к основному циклу и rt_OneStep
. См. Инструкции для Изменения Основной Программы и Инструкций для Изменения rt_OneStep.
Кроме того, необходимо заменить вызов rt_OneStep
в основном цикле с вызовом фоновой задачи или пустым оператором.
Другие модификации, которые вы, возможно, должны сделать,
Если применимо следуйте комментариям в коде относительно того, где добавить код для чтения/записи модели I/O и сохранения/восстановления контекста FPU.
Если вы изменяете rt_main.c
, rt_malloc_main.c
или rt_cppclass_main.cpp
, чтобы считать значение из непрерывного выходного порта после каждого шага модели тарифной ставки, см. соответствующую предостерегающую инструкцию в Инструкциях для Изменения rt_OneStep.
Когда Генерировать пример, основная опция программы выключена, rtmodel.h
, сгенерировано, чтобы обеспечить интерфейс между основным модулем и сгенерированным типовым кодексом. Если бы вы создаете свой собственный статический основной программный модуль, вы обычно включали бы rtmodel.h
.
Также можно подавить генерацию rtmodel.h
и включать
непосредственно в основной модуль. Чтобы подавить генерацию model.h
rtmodel.h
, используйте следующий оператор в своем системном конечном файле:
%assign AutoBuildProcedure = 0
Если вы очистились, Оконечная функция потребовала опции, удалите или прокомментируйте следующее в своей производственной версии rt_main.c
, rt_malloc_main.c
или rt_cppclass_main.cpp
:
Проверка ошибки времени компиляции #if TERMFCN...
Вызов MODEL_TERMINATE
Для rt_main.c
(не применимый к rt_cppclass_main.cpp
): Если вы не хотите комбинировать функции вывода и обновления, очистите опцию Single output/update function и внесите следующие изменения в вашей производственной версии rt_main.c
:
Замените вызовы MODEL_STEP
с вызовами MODEL_OUTPUT
и MODEL_UPDATE
.
Удалите проверку на ошибки #if ONESTEPFCN...
.
Статический модуль rt_main.c
не поддерживает упаковку интерфейса Reusable function
кода. Следующая проверка на ошибки повышает ошибку времени компиляции, если упаковка интерфейса Reusable function
кода используется незаконно.
#if MULTI_INSTANCE_CODE==1
Если вы используете статический основной программный модуль, и ваша модель сконфигурирована для упаковки интерфейса Reusable function
кода, но образцовая опция, Use dynamic memory allocation for model initialization не выбран, образцовые данные об экземпляре, должна быть выделена или статически или динамически вызывающим основным кодом. Указатели на отдельные структуры данных модели (такие как Блок IO, DWork и Параметры) должны быть настроены в структуре данных модели реального времени верхнего уровня.
Чтобы поддержать основные модификации, процесс сборки генерирует подмножество следующих макросов модели реального времени (RTM), на основе требований к данным вашей модели, в
.model.h
Синтаксис макроса RTM | Описание |
---|---|
rtmGetBlockIO(rtm) |
Получите структуру данных блока I/O |
rtmSetBlockIO(rtm,val) |
Установите структуру данных блока I/O |
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
или во время выполнения.
Когда Генерировать пример, основная опция программы выключена, генерация кода, производит немного отличающийся код группировки уровня для совместимости с более старым статическим модулем ert_main.c
. Смотрите, что Уровень Группируется и Статическая Основная Программа для деталей.
Встроенные блоки Simulink®, а также блоки DSP System Toolbox™, совместимы с требованиями для генерации кода группировки уровня. Однако написанные пользователем многоскоростные встроенные S-функции не могут быть уровнем, группирующимся совместимый. Несовместимые блоки генерируют менее эффективный код, но в противном случае совместимы с группировкой уровня. Чтобы в полной мере воспользоваться эффективностью группировки уровня, ваши многоскоростные встроенные S-функции должны быть обновлены, чтобы быть полностью уровнем, группирующимся совместимый. Необходимо обновить S-реализации-функции TLC, как описано в этом разделе.
Использование несовместимых многоскоростных блоков, чтобы сгенерировать группирующий уровень код генерирует мертвый код. Это может вызвать две проблемы:
Уменьшаемая эффективность кода.
Предупреждающие сообщения выпущены во время компиляции. Такие предупреждения вызываются когда временные переменные ссылок мертвого кода перед инициализацией. Поскольку мертвый код не запускается, эта проблема не влияет на поведение во время выполнения сгенерированного кода.
Чтобы сделать ваш уровень S-функций, группирующийся совместимый, можно использовать следующие функции TLC, чтобы сгенерировать ModelOutputs
и код ModelUpdate
, соответственно:
OutputsForTID(block, system, tid) UpdateForTID(block, system, tid)
Листинги кода ниже иллюстрируют генерацию выходных вычислений без группировки уровня (Листинг 1) и с группировкой уровня (Листинг 2). Отметьте следующее:
Аргумент tid
является идентификатором задачи (0..NumTasks-1
).
Только код, который охраняет tid
, переданный в к OutputsForTID
, сгенерирован. Тест if (%<LibIsSFcnSampleHit(portName)>)
не используется в OutputsForTID
.
Когда генерация кода группировки уровня, OutputsForTID
и/или UpdateForTID
называется во время генерации кода. При генерации кода "не называются группировка уровня", Outputs
и/или Update
.
В уровне, группирующем совместимый код, Outputs
верхнего уровня и/или функции Update
вызывают OutputsForTID
и/или функции UpdateForTID
для каждого уровня (tid
), вовлеченный в блок. Код, возвращенный OutputsForTID
и/или UpdateForTID
, должна охранять соответствующая защита tid
:
if (%<LibIsSFcnSampleHit(portName)>)
как в Листинге 2.
Листинг 1: Выходная генерация кода без группировки уровня
%% multirate_blk.tlc %implements "multirate_blk" "C" %% Function: mdlOutputs ===================================================== %% Abstract: %% %% Compute the two outputs (input signal decimated by the %% specified parameter). The decimation is handled by sample times. %% The decimation is only performed if the block is enabled. %% Each port has a different rate. %% %% Note, the usage of the enable should really be protected such that %% each task has its own enable state. In this example, the enable %% occurs immediately which may or may not be the expected behavior. %% %function Outputs(block, system) Output /* %<Type> Block: %<Name> */ %assign enable = LibBlockInputSignal(0, "", "", 0) { int_T *enabled = &%<LibBlockIWork(0, "", "", 0)>; %if LibGetSFcnTIDType("InputPortIdx0") == "continuous" %% Only check the enable signal on a major time step. if (%<LibIsMajorTimeStep()> && ... %<LibIsSFcnSampleHit("InputPortIdx0")>) { *enabled = (%<enable> > 0.0); } %else if (%<LibIsSFcnSampleHit("InputPortIdx0")>) { *enabled = (%<enable> > 0.0); } %endif if (*enabled) { %assign signal = LibBlockInputSignal(1, "", "", 0) if (%<LibIsSFcnSampleHit("OutputPortIdx0")>) { %assign y = LibBlockOutputSignal(0, "", "", 0) %<y> = %<signal>; } if (%<LibIsSFcnSampleHit("OutputPortIdx1")>) { %assign y = LibBlockOutputSignal(1, "", "", 0) %<y> = %<signal>; } } } %endfunction %% [EOF] sfun_multirate.tlc
Листинг 2: Выходная генерация кода с группировкой уровня
%% example_multirateblk.tlc %implements "example_multirateblk" "C" %% Function: mdlOutputs ===================================================== %% Abstract: %% %% Compute the two outputs (the input signal decimated by the %% specified parameter). The decimation is handled by sample times. %% The decimation is only performed if the block is enabled. %% All ports have different sample rate. %% %% Note: the usage of the enable should really be protected such that %% each task has its own enable state. In this example, the enable %% occurs immediately which may or may not be the expected behavior. %% %function Outputs(block, system) Output %assign portIdxName = ["InputPortIdx0","OutputPortIdx0","OutputPortIdx1"] %assign portTID = [%<LibGetGlobalTIDFromLocalSFcnTID("InputPortIdx0")>, ... %<LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx0")>, ... %<LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx1")>] %foreach i = 3 %assign portName = portIdxName[i] %assign tid = portTID[i] if (%<LibIsSFcnSampleHit(portName)>) { %<OutputsForTID(block,system,tid)> } %endforeach %endfunction %function OutputsForTID(block, system, tid) Output /* %<Type> Block: %<Name> */ %assign enable = LibBlockInputSignal(0, "", "", 0) %assign enabled = LibBlockIWork(0, "", "", 0) %assign signal = LibBlockInputSignal(1, "", "", 0) %switch(tid) %case LibGetGlobalTIDFromLocalSFcnTID("InputPortIdx0") %if LibGetSFcnTIDType("InputPortIdx0") == "continuous" %% Only check the enable signal on a major time step. if (%<LibIsMajorTimeStep()>) { %<enabled> = (%<enable> > 0.0); } %else %<enabled> = (%<enable> > 0.0); %endif %break %case LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx0") if (%<enabled>) { %assign y = LibBlockOutputSignal(0, "", "", 0) %<y> = %<signal>; } %break %case LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx1") if (%<enabled>) { %assign y = LibBlockOutputSignal(1, "", "", 0) %<y> = %<signal>; } %break %default %% error it out %endswitch %endfunction %% [EOF] sfun_multirate.tlc
Этот пример показывает, как сгенерировать код, который читает значение сигнала путем разыменования адреса памяти, который вы задаете. С этим методом можно сгенерировать алгоритм управления, который взаимодействует с памятью, которую заполняет оборудование (например, память, которая хранит вывод аналого-цифрового конвертера в микроконтроллере).
В этом примере вы генерируете алгоритм, который получает входные данные от 16-битного блока памяти в адресе 0x8675309
. Примите, что аппаратное устройство асинхронно заполняет только более низкие 10 битов адреса. Алгоритм должен обработать адрес как только для чтения (const
), энергозависимый (volatile
) данные, и проигнорировать верхние 6 битов адреса.
Сгенерированный код может получить доступ к данным путем определения макроса, который разыменовывает 0x8675309
и маскирует ненужные биты:
#define A2D_INPUT ((*(volatile const uint16_T *)0x8675309)&0x03FF)
Чтобы сконфигурировать модель, чтобы сгенерировать код, который задает и использует этот макрос, необходимо создать усовершенствованный пользовательский класс памяти и код Компилятора выходного языка (TLC) записи. Для примера, который показывает, как использовать 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
После разыменовать операции примените маску, чтобы сохранить только 10 битов, которые заполняет оборудование. Используйте явные круглые скобки, чтобы управлять порядком операций.
(*(volatile const uint16_T *)0x8675309)&0x03FF
Как безопасная практика кодирования, перенесите целое построение в другой слой круглых скобок.
((*(volatile const uint16_T *)0x8675309)&0x03FF)
Создайте модель в качестве примера
Создайте модель ex_memmap_simple
в качестве примера.
Для блока Inport, устанавливает тип выходных данных на uint16
. Назовите сигнал как A2D_INPUT
. Блок 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
, на вкладке "Общие", наборе:
Введите к Other
. Пользовательский класс памяти может действовать через пользовательский код TLC, который вы пишете позже.
Данные определяют объем к Exported
. Для элементов данных, которые используют этот пользовательский класс памяти, Simulink Coder генерирует определение (например, оператор #define
, который задает макрос).
Инициализация данных к None
. Simulink Coder не генерирует код, который инициализирует элемент данных. Используйте эту установку, потому что этот пользовательский класс памяти представляет данные только для чтения. Вы не выбираете Macro
, потому что Custom Storage Class Designer не позволяет вам использовать Macro
для данных сигнала.
Файл определения к Specify
(оставляют текстовое поле пустым). Для элементов данных, которые используют память в сгенерированном коде, файл Определения задает исходный файл .c
, который выделяет память. Однако этот пользовательский класс памяти приводит к макросу, который не требует памяти. Как правило, заголовочные файлы (.h
), не файлы .c
, задают макросы. Установка файла Определения к Specify
вместо Instance specific
предотвращает пользователей пользовательского класса памяти от ненужного определения файла определения.
Заголовочный файл к Instance specific
. Чтобы управлять размещением файла макроопределения, пользователь пользовательского класса памяти должен задать заголовочный файл для каждого элемента данных, который использует этот пользовательский класс памяти.
Владелец к Specify
(оставляют текстовое поле пустым). Владелец применяется только к элементам данных, которые используют память.
После того, как вы закончите выбирать настройки, нажмите Apply и Save.
Теперь, когда вы применяете пользовательский класс памяти к элементу данных, такому как сигнальная линия A2D_INPUT
, можно задать заголовочный файл, чтобы содержать сгенерированное макроопределение. Однако вы еще не можете задать адрес памяти для элемента данных. Чтобы включить спецификацию адреса памяти, создайте класс пользовательских атрибутов, что можно сопоставить с MemoryMappedAddress
пользовательский класс памяти.
Задайте класс, чтобы сохранить пользовательские атрибуты для пользовательского класса памяти
Задайте класс MATLAB, чтобы хранить дополнительную информацию для элементов данных, которые используют пользовательский класс памяти. В этом случае дополнительной информацией является адрес памяти.
В пакете MemoryMap
(папка +MemoryMap
), создайте папку под названием @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 %% ==========================================================================
Случай 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 %% ==========================================================================
Случай define
выводит возвращаемое значение, которое генератор кода испускает в файл .c
, который задает элементы данных, которые используют память.
Замените существующее содержимое %case
на это новое содержимое:
%case "define" %return "" %%break %% ==========================================================================
MemoryMappedAddress
приводит к макросу в сгенерированном коде, таким образом, вы используете случай declare
вместо случая define
, чтобы создать и испустить макрос. Чтобы препятствовать тому, чтобы случай define
испустил дублирующееся макроопределение, новый код TLC возвращает пустую строку.
Найдите фрагмент, который управляет генерацией кода, который инициализирует данные.
%case "initialize" %% LibDefaultCustomStorageInitialize is the default initialization %% function that initializes a scalar element of a global variable to 0. %return LibDefaultCustomStorageInitialize(record, idx, reim) %%break %% ==========================================================================
Случай initialize
генерирует код, который инициализирует элементы данных (например, в функции 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
, на вкладке Other Attributes, наборе:
Имя файла TLC к memory_map_csc.tlc
.
CSC приписывает класс MemoryMap.MemoryMapAttribs
.
Нажмите Apply и Save.
Задайте класс данных сигнала
Чтобы применить пользовательский класс памяти к сигналу в модели, в пакете MemoryMap
, необходимо создать класс MATLAB, который выводит от Simulink.Signal
. Когда вы конфигурируете сигнал в модели, вы выбираете этот новый класс данных вместо класса по умолчанию, Simulink.Signal
.
В пакете MemoryMap
создайте папку под названием @Signal
.
В папке @Signal
создайте файл с именем Signal.m
.
classdef Signal < Simulink.Signal methods function setupCoderInfo( this ) useLocalCustomStorageClasses( this, 'MemoryMap' ); return; end end end
Файл задает класс под названием MemoryMap.Signal
. Определение класса заменяет метод setupCoderInfo
, который уже реализует класс Simulink.Signal
. Новая реализация указывает, что объекты класса MemoryMap.Signal
используют пользовательские классы памяти от пакета MemoryMap
(вместо пользовательских классов памяти от пакета Simulink
). Когда вы конфигурируете сигнал в модели путем выбора класса MemoryMap.Signal
, можно выбрать новый пользовательский класс памяти, MemoryMappedAddress
.
Примените пользовательский класс памяти к сигнальной линии
Перейдите к папке, которая содержит модель в качестве примера, и откройте модель.
В модели выберите View> Property Inspector.
Кликните по сигналу под названием A2D_INPUT
.
В Property Inspector, под Генерацией кода, классом объекта Сигнала набора к MemoryMap.Signal
. Если вы не видите MemoryMap.Signal
, выберите Customize class lists
и используйте диалоговое окно, чтобы включить выбор MemoryMap.Signal
.
В Property Inspector, Классе памяти набора к MemoryMappedAddress
.
Установите Заголовочный файл на memory_mapped_addresses.h
.
Установите MemoryAddress на 0x8675309
.
Сгенерируйте и осмотрите код
Сгенерируйте код из модели.
### Starting build procedure for model: ex_memmap_simple ### Successful completion of build procedure for model: ex_memmap_simple
Осмотрите сгенерированный заголовочный файл 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 ex_memmap_simple_step(void) { /* Outport: '<Root>/Out1' incorporates: * Gain: '<Root>/Gain' * Inport: '<Root>/In1' */ rtY.Out1 = 42.0 * (real_T)A2D_INPUT; }