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

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

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

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

  1. В разделе Custom templates Code Generation> панель Templates диалогового окна Configuration Parameters, выберите Generate пример основная опция программы (который включен по умолчанию). Это включает меню Target operating system.

  2. Из меню Target operating system выберите BareBoardExample (выбор по умолчанию).

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

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

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

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

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

Автономные компоненты программы

Ядро автономной программы является основным циклом. На каждой итерации основной цикл выполняет фон или пустую задачу и проверки на условие завершения.

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

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

Основная программа

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

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

main()
{
  Initialization (including installation of rt_OneStep as an 
    interrupt service routine for a real-time clock)
  Initialize and start timer hardware
  Enable interrupts
  While(not Error) and (time < final time)
    Background task
  EndWhile
  Disable interrupts (Disable rt_OneStep from executing)
  Complete any background tasks
  Shutdown
}

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

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

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

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

  2. Инициализируйте целевые структуры данных и оборудование, такие как 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_step

void model_step(void)

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

Флаг переполнения очищен только по успешному возврату из model_step. Поэтому, если rt_OneStep повторно прерван прежде, чем завершить model_step, перепрерывание обнаруживается через флаг переполнения.

Перепрерывание rt_OneStep таймером является состоянием ошибки. Если это условие является обнаруженными сигналами rt_OneStep ошибка и сразу возвращается. (Обратите внимание на то, что можно изменить это поведение, если вы хотите обработать условие по-другому.)

Обратите внимание на то, что проект rt_OneStep принимает, что прерывания отключены, прежде чем rt_OneStep называется. rt_OneStep должен быть непрерываемым, пока флаг переполнения прерывания не проверялся.

Многоскоростная многозадачная операция

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

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

rt_OneStep()
{
  Check for base-rate interrupt overrun
  Enable "rt_OneStep" interrupt
  Determine which rates need to run this time step

  Model_Step0()        -- run base-rate time step code

  For N=1:NumTasks-1   -- iterate over sub-rate tasks
    If (sub-rate task N is scheduled)
    Check for sub-rate interrupt overrun
      Model_StepN()    -- run sub-rate time step code
    EndIf
  EndFor
}

Идентификаторы задачи.  Выполнение блоков, имеющих различные частоты дискретизации, повреждается в задачи. Каждый блок, который выполняется на уровне данной частоты дискретизации, присвоен идентификатор задачи (tid), который сопоставляет его с задачей, которая выполняется на том уровне. Где существуют задачи NumTasks в системе, область значений идентификаторов задачи 0.. NumTasks-1.

Установление приоритетов Задач Тарифной ставки и Подуровня.  Задачи приоритизированы, в порядке убывания, уровнем. Задачей тарифной ставки является задача, которая запускается на самом быстром уровне в системе (аппаратная тактовая частота). Задача тарифной ставки имеет самый высокий приоритет (tid 0). Следующая самая быстрая задача (tid 1) имеет следующий самый высокий приоритет, и так далее вниз к самой медленной, самой низкой приоритетной задаче (tid NumTasks-1).

Более медленные задачи, запускающиеся во множителях базовой ставки, называются задачами подуровня.

Группировка уровня и Специфичные для уровня Функции model_step.  В односкоростной модели выходные вычисления блока выполняются в одной функции, model_step. Для многоскоростных, многозадачных моделей генератор кода пытается использовать различную стратегию. Эта стратегия называется группировкой уровня. Группировка уровня генерирует отдельные функции model_step для задачи базовой ставки и каждой задачи подуровня в модели. Функциональное соглашение о присвоении имен для этих функций

model_stepN

где N является идентификатором задачи. Например, для модели под названием my_model, который имеет три уровня, следующие функции сгенерированы:

void my_model_step0 (void);
void my_model_step1 (void);
void my_model_step2 (void);

Каждая функция model_stepN выполняет блоки, совместно использующие tid N; другими словами, блочный код, который выполняет в задаче N, сгруппирован в связанную функцию model_stepN.

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

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

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

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

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

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

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

Примечание

Если вы разработали многоскоростные S-функции, или если вы используете индивидуально настраиваемый статический основной программный модуль, смотрите, что Уровень Группирует Проблемы соответствия и Проблемы совместимости для получения информации о том, как адаптировать ваш код к совместимости группировки уровня. Эта адаптация позволяет вашим многоскоростным, многозадачным моделям сгенерировать более эффективный код.

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

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

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

void model_step(void)

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

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

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

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

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

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

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

    Примечание

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

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

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

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

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

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

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

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

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

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

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

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

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

