По умолчанию, Embedded Coder® программное обеспечение генерирует программы независимого исполняемого файла, которые не требуют внешней исполнительной или операционной системы в реальном времени. Автономная программа требует, чтобы минимальная модификация была адаптирована к целевому компьютеру. Автономная архитектура программы поддерживает выполнение моделей или с одним или с несколько частот дискретизации.
Уровень, группирующий проблемы соответствия и проблемы совместимости
Сгенерируйте код, который разыменовывает данные из литерального адреса памяти
Сгенерировать автономную программу:
Выбор параметр конфигурации модели Генерирует пример основная программа. Это включает меню Target operating system.
В меню Target operating system выберите BareBoardExample
.
Сгенерируйте код.
Различный код сгенерирован для многоскоростных моделей в зависимости от этих факторов:
Выполняется ли модель в однозадачном или многозадачном режиме.
Генерируется ли повторно используемый код.
Эти факторы могут повлиять на алгоритмы планирования, используемые в сгенерированном коде, и в некоторых случаях повлиять на API для функций точки входа модели. Следующие разделы обсуждают эти варианты.
Ядро автономной программы является основным циклом. На каждой итерации основной цикл выполняет фон или пустую задачу и проверки на условие завершения.
Основной цикл периодически прерывается таймером. Функциональный rt_OneStep
или установлен как служебная программа прерывания по таймеру (ISR) или назван от ISR таймера на каждом шаге часов.
Драйвер выполнения, rt_OneStep
, последовательности вызывают к
функции. Операция model
шагrt_OneStep
отличается в зависимости от того, является ли генерирующаяся модель односкоростной или многоскоростной. В односкоростной модели, rt_OneStep
просто вызывает
функция. В многоскоростной модели, model
шаг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
шаг
void model_step(void)
Односкоростной rt_OneStep
спроектирован, чтобы выполнить
в один период часов. Осуществлять это ограничение синхронизации, model
шагrt_OneStep
обеспечивает и проверяет таймер превышенный флаг. На записи отключены прерывания по таймеру, пока флаг переполнения и другое состояние ошибки не проверялись. Если флаг переполнения ясен, rt_OneStep
устанавливает флаг и возобновляет включенные прерывания по таймеру.
Флаг переполнения очищен только по успешному возврату из
. Поэтому, если model
шагrt_OneStep
повторно прерван прежде, чем завершить
, перепрерывание обнаруживается через флаг переполнения.model
шаг
Перепрерывание 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
шаг
функции для задачи базовой ставки и каждой задачи подуровня в модели. Функциональное соглашение о присвоении имен для этих функцийmodel
шаг
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
_step0rt_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
шаг
void model_step(void)
На каждом такте системных часов, rt_OneStep
проверяет флаг переполнения и вызывает
. Функцией планирования для многоскоростной однозадачной программы является model
шагrate_scheduler
(а не rate_monotonic_scheduler
). Планировщик обеспечивает счетчики планирования на каждом такте системных часов. Существует один счетчик для каждой частоты дискретизации в модели. Счетчики реализованы в массиве (индексированный на tid) в Timing
структура в rtModel
.
Счетчики являются делителями тактовой частоты, которые подсчитывают период расчета, сопоставленный с каждой задачей подуровня. Когда счетчик указывает, что период расчета для данного уровня протек, rate_scheduler
очищает счетчик. Это условие указывает, что блоки, запускающиеся на том уровне, должны выполниться на следующем вызове
, который ответственен за проверку счетчиков.model
шаг
rt_OneStep
не требует обширной модификации. Единственная необходимая модификация должна повторно включить прерывания после флагов переполнения, и состояние ошибки проверялось. Если применимо вы должны также
Сохраните и восстановите свой контекст FPU на входе и выходе к rt_OneStep
.
Установите входные параметры модели, сопоставленные с базовой ставкой прежде, чем вызвать
.model
_step0
Получите выходные параметры модели, сопоставленные с базовой ставкой после вызова
.model
_step0
Примечание
Если вы изменяете rt_OneStep
чтобы считать значение из непрерывного выходного порта после каждого шага модели тарифной ставки, см. соответствующую предостерегающую инструкцию ниже.
В многоскоростной, многозадачной модели, входные параметры модели набора, сопоставленные с подуровнями прежде, чем вызвать
в цикле подуровня.model
_stepN
В многоскоростной, многозадачной модели получите выходные параметры модели, сопоставленные с подуровнями после вызова
в цикле подуровня.model
_stepN
Комментарии в rt_OneStep
укажите на место, чтобы добавить ваш код.
В многоскоростном rt_OneStep
, можно улучшать производительность путем разворачивания for
и while
циклы.
Кроме того, можно принять решение изменить поведение переполнения, чтобы продолжить выполнение после того, как восстановление после ошибки будет завершено.
Также наблюдайте эти предостерегающие инструкции:
Вы не должны изменять путь, которым счетчики, флаги события или другие структуры данных синхронизации установлены в rt_OneStep
, или в функциях называется от rt_OneStep
. rt_OneStep
синхронизация структур данных (включая rtModel
) и логика очень важна для операции сгенерированной программы.
Если вы настроили основной программный модуль, чтобы считать выходные параметры модели после каждого шага модели тарифной ставки, имеют в виду, что, выбирая параметры конфигурации модели 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.cNonreusable function
установка для параметра конфигурации модели Code interface packaging.
— Поддерживает matlabroot
/rtw/c/src/common/rt_malloc_main.cReusable function
установка для параметра Code interface packaging. Необходимо выбрать параметр конфигурации модели 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
установка для параметра Code interface packaging.
Статические основные программы не являются частью сгенерированного кода. Программы предоставлены как базис для разработки пользовательских модификаций и для использования в симуляции.
Статическая основная программа содержит:
rt_OneStep
— Служебная программа прерывания по таймеру (ISR), который вызывает функцию точки входа
выполнить обработку в течение одного периода часов модели.model
шаг
Скелетный main
функция — Как предусмотрено, main
полезно для симуляции только. Необходимо изменить main
для управляемого прерыванием выполнения в реальном времени.
Для односкоростных моделей, операции rt_OneStep
и main
функция в статической основной программе эквивалентна, они находятся в автоматически сгенерированной версии, описанной в, Развертывают Сгенерированные Программы Независимого исполняемого файла В Целевой компьютер. Для многоскоростных, многозадачных моделей статическая основная программа и сгенерированный код отличаются как описано в следующем разделе.
Разрабатывать приложение при помощи статической основной программы:
Скопируйте программный модуль в свою рабочую папку.
Переименуйте файл. Например, переименуйте rt_main.c
к
. model
_rt_main.c
Настройте содержимое программного модуля.
Измените make-файл шаблона или настройки набора инструментальных средств, таким образом, что процесс сборки создает соответствующий объектный файл, такой как
(на UNIX®, model
_rt_main.obj
), в папке сборки.model
_rt_main.o
Если ваше существующее приложение зависит от статического ert_main.c
разработанный в релизах перед R2012b, rt_main.c
, rt_malloc_main.c
, или rt_cppclass_main.cpp
, вы можете должны быть продолжить использовать ту статическую основную программу.
Основанные на ERT системные конечные файлы могут использовать статический основной программный модуль и запретить использование параметра конфигурации модели, Генерируют пример основная программа. Пользовательский системный конечный файл не запрещает использование того параметра конфигурации модели, когда целевые изменения были внесены в статическую основную программу, и те модификации должны быть сохранены. Сгенерированный пример основная программа не сохраняет изменения в статической основной программе.
Для моделей, сконфигурированных для многозадачности (Обрабатывают каждый дискретный уровень, когда, отдельная задача выбрана), или параллелизм (Позвольте задаче выполниться одновременно на цели, выбран), генератор кода использует схему группировки уровня, производя функцию точки входа шага для каждого уровня (
, где model
_stepN
идентифицирует уровень). Отчет Интерфейса Кода перечисляет отдельные функции точки входа, которые может вызвать основная программа.N
Чтобы указать, что только основанные на уровне ступенчатые функции сгенерированы без функции обертки, используйте переменную TLC RateBasedStepFcn
. Если ваша цель вызывает уровень, группирующий совместимую функцию непосредственно, установите RateBasedStepFcn
к 1
. В этом случае функция обертки не сгенерирована.
В вашем системном конечном файле, до %include "codegenentry.tlc"
оператор, набор переменная TLC RateBasedStepFcn
к 1
. В качестве альтернативы установите RateBasedStepFcn
к 1
в вашем target_settings.tlc
файл.
Сделайте немного модификаций к основному циклу и 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
Hrtmodel.h
, используйте следующий оператор в своем системном конечном файле:
%assign AutoBuildProcedure = 0
Если вы очистили параметр конфигурации модели Оконечная требуемая функция, удаляете или комментируете следующие элементы в вашей производственной версии rt_main.c
, rt_malloc_main.c
, или rt_cppclass_main.cpp
:
#if TERMFCN...
проверка ошибки времени компиляции
Вызов MODEL_TERMINATE
Только для rt_main.c
: Если вы не хотите комбинировать функции выхода и обновления, очистите параметр конфигурации модели 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
определение данных или во время выполнения.
Когда вы, параметр конфигурации модели clear Генерирует пример основная программа, генератор кода, производите немного отличающийся код группировки уровня для совместимости с более старым статическим 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)
Чтобы сконфигурировать модель, чтобы сгенерировать код, который задает и использует этот макрос:
Создайте модель в качестве примера.
Создайте пакет, чтобы содержать определения класса данных и класса памяти.
Используйте Custom Storage Class Designer, чтобы создать класс памяти.
Задайте класс, чтобы сохранить настройки свойства для класса памяти.
Запишите код Компилятора выходного языка (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
После разыменовать операции примените маску, чтобы сохранить только 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.
Теперь, когда вы применяете класс памяти к элементу данных, такому как импорт In1
, можно задать заголовочный файл, чтобы содержать сгенерированное макроопределение. Вы еще не можете задать адрес памяти для элемента данных. Чтобы включить спецификацию адреса памяти, создайте класс пользовательских атрибутов, который можно сопоставить с 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
.
Загрузите пакет в словарь Embedded Coder
Перейдите к папке, которая содержит модель в качестве примера, и откройте модель.
Откройте приложение Embedded Coder.
Откройте Словарь Embedded Coder. На вкладке C Code выберите Code Interface> Embedded Coder Dictionary.
В Словаре Embedded Coder нажмите Manage Packages.
В диалоговом окне Manage Package нажмите Refresh. Когда обновление будет завершено, выберите пакет MemoryMap
. Нажмите Load.
Закройте словарь Embedded Coder.
Сконфигурируйте Корневой Уровень Inport, чтобы использовать класс памяти
В приложении Embedded Coder используйте редактор Отображений Кода и Property Inspector, чтобы сконфигурировать In1
Inport использовать класс памяти, который вы задали.
Откройте редактор Отображений Кода. На вкладке C Code выберите Code Interface> Individual Element Code Mappings.
В Редакторе Отображений Кода, на вкладке Inports, выбор импортирует In1
. Установите класс памяти для In1
к MemoryMappedAddress
.
В Property Inspector, в соответствии с Кодом, устанавливает свойство HeaderFile на memory_mapped_addresses.h
и свойство MemoryAddress к 0x8675309
.
Сохраните модель.
Сгенерируйте и смотрите код
Сгенерируйте код из модели.
### Starting build procedure for: ex_memmap_simple tf = logical 0 ### 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 25.046s
Смотрите сгенерированный заголовочный файл 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; }