Подключение к аппаратуре путем встраивания пользовательского кода

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

SimInTech позволяет встраивать в автоматически сгенерированный Си-код пользовательские фрагменты программ, которые обеспечивают привязку и взаимодействие с определенной апаратурой. Для этого используется блок Язык программирования.

Скрипт, написанный внутри данного блока на встроенном языке программирования исполняется локально, однако для вставки в сгенерированную программу пользовательского Си-кода (для работы с АЦП, ЦАП, UART и т.д.) имеется набор текстовых свойств, позволяющих вручную формировать участки кода, вставляемые при генерации в определенные места (header, init, основная секция...).

Рисунок 1. Свойства блока "Язык программирования"

Примечание: для того, чтобы заданный в свойствах ниже код вставлялся в сгенерированную программу, необходимо чтобы свойство "Транслировать скрипт в код автоматически" было установлено в Нет.
Примечание: если в разрабатываемом блоке будут иметься выходы, мгновенно зависящие от входов, то рекомендуется задать свойству "Тип сортировки" значение "Функциональный".
Свойства "Заголовок модуля", "Секция декларации переменных", "Основная секция кода", "Секция запоминания состояний", "Декларация переменных DBM", "Значения переменных по умолчанию", "Код вычисления производных", "Код вычисления алгебраических переменных", "Код остановки задачи" являются текстовыми и предназначены для заполнения фрагментами пользовательского кода. Рассмотрим их подробнее: Как правило, большинство задач подключения и взаимодействия со специфическим оборудованием можно решить, используя лишь задание кода в свойствах header, code, data.
Примечание: при формировании текста пользовательского кода не обзязательно задавать его вручную через редактор свойств блока "Язык программирования", это можно делать также из секции initialization скрипта внутри блока, используя простую операцию присвоения текста программы свойству с соответствующим именем. Например:
initialization
 var bits: integer = 0;
 var func: intarray = [1, 0, 1];
 var in_out: intarray = [0, 0, 0];
 data =
 "
 /*Инициализация порта*/
 Init_port("+inttostr(bits)+", "+inttostr(func[1])+","+inttostr(in_out[1])+");
 %out:0% = 0x01;						
 ";
end;
В результате выполнения данного фрагмента скрипта свойству data (Значения переменных по умолчанию) будет присвоено соответствующее текстовое значение. При этом текстовые значения свойств можно формировать динамическим образом, с помощью операции сложения переменных типа string.
Для формирования уникальных имен переменных в пользовательском коде доступные следующие символы и префиксы, которые заменяются в процессе генерации кода:
Ключевое слово символа или префикса Значение
%blockname% уникальное имя блока в пределах данной схемы
%unikname% уникальное имя в пределах всего проекта (<префикс><имя алгоритма><blockname>)
%out_global% имя глобала переменных входа-выхода
%state_global% имя глобала переменных состояния блока
%input:<номер порта>% имя переменной порта входа блока (начиная с нуля)
%out:<номер порта>% имя переменной порта выхода блока (начиная с нуля)
%value:<имя переменной>% подстановка значения локальной (или глобальной переменной)
%system% имя системы компонента
%component% имя компонента
%prefix% префикс имени переменных (v)
%step% имя переменной "Шаг интегрирования"
%time% имя переменной "Время", в секундах
chr(34) кавычка (")

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

Создание нового блока для связи с оборудованием предлагается производить на основе существующего шаблона. В качестве шаблона можно использовать, например, блок из библиотеки "Миландр K1986BE92QI".

Найдем и поместим на схемное окно блок Инициализация порта из вкладки "Миландр 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];

//Вычисляем 32 битное число с битами
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. Свойство "Графическое изображение"

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