Обзор

В большинстве случаев самая легкая стратегия развертывания сгенерированного кода состоит в том, чтобы использовать Генерировать пример основная опция программы, чтобы сгенерировать ert_main.c или модуль .cpp (см., Генерируют Автономную Программу).

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

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

  • matlabroot/rtw/c/src/common/rt_malloc_main.c — Поддержки упаковка интерфейса Reusable function кода. Образцовая опция Use dynamic memory allocation for model initialization должен быть включен и параметр модели Pass root-level I/O as, должна быть установлена в Part of model data structure.

  • matlabroot/rtw/c/src/common/rt_cppclass_main.cpp — Поддержки упаковка интерфейса C++ class кода.

Статический основной модуль не является частью сгенерированного кода; это обеспечивается как основание для ваших пользовательских модификаций, и для использования в симуляции. Если ваши существующие приложения зависят от статического ert_main.c (разработанный в релизах перед R2012b), rt_main.c, rt_malloc_main.c или rt_cppclass_main.cpp, вы, возможно, должны продолжить использовать статический основной программный модуль.

При разрабатывании приложений с помощью статического основного модуля необходимо скопировать модуль в рабочую папку и переименовать его прежде, чем сделать модификации. Например, вы могли переименовать rt_main.c к model_rt_main.c. Кроме того, необходимо изменить make-файл шаблона или настройки набора инструментальных средств, таким образом, что процесс сборки создает соответствующий объектный файл, такой как model_rt_main.obj (на UNIX®, model_rt_main.o), в папке сборки.

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

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

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

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

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

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

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

void model_step(int_T tid);

По умолчанию, когда Генерировать пример, основная опция программы выключена, цель ERT, генерирует model_step “обертка” для многоскоростных, многозадачных моделей. Цель обертки состоит в том, чтобы соединить интерфейсом со специфичными для уровня функциями model_stepN к вызову старого стиля. Код обертки отправляет вызову model_stepN с оператором switch, как в следующем примере:

void mymodel_step(int_T tid) /* Sample time:  */
{

  switch(tid) {
   case 0 :
    mymodel_step0();
    break;
   case 1 :
    mymodel_step1();
    break;
   case 2 :
    mymodel_step2();
    break;
   default :
    break;
  }
}

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

rt_OneStep()
{
  Check for base-rate interrupt overflow
  Enable "rt_OneStep" interrupt
  Determine which rates need to run this time step

  ModelStep(tid=0)     --base-rate time step

  For N=1:NumTasks-1  -- iterate over sub-rate tasks
    Check for sub-rate interrupt overflow
    If (sub-rate task N is scheduled)
      ModelStep(tid=N)    --sub-rate time step
    EndIf
  EndFor
}

Можно использовать переменную TLC RateBasedStepFcn, чтобы указать, что только основанные на уровне ступенчатые функции сгенерированы без функции обертки. Если ваша цель вызывает уровень, группирующий совместимую функцию model_stepN непосредственно, установите RateBasedStepFcn на 1. В этом случае функция обертки не сгенерирована.

Необходимо установить RateBasedStepFcn до оператора %include "codegenentry.tlc" в системном конечном файле. Также можно установить RateBasedStepFcn в файле target_settings.tlc.

Измените статическую основную программу

Как со сгенерированным ert_main.c или .cpp, необходимо сделать несколько модификаций к основному циклу и rt_OneStep. См. Инструкции для Изменения Основной Программы и Инструкций для Изменения rt_OneStep.

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

Другие модификации, которые вы, возможно, должны сделать,

  • Если применимо следуйте комментариям в коде относительно того, где добавить код для чтения/записи модели I/O и сохранения/восстановления контекста FPU.

    Примечание

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

  • Когда Генерировать пример, основная опция программы выключена, rtmodel.h, сгенерировано, чтобы обеспечить интерфейс между основным модулем и сгенерированным типовым кодексом. Если бы вы создаете свой собственный статический основной программный модуль, вы обычно включали бы rtmodel.h.

    Также можно подавить генерацию rtmodel.h и включать model.h непосредственно в основной модуль. Чтобы подавить генерацию rtmodel.h, используйте следующий оператор в своем системном конечном файле:

    %assign AutoBuildProcedure = 0
  • Если вы очистились, Оконечная функция потребовала опции, удалите или прокомментируйте следующее в своей производственной версии rt_main.c, rt_malloc_main.c или rt_cppclass_main.cpp:

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

    • Вызов MODEL_TERMINATE

  • Для rt_main.c (не применимый к rt_cppclass_main.cpp): Если вы не хотите комбинировать функции вывода и обновления, очистите опцию Single output/update function и внесите следующие изменения в вашей производственной версии rt_main.c:

    • Замените вызовы MODEL_STEP с вызовами MODEL_OUTPUT и MODEL_UPDATE.

    • Удалите проверку на ошибки #if ONESTEPFCN....

  • Статический модуль rt_main.c не поддерживает упаковку интерфейса Reusable function кода. Следующая проверка на ошибки повышает ошибку времени компиляции, если упаковка интерфейса Reusable function кода используется незаконно.

    #if MULTI_INSTANCE_CODE==1

