Введение, постановка проблемы. Современное производство предъявляет все более высокие требования к инструментам управления, контроля и автоматики. Персональные компьютеры применяются в настоящее время на всех уровнях промышленной автоматизации. Для большинства задач автоматизации применение промышленных ПК не оправдано экономически и технически сложно. Решать задачи автоматизации максимально просто, в кратчайшие сроки и с минимальным привлечением специалистов в области прикладного и системного программного обеспечения позволяют программируемые логические контроллеры (ПЛК). ПЛК — это программно управляемый дискретный автомат, имеющий некоторое множество входов, подключенных посредством датчиков к объекту управления, и множество выходов, подключенных к исполнительным устройствам. ПЛК контролирует состояния входов и вырабатывает определенные последовательности программно заданных действий, отражающихся в изменении выходов. ПЛК предназначен для работы в режиме реального времени в условиях промышленной среды и должен быть доступен для программирования неспециалистом в области информатики [1].
Программирование большинства современных ПЛК осуществляется на языках стандарта международной электротехнической комиссии (МЭК) 61131 [2]. Стандарт содержит описание аппаратных средств, требования к монтажу, тестированию, документации и связи по промышленной сети. Часть 3 стандарта описывает требования к языкам программирования, всего таких языков описывается пять: IL – ассемблероподобный текстовый язык; LD – графический язык релейно-контактных схем; ST – паскалеподобный текстовый язык; FBD – графический язык функциональных блоков; SFC – графический язык последовательных функциональных схем. Для программирования ПЛК не требуется знание всех пяти языков МЭК 61131-3, но выбор языка существенно влияет на способ реализации задачи.
В качестве компонентов организации программ в стандарте указываются функции, функциональные блоки и программы. Любой программный компонент обладает свойством инкапсуляции, т.е. работает как «черный ящик», скрывая детали своей реализации. Для работы с компонентом достаточно знать описание его входов и выходов, а особенности реализации внутреннего алгоритма работы знать необязательно. На базе свойства инкапсуляции программные компоненты позволяют осуществлять структурную декомпозицию проекта. На верхнем уровне функционируют крупные компоненты (программы), излишние подробности на этом уровне мешают комплексному пониманию проблемы. Изучая более мелкие (вложенные) компоненты, можно раскрыть детальное представление программной реализации алгоритма работы системы. Теоретически любой компонент можно изучить и исправить по своему усмотрению. Это, безусловно, относится только к открытым для просмотра и редактирования пользовательским компонентам или библиотекам.
Как правило, описание принципа реализации и алгоритма работы программных компонентов, включаемых даже в открытые библиотеки, производители инструментальных сред программирования ПЛК практикуют очень редко. Поскольку библиотеки открытые и бесплатные, то их использование и подробности функционирования отдаются на откуп пользователю. Информация по внутреннему принципу функционирования таких компонентов весьма скудная и ограничивается справочными данными. Тем не менее часто возникает необходимость адаптировать функциональные возможности компонента под конкретную задачу с целью минимизации программного кода, оптимизации алгоритма его реализации и упрощения программы в целом. Поэтому актуальной является задача описания и анализа работы таких наиболее распространенных программных блоков, описываемых на языках стандарта МЭК 61131.
3S CoDeSys [3] это одна из самых полных и удачно реализованных сред программирования МЭК 61131-3. CoDeSys обладает набором открытых функциональных библиотек, предназначенных для решения широкого круга наиболее распространенных задач автоматизации. Одна из таких библиотек - Util.lib содержит дополнительный набор различных функций и функциональных блоков, применяемых регуляторов, генераторов и преобразований аналоговых сигналов. Рассмотрим работу функционального блока (ФБ) GEN, входящего в состав данной библиотеки среды CoDeSys v2.3. Данный блок предназначен для генерации периодически изменяющихся сигналов различных предустановленных форм: треугольный (двуполярный и однополярный), двуполярный пилообразный (нарастающий и ниспадающий), меандр, синусоида и косинусоида.
Описание принципа работы функционального блока. Библиотека Util.lib имеет открытый программный код. Для доступа к коду функциональных блоков, входящих в библиотеку необходимо ее открыть из меню «Открыть» вновь созданного проекта. Листинг программы с авторскими комментариями и описанием используемых переменных представлен ниже.
(*функциональный блок для генерации некоторых периодических функций*)
FUNCTION_BLOCK GEN
(*Раздел описания переменных*)
VAR_INPUT (*входной интерфейс блока*)
MODE: GEN_MODE;
(*определение типа генерируемого сигнала:
TRIANGLE - сигнал треугольной формы от отрицательного значения амплитуды до положительного значения амплитуды сигнала.
TRIANGLE_POS - сигнал треугольной формы от нуля до положительного значения амплитуды сигнала.
SAWTOOTH_RISE - пилообразный сигнал увеличивающийся от отрицательного значения амплитуды до положительного значения амплитуды сигнала.
SAWTOOTH_FALL - пилообразный сигнал увеличивающийся от положительного значения амплитуды до отрицательного значения амплитуды.
RECTANGLE - сигнал прямоугольной формы от отрицательного значения амплитуды до положительного значения амплитуды сигнала.
SINUS - синусоидальный сигнал.
COSINUS - косинусоидальный сигнал. *)
BASE: BOOL;(* FALSE: период определяется как количество вызовов функционального блока; TRUE: период определяется заданным временем *)
PERIOD: TIME:=T#1s; (*время периода сигнала, учитывается только, если BASE=TRUE*)
CYCLES: INT:=1000; (*число вызовов блока за период, учитывается только, если BASE=FALSE *)
AMPLITUDE: INT; (*амплитуда сигнала*)
RESET: BOOL; (*обнуление выхода и остановка генерации сигнала*)
END_VAR
VAR_OUTPUT (*выходной интерфейс блока*)
OUT: INT; (*переменная генерируемого сигнала*)
END_VAR
VAR (*локальные переменные функционального блока*)
CET: DINT; (*переменная, используемая для отсчета времени генерируемого сигнала (шаг дискретизации по времени)*)
PER: DINT; (*переменная, определяющая длительность периода генерируемого сигнала *)
COUNTER: INT; (*счетчик вызовов блока*)
CLOCK: TON; (*экземпляр таймера TON*)
help: REAL; (*промежуточная переменная для вычисления значения синусоидального и косинусоидального сигналов*)
END_VAR
_____________________________________________________________________________
(* Программный код*)
IF RESET THEN
COUNTER:=0;
OUT:=0;
CLOCK.PT := t#0s;
CLOCK(IN:=FALSE);
ELSE
IF BASE=FALSE THEN
COUNTER:=(COUNTER+1)MOD CYCLES;
CASE MODE OF
0: OUT:=(CYCLES-2*ABS(CYCLES-2*COUNTER))*AMPLITUDE/CYCLES;
1: OUT:=(CYCLES-ABS(CYCLES-2*COUNTER))*AMPLITUDE/CYCLES;
2: OUT:=(2*COUNTER-CYCLES)*AMPLITUDE/CYCLES;
3: OUT:=(CYCLES-2*COUNTER)*AMPLITUDE/CYCLES;
4: IF 2*COUNTER>CYCLES THEN
OUT:=-AMPLITUDE;
ELSE
OUT:=AMPLITUDE;
END_IF
5: OUT:=REAL_TO_INT(SIN(6.283167*COUNTER/CYCLES)*AMPLITUDE);
6: OUT:=REAL_TO_INT(COS(6.283167*COUNTER/CYCLES)*AMPLITUDE);
END_CASE;
ELSE
CLOCK;
IF CLOCK.PT<>PERIOD+PERIOD THEN
CLOCK.PT:=PERIOD+PERIOD;
CLOCK(IN:=TRUE);
ELSE
CET:=TIME_TO_DINT(CLOCK.ET);
PER:=TIME_TO_DINT(PERIOD);
IF CET>=PER THEN
CLOCK(IN:=FALSE);
CLOCK(IN:=TRUE);
CET:=CET MOD PER;
END_IF
CASE MODE OF
0: OUT:=DINT_TO_INT((2*ABS(PER-2*CET)*AMPLITUDE+PER/2)/PER-AMPLITUDE);
1: OUT:=DINT_TO_INT(((PER-ABS(PER-2*CET))*AMPLITUDE+PER/2)/PER);
2: OUT:=DINT_TO_INT((CET-PER)*2*AMPLITUDE/PER+AMPLITUDE);
3: OUT:=DINT_TO_INT((PER-CET)*2*AMPLITUDE/PER+AMPLITUDE);
4: IF CET*2<PER THEN
OUT:=-AMPLITUDE;
ELSE
OUT:=AMPLITUDE;
END_IF
5: help:=SIN(6.283167*CET/PER)*AMPLITUDE;
OUT:=REAL_TO_INT(help+0.5);
6: help:=COS(6.283167*CET/PER)*AMPLITUDE;
OUT:=REAL_TO_INT(help+0.5);
END_CASE;
END_IF
END_IF;
END_IF;
Для отсчета отрезков времени функциональным блоком (в частности формирования шага дискретизации) используется таймер задержки на включение TON. Для лучшего понимания программного кода, написанного на языке ST, перейдем к алгоритмическому представлению программы в виде схемы (см. рис.1).
Рис. 1. Схема программы функционального блока GEN
Опишем работу программы. В начале цикла вызова функционального блока проверяется наличие сигнала на входе RESET. При его наличии производится сброс переменных COUNTER, OUT, уставки времени и вызов экземпляра CLOCK функционального блока TON с значением входа IN равным FALSE (т.е. прекращается генерация выходных импульсов).
Далее происходит определение способа отсчета периода импульсов, формируемых блоком. Проверяется значение входа BASE (BASE=FALSE?). Если на входе значение FALSE, формирование периода сигнала идет по количеству циклов (или числу вызовов экземпляра функционального блока), если на входе значение TRUE, формирование периода сигнала происходит в заданных при вызове блока единицах времени. При формировании периода сигнала в единицах времени вызывается экземпляр CLOCK функционального блока TON (CLOCK), затем проверяется неравенство уставки времени двум периодам сигнала (CLOCK <> 2*PERIOD), если уставка еще не была задана, то уставка времени задается равной двум периодам (CLOCK.PT=2*PERIOD) (с запасом, т.к. цикл генерации импульса ведется в течении времени, которое должно быть больше или равно времени периода выдачи импульсов) и запускается экземпляр CLOCK (CLOCK(IN:=TRUE)). При этом внутренней переменной CET присваивается текущее значение времени с выхода ET блока TON (CET:=CLOCK.ET), а переменной PER - значение времени периода импульсов со входа PERIOD (PER:=PERIOD).
Далее проверяется соотношение определяющее достижение текущего отсчета времени заданному времени периода импульсов (CET>=PER?). Если соотношение выполняется, то функциональный блок CLOCK перезапускается подачей переднего фронта сигнала (CLOCK(IN:=FALSE), CLOCK(IN:=TRUE)), производится сброс переменной CET (CET:=CET MOD PER) и происходит переход на вычисление текущей амплитуды формируемого сигнала в зависимости от заранее выбранного режима работы MODE c помощью оператора выбора CASE (MODE=?).Такой же переход для вычисления текущей амплитуды формируемого сигнала происходит сразу же, если соотношение CET>=PER ? не выполняется.
Рассмотрим один из вариантов работы программы – формирование прямого пилообразного двуполярного сигнала от –А до +А (при MODE=2). Вычисление текущей амплитуды пилообразного сигнала производится по формуле (1):
OUT = ((CET-PER)*2*AMPLITUDE/PER+AMPLITUDE) (1)
Рассмотрим эту формулу подробнее. Найдем значение амплитуды генерируемого на выходе OUT сигнала (для заданного периода времени CET), соответствующее длине отрезка OD (см. рисунок 2). Вначале находим значение длины отрезка DC с отрицательным знаком DC = CET- PER, затем определяем длину отрезка BD через тангенс угла α BD=DC*tg α. Тангенс угла α равен отношению двойной амплитуды сигнала к его периоду, т.е. 2*AMPLITUDE/PER. Тогда BD = (CET-PER)* 2*AMPLITUDE/PER. Для нахождения отрезка OD, равное значению амплитуды OUT для периода времени CET, необходимо из отрезка BD отнять отрезок OB, соответствующий амплитуде сигнала A или прибавить его к отрезку BD, т.к. произведение (CET-PER)* 2*AMPLITUDE/PER имеет отрицательный знак.
Рис. 2. Определение текущей амплитуды генерируемого сигнала
После вычисления текущей амплитуды формируемого сигнала происходит переход на начало программы (т.е. повторный запуск алгоритма функционального блока в цикле ПЛК).
При формировании периода сигнала по количеству циклов переменная COUNTER сбрасывается в нуль при ее равенстве переменной CYCLES (COUNTER=(COUNTER+1) MOD CYCLES). Затем происходит переход на формирование нового периода сигнала и вычисление текущей амплитуды формируемого сигнала в зависимости от заранее выбранного режима работы MODE c помощью оператора выбора CASE (MODE=?).
Использование блока GEN на практике. Обычно на практике нет необходимости в применении полного функционала универсального генератора GEN и в большинстве случаев используют какой-то один тип генерируемого сигнала. Поэтому для минимизации программного кода, уменьшения минимально возможного времени цикла генерируемых импульсов (зависит от длительности цикла ПЛК и времени выполнения программы) на базе библиотечного блока GEN можно создать новый блок (назовем его NEWGEN), который будет генерировать требуемый сигнал. Ниже приведен листинг программы функционального блока NEWGEN для случая двуполярного нарастающего пилообразного сигнала.
FUNCTION_BLOCK NEWGEN
VAR_INPUT
PERIOD: TIME:=t#1s; (*период сигнала, по умолчанию 1 сек.*)
AMPLITUDE: INT; (*амплитуда сигнала*)
RESET: BOOL; (*сброс импульсов*)
END_VAR
VAR_OUTPUT
OUT: INT; (*генерируемый сигнал*)
END_VAR
VAR
CET: DINT; (*отсчет времени генерируемого периода сигнала*)
PER:DINT; (*длительность периода генерируемого сигнала*)
CLOCK:TON; (*экземпляр таймера TON*)
END_VAR
_____________________________________________________________________________
IF RESET THEN
OUT:=0; (*обнуление выхода*)
CLOCK.PT := t#0s; (*сброс уставки таймера TON*)
CLOCK(IN:=FALSE); (*отключениеблока TON*)
ELSE
CLOCK; (*вызов блока таймера*)
IF CLOCK.PT<>PERIOD+PERIOD THEN CLOCK.PT:=PERIOD+PERIOD; (*задание временной уставки с запасом*)
CLOCK(IN:=TRUE); (*таймер активен*)
ELSE
CET:=TIME_TO_DINT(CLOCK.ET); (* присваивание переменной CET текущего значения времени *)
PER:=TIME_TO_DINT(PERIOD); (*присваивание переменной PER значения периода сигнала *)
IF CET>=PER THEN (*определение окончания времени формирования одного периода*)
CLOCK(IN:=FALSE); (* перезапуск таймера TON*)
CLOCK(IN:=TRUE);
CET:=CET MOD PER; (*сброс текущего значения времени для формирования нового периода сигнала*)
END_IF
OUT:=DINT_TO_INT((CET-PER)*2*AMPLITUDE/PER+AMPLITUDE);
(*вычисление текущей амплитуды сигнала*)
END_IF
END_IF
Выводы. В работе проведен анализ принципа работы функционального блока GEN библиотеки Util.lib. Приведенная схема программы и описание ее работы позволяет провести быструю модификацию блока для решения конкретной задачи. Приведены рекомендации по практическому использованию блока при программировании на языках стандарта МЭК 61131 на примере генератора пилообразного сигнала. Модификация программы под конкретную задачу позволяет минимизировать программный код и уменьшить минимально допустимое время цикла генерируемых импульсов.
Литература:
1. И.В. Петров. Программируемые контроллеры: стандартные языки и приемы прикладного проектирования. Солон-Пресс, 2008 г. -256 с.
2. МЭК 61131-3—2012. Программируемые контроллеры. Часть 3. Языки программирования.
3. Руководство пользователя по программированию ПЛК в CoDeSys 2.3, 2006г. – 453с. Режим доступа: http://www.kipshop.ru/CoDeSys/steps/codesys_v23_ru.pdf (доступ свободный) – Яз. Рус.