Встраивание в сгенерированный код пользовательских функций для привязки к специфичному
оборудования
SimInTech позволяет встраивать в автоматически сгенерированный Си-код пользовательские
фрагменты программ, которые обеспечивают привязку и взаимодействие с определенной апаратурой.
Для этого используется блок 0985.dita#.
Скрипт, написанный внутри данного блока на встроенном языке программирования исполняется
локально, однако для вставки в сгенерированную программу пользовательского Си-кода (для работы
с АЦП, ЦАП, UART и т.д.) имеется набор текстовых свойств, позволяющих вручную формировать
участки кода, вставляемые при генерации в определенные места (header, init, основная
секция...).
Рисунок 1. Свойства блока "Язык программирования"
Прим.: для того, чтобы заданный в свойствах ниже код вставлялся в сгенерированную
программу, необходимо чтобы свойство "Транслировать скрипт в код автоматически" было
установлено в Нет.
Прим.: если в разрабатываемом блоке будут иметься выходы,
мгновенно зависящие от входов, то рекомендуется задать свойству "Тип сортировки" значение
"Функциональный".
Свойства "Заголовок модуля", "Секция декларации переменных",
"Основная секция кода", "Секция запоминания состояний", "Декларация переменных DBM", "Значения
переменных по умолчанию", "Код вычисления производных", "Код вычисления алгебраических
переменных", "Код остановки задачи" являются текстовыми и предназначены для заполнения
фрагментами пользовательского кода. Рассмотрим их подробнее:
- Заголовок модуля (header) пользовательский Си-код, предназначенный для
непосредственной вставки в заголовочный файл генерируемой программы. Здесь можно,
например, декларирывать пользовательские функции, переменные, константы, типы данных и
т.д.
- Секция декларации переменных (vars) - секция, позволяющая осуществлять декларацию
дополнительных переменных.
- Основная секция кода (code) - пользовательский Си-код, предназначенный для
непосредственной вставки в секцию кода генерируемой программы, исполняемую на каждом шаге.
Здесь можно, например, присваивать выходы блока, читать входы блока, вызывать объявленные
в заголовке процедуры и т.д.
- Секция запоминания состояний (state) - пользовательский Си-код, предназначенный
для непосредственной вставки в секцию state кода генерируемой программы. Исполнение данной
секции происходит после выполнения основной секции кода. Здесь могут запоминаться
состояния динамических переменных для интегрируемых блоков.
- Декларация переменных DBM (dbm) - секция позволяющая вставлять переменные в
структуры, которые автоматически выделяются кодогенератором. Для большинства случаев
привязки к аппаратуре такой тип декларации использовать нецелесообразно.
- Значения переменных по умолчанию (data) - пользовательский Си-код,
предназначенный для непосредственной вставки в секцию инициализации генерируемой
программы. Здесь могут быть декларированы локальные переменные, необходимые для
инициализации программы. Данный код вызывается один раз при запуске расчета аналогично
секции initialization встроенного скриптового языка.
- Код вычисления производных (deri_code) - вызывается только для блоков имеющих
динамические переменные состояния, объявленные, например, локально с помощью ключевого
слова Init.
- Код остановки задачи (stop_code) - выполняется только один раз, при остановке
задачи. Аналогичен секции finalization встроенного скриптового
языка.
- Код вычисления алгебраических переменных (alg_code) - секция выполняется в случае
если в блоке происходит решение алгебраических уравнений.
- Типы выходов (outtypes) - опция для принудительного назначения типа выходов
блока. В случае, если данное свойство имеет какое-либо значение, то оно будет более
приоритетным, чем тот тип выходов, который указан внутри скрипта блока "Язык
программирования".
Как правило, большинство задач подключения и взаимодействия со специфическим
оборудованием можно решить, используя лишь задание кода в свойствах
header,
code,
data.
Пример создания пользовательского блока для связи с оборудованием
Создание нового блока для связи с оборудованием предлагается производить на основе
существующего шаблона. В качестве шаблона можно использовать, например, блок из библиотеки
"Миландр K1986BE92QI".
Найдем и поместим на схемное окно блок 5223.dita#id5223 из вкладки "Миландр
K1986BE92QI" палитры блоков.
Рассмотрим данный блок подробнее.Рисунок 2. Блок инициализации порта в схемном окне
Данный блок создан на основе типового блока "Язык программирования". Рассмотрим его,
начиная со списка свойств. Открыв окно свойств блока можно увидеть, что список его свойств
отличается от списка свойств блока "Язык программирования", рассмотренного
выше.
Рисунок 3. Список свойств блока инициалиации порта
Однако, если выделить блок и выполнить команду главного меню
Правка →
Изменить блок, то на вкладке "Свойства" (
Рисунок 4) можно увидеть не только видимые
свойства (
Рисунок 3), но и те, которые
описаны
вначале.
Рисунок 4. Окно редактора блока "Инициализация порта"
Стандартные свойства блока были скрыты с помощью свойства InvisibleProps
на вкладке "Общие", а ряд новых, пользовательских, был вновь добавлен. Подробнее о том, как
это сделано и о работе со свойствами блока описывается в соответствующем
материале.
Значения пользовательских свойств блока используются в скрипте для
локального
динамического формирования пользовательского Си-кода. Чтобы увидеть внутренний скрипт
блока, необходимо разблокировать его. Для этого необходимо щелкнуть по нему правой кнопкой
мыши и выбрать
Действия → Разблокировать.
Рисунок 5. Разблокировка блока "Язык программирования"
После этого, двойным щелчком левой кнопки по блоку можно попасть в редактор скрипта
блока и ознакомиться с ним.
Рисунок 6. Редактор скрипта блока
Текст скрипта начинается с объявления выходов
блока:
output ready:boolean=1;
Прим.: соединение
блоков с помощью линий связи в данном случае используется исключительно для определения
порядка сортировки блоков, чтобы пользовательские участки кода в итоговой программе были
расположены правильно относительно друг друга (например для избежания ситуации обращения в
порту до его инициализации). При этом сами значения, передающиеся по линиям связи не несут
никакой информации.
Ниже начинается секция
initialization, которая
выполняется только один раз - при запуске расчета. В ней происходит декларация необходимых
переменных и констант для локального расчета, а также присвоение локальным переменным
значений свойств
блока:
var a: intarray;
var number:integer;
const pins_mean = [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768];
const base:intarray = [0x400A8000, 0x400B0000, 0x400B8000, 0x400C0000, 0x400C8000, 0x400E8000];
var bits :integer = 0, setter: integer = 0;
var mode :intarray;
var func :intarray;
var in_out:intarray;
var speed :intarray;
var port_ :intarray;
a = Pins;
mode = PORT_MODE;
func = PORT_FUNC;
in_out = PORT_OE;
speed = PORT_SPEED;
port_ = PORT;
bits = 0;
Ниже
формируется битовая маска:
for(i=1,cols(a)) bits = bits or pins_mean[a[i] + 1];
После
чего происходит непосредственно формирование пользовательского Си-кода в виде текстовых
переменных и присвоение его соответсвующим типовым свойствам блока "Язык программирования"
(которые скрыты в нашем
случае).
header = "";
code = "";
data =
"
/*Инициализация порта*/
Init_port("+inttostr(bits) + "," +inttostr(mode[1]) + "," + inttostr(func[1]) + "," + inttostr(in_out[1]) + "," + inttostr(speed[1]) + ",(MDR_PORT_TypeDef*)" + base[port_[1] + 1] + ");
%out:0% = 0x01;
";
Таким
образом, при инициализации локального скрипта происходит формирование пользовательского
Си-кода в зависимости от значений пользовательских же свойств.
Используя данный блок в качестве шаблона, создадим свой. Для начала изменим список
свойств блока: удалим пользовательские свойства, специфичные для шаблонного блока и создадим
новое свойство. Эти операции выполняются с помощью окна "Редактирование блока", которое
открывается после выделения блока мышью и выполнения команды главного меню
Правка →
Изменить блок. Отредактируем список свойств так, чтобы он был похож на
представленный ниже (
Рисунок 7).
Рисунок 7. Список свойств нового блока
Таким образом было создано пользовательское свойство с именем
mydata1, имеющее формат целого числа.
Прим.: обратите внимание, что удалять
свойства, специфичные для блока "Язык программирования" (
Рисунок 1) нельзя! Если их отображение в
окне "Свойства блока" необязательно, что справедливо при заполнении свойств из локального
скрипта, то их можно скрыть, перечислив их имена через точку с запятой в свойстве
InvisibleProps на вкладке "Общие".
После корректировки списка свойств,
произведем корректировку локального скрипта блока. Перед секцией инициализации определим
выход блока, затем внутри секции инициализации произведем декларацию локальных переменных,
их предварительные преобразования и, наконец, присвоение собственным свойствам блока "Язык
программирования" текстовых значений пользовательского кода. Итоговый тестовый скрипт блока
с
комментариями:
output adc_result: integer;
initialization
var temp: integer;
temp = mydata1*2;
header = "
static int %unikname%_aaabbb;
const int %unikname%_bbbaaa = " + inttostr(temp) + ";
int %unikname%_MyProc()
{
return %unikname%_aaabbb;
};
";
code = "
%out:0% = %unikname%_MyProc();
";
data = "
%out:0% = 0x01;
%unikname%_aaabbb = %unikname%_bbbaaa;
";
adc_result=0;
end;
В
результате данных действий был сделан блок, который при кодогенерации формирует
пользовательский Си-код для взаимодействия со специфическим оборудованием.
После этого для созданного блока можно задать уникальный "Тип элемента" (ClassName), а
также изменить "Графическое изображение" (Graphics), с помощью соответствующих свойств а
вкладке "Общие".
Рисунок 8. Свойство "Тип элемента"
Рисунок 9. Свойство "Графическое изображение"
Таким образом, можно создать набор пользовательских блоков для решения задач
взаимодействия с конкретным оборудованием и оформить их в виде соответствующей библиотеки.
Работа с библиотекой блоков описана в соответсвующем
материале.