Измените статическое основное, чтобы выделить и доступ к образцовым данным об экземпляре

Если вы используете статический основной программный модуль, и ваша модель сконфигурирована для упаковки интерфейса Reusable function кода, но образцовая опция, Use dynamic memory allocation for model initialization не выбран, образцовые данные об экземпляре, должна быть выделена или статически или динамически вызывающим основным кодом. Указатели на отдельные структуры данных модели (такие как Блок IO, DWork и Параметры) должны быть настроены в структуре данных модели реального времени верхнего уровня.

Чтобы поддержать основные модификации, процесс сборки генерирует подмножество следующих макросов модели реального времени (RTM), на основе требований к данным вашей модели, в model.h.

Синтаксис макроса RTMОписание
rtmGetBlockIO(rtm)

Получите структуру данных блока I/O

rtmSetBlockIO(rtm,val)

Установите структуру данных блока I/O

rtmGetContStates(rtm)

Получите непрерывную структуру данных состояний

rtmSetContStates(rtm,val)

Установите непрерывную структуру данных состояний

rtmGetDefaultParam(rtm)

Получите структуру данных параметров по умолчанию

rtmSetDefaultParam(rtm,val)

Установите структуру данных параметров по умолчанию

rtmGetPrevZCSigState(rtm)

Получите предыдущую структуру данных состояния сигнала пересечения нулем

rtmSetPrevZCSigState(rtm,val)

Установите предыдущую структуру данных состояния сигнала пересечения нулем

rtmGetRootDWork(rtm)

Получите структуру данных DWork

rtmSetRootDWork(rtm,val)

Установите структуру данных DWork

rtmGetU(rtm)

Доберитесь корень вводит структуру данных (когда корневые входные параметры передаются как часть структуры данных модели),

rtmSetU(rtm,val)

Установите корневую входную структуру данных (когда корневые входные параметры будут переданы как часть структуры данных модели),

rtmGetY(rtm)

Получите корневую выходную структуру данных (когда корневые выходные параметры будут переданы как часть структуры данных модели),

rtmSetY(rtm,val)

Установите корневую выходную структуру данных (когда корневые выходные параметры будут переданы как часть структуры данных модели),

Используйте их макросы в вашей статической основной программе, чтобы получить доступ к отдельным структурам данных модели в структуре данных RTM. Например, предположите, что модель rtwdemo_reusable в качестве примера сконфигурирована с упаковкой интерфейса Reusable function кода, очищенный Use dynamic memory allocation for model initialization, набор Pass root-level I/O as к Individual arguments и опция панели Optimization очищенный Remove root level I/O zero initialization. Создавание модели генерирует следующие структуры данных модели и образцовые точки входа в rtwdemo_reusable.h:

/* Block states (auto storage) for system '<Root>' */
typedef struct {
  real_T Delay_DSTATE;                 /* '<Root>/Delay' */
} D_Work;

/* Parameters (auto storage) */
struct Parameters_ {
  real_T k1;                           /* Variable: k1
                                        * Referenced by: '<Root>/Gain'
                                        */
};

/* Model entry point functions */
extern void rtwdemo_reusable_initialize(RT_MODEL *const rtM, real_T *rtU_In1,
  real_T *rtU_In2, real_T *rtY_Out1);
extern void rtwdemo_reusable_step(RT_MODEL *const rtM, real_T rtU_In1, real_T
  rtU_In2, real_T *rtY_Out1);

Кроме того, если Generate an example main program не выбран для модели, rtwdemo_reusable.h содержит определения для макросов RTM rtmGetDefaultParam, rtmsetDefaultParam, rtmGetRootDWork и rtmSetRootDWork.

Кроме того, для ссылки сгенерированный файл rtmodel.h содержит определение параметра в качестве примера с начальными значениями (не выполняющий код):

#if 0

