Когда человек впервые открывает TIA Portal, создаёт проект для S7-1200 и видит пустой блок OB1, у него обычно возникает один и тот же вопрос: что именно сюда писать и как вообще мыслит контроллер.
Structured Text кажется похожим на обычный язык программирования, но логика работы ПЛК здесь другая: код не запускается один раз, а выполняется циклически, проход за проходом, пока контроллер находится в рабочем режиме. Для понимания первых программ это важнее любой синтаксической мелочи.
В этой статье разберём первую программу на ST не как набор строк, а как реальный фрагмент логики управления. Заодно посмотрим, как работает цикл OB1, чем отличаются таймеры TON, TOF, TP и TONR, где и как объявлять переменные, и почему неудачное именование способно испортить даже технически правильный проект.
Как ПЛК выполняет программу
Контроллер S7-1200 выполняет пользовательскую программу циклически. В типовом режиме операционная система обновляет образ процесса, затем исполняет код, а затем снова обновляет выходные данные, поэтому программист описывает не «всю жизнь программы», а то, что должно произойти за один цикл.
Это важно по двум причинам. Во-первых, логика в OB1 работает не как бесконечный WHILE TRUE, а как фрагмент, который CPU вызывает снова и снова автоматически. Во-вторых, работа через образ процесса делает поведение программы предсказуемым: в течение одного цикла код использует согласованное представление сигналов, а не «живые» изменения входов посреди вычислений.
Первая программа на ST
Начнём с простого примера. Пусть кнопка включает лампу сразу, а через три секунды непрерывного удержания должен сработать таймер.
VAR
Button : BOOL := FALSE;
Lamp : BOOL := FALSE;
Timer : TON;
END_VAR
Timer(IN := Button, PT := T#3S);
IF Button AND NOT Timer.Q THEN
Lamp := TRUE;
ELSIF Timer.Q THEN
Lamp := NOT Lamp;
ELSE
Lamp := FALSE;
END_IF;
Такой вызов таймера соответствует IEC-логике в S7-1200: экземпляр TON вызывается с входом IN и уставкой PT, а его состояние и выходы доступны через члены экземпляра, например Q и ET. Siemens описывает таймеры TP, TON, TOF и TONR именно как IEC-инструкции с параметрами и внутренним состоянием экземпляра.
Сразу важно отметить практический момент: ветка Lamp := NOT Lamp; после срабатывания Timer.Q будет выполняться каждый цикл, то есть лампа начнёт переключаться с очень высокой частотой. Для учебной демонстрации это допустимо, потому что хорошо показывает сам факт перехода Q в TRUE, но для реального мигания нужен отдельный таймер периода.
Разбор программы по строкам
Строка вызова таймера:
Timer(IN := Button, PT := T#3S);
Здесь Timer — не имя встроенной функции, а экземпляр таймера типа TON, объявленный как переменная. В SCL/ST именно так и работают IEC-таймеры: сначала создаётся экземпляр, затем он вызывается с параметрами, а его внутреннее состояние хранится между циклами.[1][2]
Параметр IN разрешает работу таймера, а PT задаёт уставку времени в типе TIME. Для временных литералов Siemens использует запись вида T#500MS, T#3S, T#1M30S, поэтому T#3S — это не строка и не обычное число, а значение типа времени.
Теперь логика ниже:
IF Button AND NOT Timer.Q THEN
Lamp := TRUE;
ELSIF Timer.Q THEN
Lamp := NOT Lamp;
ELSE
Lamp := FALSE;
END_IF;
Пока кнопка нажата и таймер ещё не достиг уставки, Timer.Q = FALSE, поэтому лампа просто горит. Когда накопленное время достигает PT, таймер TON устанавливает Q в TRUE, и программа переходит во вторую ветку. Если кнопка отпущена, TON сбрасывается, Q становится FALSE, и выполняется ветка ELSE, которая выключает лампу.
Что именно делает TON
Таймер TON — это задержка на включение. Пока IN = TRUE, таймер накапливает прошедшее время, а когда накопленное значение достигает PT, выход Q становится истинным. Если IN сбросить в FALSE до завершения отсчёта или после него, TON прекращает работу и сбрасывает накопленное время и выход.
Именно поэтому TON используют там, где нужно включать что-то не сразу: например, задержать пуск двигателя, не реагировать на кратковременную помеху на датчике или дождаться стабилизации сигнала. Это самый распространённый таймер в прикладных проектах на S7-1200.
Четыре IEC-таймера в S7-1200
В TIA Portal для S7-1200 основными таймерами являются четыре IEC-инструкции: TP, TON, TOF и TONR. Siemens прямо выделяет именно эти четыре типа как базовые таймерные инструкции для S7-1200 и S7-1500.
|
Таймер |
Назначение |
Как работает |
|
TON |
Delay ON |
Q становится TRUE через время PT после появления IN. При IN = FALSE таймер сбрасывается. |
|
TOF |
Delay OFF |
Q включён, пока активен IN, и остаётся включённым ещё PT после снятия входа. |
|
TP |
Pulse |
При запуске формирует импульс фиксированной длительности PT. Изменение IN во время импульса не продлевает его. |
|
TONR |
Retentive ON Delay |
Накапливает прошедшее время за несколько периодов активности IN и сбрасывается только через вход R. |
Когда какой таймер применять
TON нужен для задержки включения. Например, если насос должен стартовать только после того, как датчик уровня показывает активное состояние не менее трёх секунд, TON помогает отсечь случайные колебания сигнала.
TOF применяют там, где исполнительный механизм должен поработать ещё некоторое время после снятия команды. Классический случай — вентилятор охлаждения, который продолжает крутиться после остановки двигателя.
TP хорош в задачах, где нужен импульс строго заданной длительности. Например, при формировании короткой команды на клапан, чтобы независимо от того, как долго оператор держит кнопку, исполнительный механизм получил только один нормированный импульс.
TONR используют для накопления времени работы. Siemens указывает, что этот таймер суммирует elapsed time по нескольким периодам и сбрасывается через вход R, поэтому он удобен для учёта наработки оборудования.
Почему таймеру нужна память
IEC-таймер — это не простая переменная вроде BOOL или INT, а экземпляр инструкции с внутренним состоянием. Внутри такого экземпляра хранятся по крайней мере накопленное время и состояние выходов, поэтому таймер должен иметь область памяти, где эти значения сохраняются между циклами.
Именно поэтому в статьях и примерах важно не писать о таймере как о «магической функции», а объяснять, что это экземпляр с состоянием. Для S7-1200 это особенно полезно понимать на раннем этапе, потому что от этого зависит и структура блока, и дальнейшая архитектура проекта.
Переменные в Structured Text
Переменная в ST — это именованная область памяти с заранее определённым типом данных. Контроллер не хранит «просто значение»; он всегда хранит значение как объект конкретного типа: BOOL, INT, REAL, TIME, STRING и так далее.
Тип определяет, что именно можно делать с этой переменной. BOOL участвует в логических операциях, INT — в целочисленных вычислениях, REAL — в вещественной арифметике, а TIME используется в таймерах и временных расчётах. Когда тип выбран неудачно, программа может не только читаться хуже, но и начать вести себя неправильно из-за лишних преобразований.
Базовый синтаксис объявления
В ST переменные обычно объявляют в секции VAR ... END_VAR. Простейший пример выглядит так:
VAR
motorSpeed : INT := 0;
targetPressure : REAL := 10.5;
systemEnabled : BOOL := FALSE;
statusMessage : STRING := 'IDLE';
tmrStartDelay : TON;
tmrCooling : TOF;
END_VAR
Каждая строка содержит имя, тип и, при необходимости, начальное значение. Для преобразования типов и вычислений Siemens использует явные инструкции вида INT_TO_REAL(...), REAL_TO_INT(...) и аналогичные, поэтому типы лучше выбирать осознанно, а не надеяться на неявное приведение.
Нужно ли всегда задавать начальные значения
Для инженерной практики ответ почти всегда положительный: важные переменные лучше инициализировать явно. Это повышает читаемость, делает поведение блока предсказуемее и избавляет от ситуации, когда при чтении кода непонятно, какое стартовое состояние предполагал разработчик.
Особенно это касается булевых флагов, режимов работы, уставок и внутренних признаков автоматики. Даже когда среда или тип блока обеспечивают корректную начальную инициализацию, явная запись := FALSE, := 0 или := T#0S делает намерение программиста очевидным.
Области видимости
В хорошем проекте важно не только объявить переменную, но и ограничить круг кода, который может её менять. Чем шире видимость, тем тяжелее потом искать, кто именно изменил значение и почему автомат внезапно перешёл в другой режим. Это стандартная инженерная проблема для больших проектов в TIA Portal.
Практически это означает простое правило: если переменная нужна только внутри одного блока, она должна быть локальной. Глобальные теги полезны, когда данные действительно должны быть доступны в нескольких местах программы, но их избыток быстро превращает проект в трудноотлаживаемую систему.
Как называть переменные
Имена переменных — это не косметика, а часть инженерной документации программы. Код на Structured Text должен читаться почти как техническое описание алгоритма, поэтому имена вроде x, y, z допустимы только в коротких математических примерах, но не в прикладной автоматике.
Хорошие имена описывают смысл, а не просто экономят символы. В промышленном коде обычно используют английские идентификаторы, чтобы сохранить совместимость с ПО, библиотеками и коллегами, а для булевых переменных предпочитают понятные признаки вроде isMotorRunning, buttonPressed, alarmActive. Такой стиль не является требованием компилятора, но резко снижает стоимость сопровождения проекта.
Пример плохого и хорошего стиля:
VAR
x : REAL;
y : INT;
z : BOOL;
END_VAR
VAR
currentTemperature : REAL := 0.0;
pumpRunningHours : INT := 0;
isOverheated : BOOL := FALSE;
END_VAR
Во втором варианте код объясняет сам себя, и это гораздо важнее, чем кажущаяся компактность первого. Для промышленной автоматики читаемость почти всегда окупается быстрее, чем «краткость ради краткости».
Операторы управления потоком
Самая частая конструкция в ST — это IF ... ELSIF ... ELSE. Она подходит для большинства простых ветвлений, особенно когда логика зависит от состояния нескольких флагов и таймеров.
Но для задач с дискретными режимами работы часто удобнее CASE. Такая конструкция особенно полезна для машин состояний, где у оборудования есть фиксированные режимы вроде «Стоп», «Пуск», «Работа», «Авария».
Пример:
CASE machineState OF
0: motorSpeed := 0;
1: motorSpeed := 500;
2: motorSpeed := 1000;
3: motorSpeed := -500;
ELSE
motorSpeed := 0;
END_CASE;
Этот стиль обычно читается лучше длинной цепочки IF, потому что сразу видно, что программа выбирает действие по значению состояния. Для автоматов и последовательностной логики это один из самых удобных приёмов.
Циклы в ПЛК
Structured Text поддерживает циклы FOR и WHILE, но пользоваться ими в ПЛК нужно осторожно. Контроллер работает в реальном времени, и слишком длинный цикл может увеличить время сканирования настолько, что система начнёт работать нестабильно или превысит допустимое время выполнения цикла.
Поэтому FOR применяют только там, где число итераций заранее ограничено и понятно, например при обработке массива фиксированной длины. WHILE используют ещё осторожнее, потому что ошибка в условии способна привести к зависанию логики на одном цикле исполнения.
Пример безопасного FOR:
VAR
i : INT := 0;
sum : REAL := 0.0;
average : REAL := 0.0;
temperatures : ARRAY[0..9] OF REAL;
END_VAR
sum := 0.0;
FOR i := 0 TO 9 DO
sum := sum + temperatures[i];
END_FOR;
average := sum / 10.0;
Здесь число итераций ограничено, алгоритм прозрачен, а поведение легко оценить заранее. Для ПЛК это правильный стиль использования цикла.
Функции и функциональные блоки
Когда программа перестаёт помещаться в несколько строк, складывать всё в OB1 уже не стоит. В проектах Siemens логику обычно выносят в функции и функциональные блоки, чтобы разделять вычисления, состояние и интерфейс между частями программы.
Функция подходит для вычислений, которые не требуют собственной памяти между вызовами. Функциональный блок нужен там, где объект должен хранить состояние: внутренние флаги, таймеры, накопленные значения, этапы алгоритма. Именно поэтому IEC-таймеры реализуются как экземпляры с внутренним состоянием, а не как «одноразовые» математические операции.
Исправленный пример функции масштабирования
В исходном тексте пример масштабирования аналогового сигнала был написан с ошибочным преобразованием типа: вместо перевода INT в REAL использовалась логически неверная схема с REAL_TO_INT. Для вычисления инженерного значения нужно преобразовать целочисленные аргументы в REAL, а затем выполнить обычную формулу линейного масштабирования. Siemens описывает преобразования в SCL через явные инструкции вида INT_TO_REAL(...).
Корректный вариант функции:
FUNCTION ScaleAnalogInput : REAL
VAR_INPUT
rawValue : INT;
minRaw : INT;
maxRaw : INT;
minEng : REAL;
maxEng : REAL;
END_VAR
ScaleAnalogInput := minEng +
(INT_TO_REAL(rawValue) - INT_TO_REAL(minRaw)) /
(INT_TO_REAL(maxRaw) - INT_TO_REAL(minRaw)) *
(maxEng - minEng);
END_FUNCTION
Такая запись типобезопасна по смыслу: сначала целочисленные значения переводятся в REAL, затем выполняется вещественное вычисление, а результат возвращается как REAL. Именно этот подход соответствует нормальной практике написания вычислительных функций в ST для TIA Portal.
Типичные ошибки начинающих
Одна из самых частых ошибок — путаница между присваиванием и сравнением. В ST присваивание выполняется через :=, а сравнение — через =, поэтому запись в условии и запись в операторе присваивания решают принципиально разные задачи.
Вторая типичная проблема — небрежная работа с типами. Если переменная должна участвовать в вещественных вычислениях, лучше сразу привести её к REAL явно, а не смешивать разные типы на авось. В Siemens SCL для этого и существуют специальные инструкции преобразования типов.
Третья проблема — выход за границы массива. В практическом проекте это означает ошибку адресации или некорректную работу алгоритма, поэтому индекс всегда должен оставаться в заранее допустимом диапазоне.
Четвёртая — злоупотребление глобальными переменными. Когда десятки блоков могут менять одно и то же значение, программа перестаёт быть прозрачной: сигнал как будто «меняется сам», хотя на деле его меняет один из разбросанных по проекту участков кода.
Что важно запомнить после первой статьи по ST
Structured Text в S7-1200 — это не «текстовая версия языков высокого уровня», а полноценный инструмент для описания циклической логики управления в ПЛК. Чтобы писать на нём уверенно, нужно понимать не только синтаксис IF и CASE, но и сам принцип работы CPU: цикл OB1, образ процесса, поведение экземпляров таймеров и роль памяти между проходами.
Если это понимание есть, код перестаёт быть набором загадочных инструкций и превращается в читаемое описание поведения машины. А когда к этому добавляются грамотные имена переменных, разумная область видимости и корректная работа с типами, проект в TIA Portal начинает выглядеть как инженерная система, а не как случайный набор строк.
Хотите углубиться в ПЛК-программирование?
Подпишитесь на платный канал "ПЛК и автоматизация" — практические разборы Siemens S7-1200/1500, TIA Portal, SCADA, частотники, Modbus/PROFINET и реальные проекты промышленной автоматизации.
Что внутри канала:
- Пошаговые проекты от схемы до готового кода;
- Настройка коммуникаций (PROFINET, Modbus TCP/RTU, Ethernet/IP);
- Оптимизация больших проектов и отладка;
- FB/FC для типовых задач (насосы, конвейеры, PID);
- Работа с HMI и SCADA (WinCC, Zenon).
Для кого: инженеры, автоматизаторы, студенты, преподаватели, те, кто хочет перейти от примеров к реальным системам.
Подписаться на "ПЛК и автоматизация"
Первые 100 подписчиков — со скидкой 30% на 3 и 6 месяцев
Андрей Повный
