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

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

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

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

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

  2. В меню Target operating system выберите BareBoardExample.

  3. Сгенерируйте код.

Различный код сгенерирован для многоскоростных моделей в зависимости от этих факторов:

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

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

Эти факторы могут повлиять на алгоритмы планирования, используемые в сгенерированном коде, и в некоторых случаях повлиять на 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
}

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

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

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

  1. Вызовите model_initialize.

  2. Инициализируйте целевые структуры данных и оборудование, такие как ADCs или DACs.

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

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

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

    Примечание

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

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

  7. После завершения основного цикла (если применимо):

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

    • Выполните целевую очистку, такую как обнуление DACs.

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

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

rt_OneStep и Факторы Планирования

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

Операция rt_OneStep зависит от

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

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

Разрешенные Режимы Решателя для Встроенных Конечных файлов Системы реального времени обобщают разрешенные режимы решателя для односкоростного и многоскоростных моделей. Обратите внимание на то, что для односкоростной модели, только SingleTasking режим решателя позволен.

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

РежимОдноскоростнойМногоскоростной

SingleTasking

Позволенный

Позволенный

MultiTasking

Запрещенный

Позволенный

Auto

Позволенный

(значения по умолчанию к SingleTasking)

Позволенный

(значения по умолчанию к MultiTasking)

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

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

Единственным допустимым режимом решателя для односкоростной модели является SingleTasking. Такие модели запущены в “односкоростной” операции.

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

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

Для односкоростного случая, сгенерированного modelшаг функция

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_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шаг функция сгенерирована:

void model_step(void)

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

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

Инструкции для Изменения rt_OneStep

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

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

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

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

    Примечание

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

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

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

Комментарии в rt_OneStep укажите на место, чтобы добавить ваш код.

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

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

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

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

  • Если вы настроили основной программный модуль, чтобы считать выходные параметры модели после каждого шага модели тарифной ставки, имеют в виду, что, выбирая параметры конфигурации модели 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 к model_rt_main.c. Кроме того, необходимо изменить make-файл шаблона или настройки набора инструментальных средств, таким образом, что процесс сборки создает соответствующий объектный файл, такой как model_rt_main.obj (на UNIX®, model_rt_main.o), в папке сборки.

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

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

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

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

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

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

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

void model_step(int_T tid);

По умолчанию, когда Генерировать пример, основная опция программы выключена, цель ERT, генерирует modelшаг “обертка” для многоскоростных, многозадачных моделей. Цель обертки состоит в том, чтобы соединить интерфейсом со специфичным для уровня 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шаг из статической основной программы в многоскоростной, многозадачной модели.

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, и включайте modelH непосредственно в вашем основном модуле. Подавить генерацию 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): Если вы не хотите комбинировать функции выхода и обновления, параметр конфигурации модели clear 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), на основе требований к данным вашей модели, в modelH.

Синтаксис макроса 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 модуль. Смотрите, что Уровень Группируется и Статическая Основная Программа для деталей.

Сделайте свой уровень S-функций, группирующийся совместимый

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

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

  • Уменьшаемый КПД кода.

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

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

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

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

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

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

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

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

    if (%<LibIsSFcnSampleHit(portName)>)

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

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

%% multirate_blk.tlc

%implements "multirate_blk" "C"


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

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

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

  %endfunction
%% [EOF] sfun_multirate.tlc

Листинг 2: Выходная генерация кода с группировкой уровня

%% example_multirateblk.tlc

%implements "example_multirateblk" "C"


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



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

  %endfunction

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

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

  %endfunction

%% [EOF] sfun_multirate.tlc

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

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

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

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

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

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

  1. Создайте модель в качестве примера.

  2. Создайте пакет, чтобы содержать определения класса данных и класса памяти.

  3. Используйте Custom Storage Class Designer, чтобы создать класс памяти.

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

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

  6. Задайте класс для данных об импорте.

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

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

  9. Сгенерируйте и смотрите код.

Для примера, который показывает, как использовать Custom Storage Class Designer, не пишущий код TLC, смотрите, Создают и Применяют Класс памяти.

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

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

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

0x8675309

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

(uint16_T *)0x8675309

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

(volatile const uint16_T *)0x8675309

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

*(volatile const uint16_T *)0x8675309

После разыменовать операции примените маску, чтобы сохранить только 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
### Successful completion of build procedure for: ex_memmap_simple

Build Summary

Top model targets built:

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

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

Смотрите сгенерированный заголовочный файл memory_mapped_addresses.h. Файл задает макро-A2D_INPUT, который соответствует сигнальной линии в модели.

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

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

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

Похожие темы