/* Example parameter data definition with initial values */
static Parameters rtP = {
  2.0                                  /* Variable: k1
                                        * Referenced by: '<Root>/Gain'
                                        */
};                                     /* Modifiable parameters */

#endif

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

static RT_MODEL rtM_;
static RT_MODEL *const rtM = &rtM_;    /* Real-time model */
static Parameters rtP = {
  2.0                                  /* Variable: k1
                                        * Referenced by: '<Root>/Gain'
                                        */
};                                     /* Modifiable parameters */

static D_Work rtDWork;                 /* Observable states */

/* '<Root>/In1' */
static real_T rtU_In1;

/* '<Root>/In2' */
static real_T rtU_In2;

/* '<Root>/Out1' */
static real_T rtY_Out1;

В теле вашей основной функции вы могли использовать следующие макро-вызовы RTM, чтобы настроить параметры модели и данные DWork в структуре данных модели реального времени:

int_T main(int_T argc, const char *argv[])
{
...
/* Pack model data into RTM */

rtmSetDefaultParam(rtM, &rtP);
rtmSetRootDWork(rtM, &rtDWork);

/* Initialize model */
rtwdemo_reusable_initialize(rtM, &rtU_In1, &rtU_In2, &rtY_Out1);
...
}

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

Уровень, группирующий проблемы соответствия и проблемы совместимости

Основная программная совместимость

Когда Генерировать пример, основная опция программы выключена, генерация кода, производит немного отличающийся код группировки уровня для совместимости с более старым статическим модулем ert_main.c. Смотрите, что Уровень Группируется и Статическая Основная Программа для деталей.

Сделайте свой уровень 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)

Чтобы сконфигурировать модель, чтобы сгенерировать код, который задает и использует этот макрос, необходимо создать усовершенствованный пользовательский класс памяти и код Компилятора выходного языка (TLC) записи. Для примера, который показывает, как использовать Custom Storage Class Designer, не пишущий код TLC, смотрите, Создают и Применяют Пользовательский Класс памяти.

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

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

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

0x8675309

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

(uint16_T *)0x8675309

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

(volatile const uint16_T *)0x8675309

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

*(volatile const uint16_T *)0x8675309

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Для MemoryMappedAddress, на вкладке "Общие", наборе:

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

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

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

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

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

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

После того, как вы закончите выбирать настройки, нажмите Apply и Save.

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

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

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

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

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

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

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

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

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

В папке +MemoryMap создайте папку под названием tlc.

Перейдите к новой папке.

Осмотрите встроенный файл шаблона TLC, TEMPLATE_v1.tlc.

edit(fullfile(matlabroot,...
    'toolbox','rtw','targets','ecoder','csc_templates','TEMPLATE_v1.tlc'))

Сохраните копию TEMPLATE_v1.tlc в папке tlc. Переименуйте копию memory_map_csc.tlc.

В memory_map_csc.tlc найдите фрагмент, который управляет генерацией объявлений данных кода С.

    %case "declare"

      %% LibDefaultCustomStorageDeclare is the default declare function to
      %% declares a global variable whose identifier is the name of the data.
      %return "extern %<LibDefaultCustomStorageDeclare(record)>"
      %%break

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

Случай declare (%case) создает возвращаемое значение (%return), который генератор кода испускает в заголовочный файл, который вы задаете для каждого элемента данных. Чтобы управлять кодом С, который объявляет каждый элемент данных, настройте возвращаемое значение в случае declare.

Замените существующее содержимое %case на этот новый код, который задает различное возвращаемое значение:

%case "declare"

        %% In TLC code, a 'record' is a data item (for example, a signal line).
        %% 'LibGetRecordIdentifier' returns the name of the data item.
        %assign id = LibGetRecordIdentifier(record)

        %assign dt = LibGetRecordCompositeDataTypeName(record)

        %% The 'CoderInfo' property of a data item stores a
        %% 'Simulink.CoderInfo' object, which stores code generation settings
        %% such as the storage class or custom storage class that you specify
        %% for the item.
        %assign ci = record.Object.ObjectProperties.CoderInfo
        %% The 'ci' variable now stores the 'Simulink.CoderInfo' object.
        
        %% By default, the 'CustomAttributes' property of a 'Simulink.CoderInfo'
        %% object stores a 'Simulink.CustomStorageClassAttributes' object.
        %% This nested object stores specialized code generation settings
        %% such as the header file and definition file that you specify for
        %% the data item.
        %%
        %% The 'MemoryMap' package derives a new class, 
        %% 'MemoryMapAttribs', from 'Simulink.CustomStorageClassAttributes'.
        %% The new class adds a property named 'MemoryAddress'.
        %% This TLC code determines the memory address of the data item by
        %% acquiring the value of the 'MemoryAddress' property.
        %assign ca = ci.Object.ObjectProperties.CustomAttributes
        %assign address = ca.Object.ObjectProperties.MemoryAddress

        %assign width = LibGetDataWidth(record)

        %% This TLC code constructs the full macro, with correct C syntax,
        %% based on the values of TLC variables such as 'address' and 'dt'.
        %% This TLC code also asserts that the data item must be a scalar.
        %if width == 1
                %assign macro = ...
                    "#define %<id> ((*(volatile const %<dt>*)%<address>) & 0x03FF)"
        %else
                %error( "Non scalars are not supported yet." )
        %endif

        %return "%<macro>"
      %%break

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

