FLProg: Создание пользовательских блоков

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

В зоне библиотек функциональных блоков выбираем Пользовательские.

Выбираем значок

Появляется четыре варианта создания собственного блока.

Формат FBD

Формат LAD

Code-Basic

Code-Professional

Ограничения в использовании кода

1. Нельзя использовать объявление структуры кодом

typedef struct{

…}
test;

Необходимо заменить на:

struct test{


};

Пример создания блока на С

Проект FLProg в последнее время обрёл достаточно большую популярность, и мне перестало хватать времени на создание блоков для той периферии, которая необходима пользователям. В то же время среди пользователей сайта нашлось достаточное количество людей, которые неплохо разбираются в языке C и могли бы мне помочь в развитии программы. Я решил дать им соответствующий инструмент. Таким образом, в версии 1.10.3 появилась возможность создавать пользовательские блоки с интегрированным кодом на С. Это привело к довольно неожиданным результатам. Этим инструментом заинтересовались не только разбирающиеся в программировании пользователи, но и те, кто до этого ни писал не сточки кода. Они начали писать сначала простенькие блоки (например, получение логарифма – среди стандартных у меня такого блока не было), заканчивая уже серьёзными блоками с применением библиотек. Поэтому я хочу немного поломать задуманную последовательность уроков по работе с программой, и вне очереди расскажу, как создавать подобные блоки.

На уроке будет создан блок для цифрового компаса HMC5883L. За основу блока была взята эта статья. Библиотека взята отсюда.
Итак, запускаем программу и создаём новый проект. В библиотеке элементов переходим на закладку «Пользовательские» и выделяем ветку дерева библиотеки пользовательских блоков, где будет располагаться новый блок. Затем нажимаем кнопку «Создать блок»

Откроется окно с выбором языка программирования для блока. Доступны языки FBD, LAD и Code. Языки LAD и FBD будут рассмотрены в других уроках, а сейчас выбираем «Code».

Открывается окно редактора блока пользователя. В верхней части находятся закладки параметров (1), в нижней закладки секций кода (2).

Заполняем основные параметры.

Входы и выходы блока создаются аналогично. Входов у нашего блока не будет, поэтому попускаем эту закладку и переходим к закладке «Выходы блока». У блока будет три выхода – направление по оси X, Y, и Z. Для создания выхода нажимаем кнопку «Добавить выход».

Откроется окно создания входа. Оно очень похоже на окно создания переменной в основной программе, поэтому подробно описывать его не буду. Значения на выход блока подаются в формате Float – поэтому тип входов выбираем такой — же. К названию выхода применяются ограничения, применяющиеся к названию переменных в С., поскольку оно будет фигурировать в коде. В написании комментариев никаких ограничений нет.

Таким же образом создаём остальные выходы.

Теперь зададим параметр, который пользователь будет задавать при использовании блока. Это будет чувствительность датчика. В соответствии с описанием она выбирается из ряда: 0.88, 1.3, 1.9, 2.5, 4.0, 4.7, 5.6, 8.1

Переходим на закладку «Параметры пользователя» и нажимаем кнопку «Добавить параметр».

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

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

Теперь займёмся библиотекой. Перейдём на закладку «Библиотеки». Используемые в работе блока библиотеки можно загрузить непосредственно в блок. Для этого необходимо нажать кнопку «Загрузить библиотеку».

Откроется окно выбора папки с библиотекой.

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

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

На закладке «Описание» желательно написать, как работать с блоком, для чего он предназначен и т.д. Это описание поможет другим пользователям использовать его.

Теперь перейдём в зону кода

Первая закладка – секция «DeclareSection». На этой закладке необходимо прописать подключение необходимых библиотек, объявление переменных, массивов и структур. Заполним эту секцию, ориентируясь на пример из библиотеки.

Код секции Declare из примера.