Новое использование кода TLC встроенные, зарегистрированные функции TLC, такие как LibGetRecordIdentifier, и другие команды TLC и операции, чтобы получить доступ к информации об элементе данных. Временные переменные, такие как dt и address хранят ту информацию. Код TLC создает полный макрос, с правильным Синтаксисом C, путем расширения переменных, и хранит макрос в переменной macro.

В том же файле найдите фрагмент, который управляет генерацией определений данных.

    %case "define"

      %% LibDefaultCustomStorageDefine is the default define function to define
      %% a global variable whose identifier is the name of the data.  If the
      %% data is a parameter, the definition is also statically initialized to
      %% its nominal value (as set in MATLAB).
      %return "%<LibDefaultCustomStorageDefine(record)>"
      %%break

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

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

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

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

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

MemoryMappedAddress приводит к макросу в сгенерированном коде, таким образом, вы используете случай declare вместо случая define, чтобы создать и испустить макрос. Чтобы препятствовать тому, чтобы случай define испустил дублирующееся макроопределение, новый код TLC возвращает пустую строку.

Найдите фрагмент, который управляет генерацией кода, который инициализирует данные.

    %case "initialize"

      %% LibDefaultCustomStorageInitialize is the default initialization
      %% function that initializes a scalar element of a global variable to 0. 
      %return LibDefaultCustomStorageInitialize(record, idx, reim)
      %%break

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

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

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

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

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

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

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

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

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

Откройте Custom Storage Class Designer снова.

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

Для MemoryMappedAddress, на вкладке Other Attributes, наборе:

  • Имя файла TLC к memory_map_csc.tlc.

  • CSC приписывает класс MemoryMap.MemoryMapAttribs.

Нажмите Apply и Save.

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

Чтобы применить пользовательский класс памяти к сигналу в модели, в пакете MemoryMap, необходимо создать класс MATLAB, который выводит от Simulink.Signal. Когда вы конфигурируете сигнал в модели, вы выбираете этот новый класс данных вместо класса по умолчанию, Simulink.Signal.

В пакете MemoryMap создайте папку под названием @Signal.

В папке @Signal создайте файл с именем Signal.m.

classdef Signal < Simulink.Signal
    methods
        function setupCoderInfo( this )
            useLocalCustomStorageClasses( this, 'MemoryMap' );
            return;
        end
    end
end

Файл задает класс под названием MemoryMap.Signal. Определение класса заменяет метод setupCoderInfo, который уже реализует класс Simulink.Signal. Новая реализация указывает, что объекты класса MemoryMap.Signal используют пользовательские классы памяти от пакета MemoryMap (вместо пользовательских классов памяти от пакета Simulink). Когда вы конфигурируете сигнал в модели путем выбора класса MemoryMap.Signal, можно выбрать новый пользовательский класс памяти, MemoryMappedAddress.

Примените пользовательский класс памяти к сигнальной линии

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

В модели выберите View> Property Inspector.

Кликните по сигналу под названием A2D_INPUT.

В Property Inspector, под Генерацией кода, классом объекта Сигнала набора к MemoryMap.Signal. Если вы не видите MemoryMap.Signal, выберите Customize class lists и используйте диалоговое окно, чтобы включить выбор MemoryMap.Signal.

В Property Inspector, Классе памяти набора к MemoryMappedAddress.

Установите Заголовочный файл на memory_mapped_addresses.h.

Установите MemoryAddress на 0x8675309.

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

Сгенерируйте код из модели.

### Starting build procedure for model: ex_memmap_simple
### Successful completion of build procedure for model: ex_memmap_simple

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

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

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

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

Похожие темы