/*
HMC5883L_Example.pde - Example sketch for integration with an HMC5883L triple axis magnetomerwe.
Copyright (C) 2011 Love Electronics (loveelectronics.co.uk)

This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

// Reference the I2C Library
#include <Wire.h>
// Reference the HMC5883L Compass Library
#include <HMC5883L.h>

// Store our compass as a variable.
HMC5883L compass;
// Record any errors that may occur in the compass.
int error = 0;

// Out setup routine, here we will configure the microcontroller and compass.

При переносе в блок убраны лишние комментарии, хотя ограничений по их применению никаких отличных от ограничений самого языка С нет. Так же добавлены некоторые переменные, которые пригодятся позже в коде. В оригинальном примере они объявлены в секции Loop. Здесь они перенесены в секцию Declare.

На сегодняшний день в разделе Declare можно использовать объявления переменных любых типов, объявление массивов и структур. Корректно обрабатывается директива #define.
Нельзя использовать объявление такого вида:

typedef struct{
...
...}
test;

Его необходимо переделать в такой вид:

struct test{
...

...
};

Нельзя использовать тип extern. Это оказалась достаточно большой проблемой, и я надеюсь в ближайшее время её решить.

Теперь перейдём к секции «SetupSection». Здесь код переносится из примера практически один к одному.

Код из примера

void setup()
{
  // Initialize the serial port.
  Serial.begin(9600);

  Serial.println("Starting the I2C interface.");
  Wire.begin(); // Start the I2C interface.

  Serial.println("Constructing new HMC5883L");
  compass = HMC5883L(); // Construct a new HMC5883 compass.
    
  Serial.println("Setting scale to +/- 1.3 Ga");
  error = compass.SetScale(1.3); // Set the scale of the compass.
  if(error != 0) // If there is an error, print it out.
    Serial.println(compass.GetErrorText(error));
  
  Serial.println("Setting measurement mode to continous.");
  error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
  if(error != 0) // If there is an error, print it out.
    Serial.println(compass.GetErrorText(error));
}

Из примера убраны заголовок секции и ограничивающие секцию скобки (при компиляции они вставляются программой). Так же убран весь диагностический вывод в компорт. Вот что получилось.

Обратите внимание, что вместо параметра точности вставлено имя параметра «Точность». При компиляции блока вместо этого имени будет вставлено значение, которое ввёл пользователь.

Теперь перейдём к секции Loop.

Оригинальный код из примера библиотеки

void loop()
{
  // Retrive the raw values from the compass (not scaled).
  MagnetometerRaw raw = compass.ReadRawAxis();
  // Retrived the scaled values from the compass (scaled to the configured scale).
  MagnetometerScaled scaled = compass.ReadScaledAxis();
  
  // Values are accessed like so:
  int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)

  // Calculate heading when the magnetometer is level, then correct for signs of axis.
  float heading = atan2(scaled.YAxis, scaled.XAxis);
  
  // Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
  // Find yours here: http://www.magnetic-declination.com/
  // Mine is: 2” 37' W, which is 2.617 Degrees, or (which we need) 0.0456752665 radians, I will use 0.0457
  // If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
  float declinationAngle = 0.0457;
  heading += declinationAngle;
  
  // Correct for when signs are reversed.
  if(heading < 0)
    heading += 2*PI;
    
  // Check for wrap due to addition of declination.
  if(heading > 2*PI)
    heading -= 2*PI;
   
  // Convert radians to degrees for readability.
  float headingDegrees = heading * 180/M_PI; 

  // Output the data via the serial port.
  Output(raw, scaled, heading, headingDegrees);

  // Normally we would delay the application by 66ms to allow the loop
  // to run at 15Hz (default bandwidth for the HMC5883L).
  // However since we have a long serial out (104ms at 9600) we will let
  // it run at its natural speed.
  // delay(66);
}

Так же как в секции Setup убираем заголовок секции и ограничивающие скобки. Они буду вставлены в код автоматически программой. Так же убираем объявление переменных в секции Loop. В этой секции очень не желательно объявлять временные переменные, поскольку если блок будет использоваться в проекте несколько раз, то произойдёт ошибка повторного декларирования переменной.
В результате получается такой код:

В конце кода мы вызываем функцию Output (raw, scaled, heading, headingDegrees). В принципе можно было обойтись без неё, но я решил оставить её для примера создания функции. Поэтому переходим на закладку «FunctionSection»
Для создания новой функции нажимаем кнопку «Добавить функцию».

Откроется окно ввода заголовка функции, куда вводим её определение.

После создания заголовка пишем код функции.

Оригинальный код функции из примера

void Output(MagnetometerRaw raw, MagnetometerScaled scaled, float heading, float headingDegrees)
{
   Serial.print("Raw:\t");
   Serial.print(raw.XAxis);
   Serial.print("   ");   
   Serial.print(raw.YAxis);
   Serial.print("   ");   
   Serial.print(raw.ZAxis);
   Serial.print("   \tScaled:\t");
   
   Serial.print(scaled.XAxis);
   Serial.print("   ");   
   Serial.print(scaled.YAxis);
   Serial.print("   ");   
   Serial.print(scaled.ZAxis);

   Serial.print("   \tHeading:\t");
   Serial.print(heading);
   Serial.print(" Radians   \t");
   Serial.print(headingDegrees);
   Serial.println(" Degrees   \t");
}

Уберём все относящееся к выводу в компорт, а значения присвоим выходам блока. Ну и как обычно уберём заголовок и ограничивающие скобки. Вот что получится в результате.

Работа над блоком закончена.

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

Завершаем работу нажатием кнопки «Выход».

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

Code- Professional

Редактор пользовательских блоков на С (Professional).

Общие термины и понятия.

Редактор предназначен для создания пользовательских блоков на языке С. В отличие от редактора Basic, он имеет расширенный функционал, и дополнительные возможности. Редактор доступен в программе FLProg, начиная с версии 7.0.

Основное окно редактора.

Блок, созданный в редакторе, может представлять собой как один блок, так и пакет блоков объединённых общим функционалом. В случае если в пакете содержится один блок, то в библиотеке пользовательских блоков он и представляется как один блок. Если в пакете содержится несколько блоков, то в библиотеке они представляются как папка. Порядок блоков в этой папке, названия, разбивка по подпапкам задается на соответствующей ветке в дереве редактора блока.

Поэтому пользовательский блок, созданный в этом редакторе, далее будем называть мультиблок.

Отдельные блоки, входящие в пакет далее будут называться блоками.

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

Блоки перенесённые на схему проекта называются экземплярами блока

Общий принцип построения мультиблока

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

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

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

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

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

При компиляции проекта, для мультиблока формируется два вида вставляемого кода. Общий код, и код каждого экземпляра

Общий код вставляется в скетчь ОДИН раз вне зависимости от количества применённых в проекте экземпляров блоков.

Код каждого экземпляра вставляется в скетчь для каждого экземпляра блока.

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

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

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

Общий код блока вставляется в скетч один раз при использовании экземпляров данного блока в проекте.

Код каждого экземпляра вставляется в скетч для каждого экземпляра блока, применяемого в проекте

Каждый из этих видов кода разделяется на секции, в соответствии с местом вставки в скетчь

Это секции “Declare”, “Setup”, “Loop”, “Functions”. Они соответствуют секциям в скетче в Arduino IDE.

Каждая секция разбита на участки в соответствии с местом вставки кода в секцию.

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

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

Код секции “Functions” вставляется всегда в конец скетча.

Для кода каждого инстанса в секции “Loop” существует дополнительный участок Место вставки блока. Код из этого участка вставляется в секции Loop скетча в месте вставки конкретного экземпляра блока.

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

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

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

Общие условия мультиблока могут использовать только общие параметры мультиблока, и системные значения проекта. Эти условия могут использоваться для управления в любом инстансе, и для любого элемента кода.

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

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

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

Все используемые инстансами библиотеки загружаются в мультиблок.

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

Ссылка на источник: https://habr.com/ru/company/flprog/blog/390267/