Блог

IL (Instruction List) в CODESYS

Когда IL имеет смысл

Есть язык, который пишется как машинный код, но на нём можно написать что угодно — от простой логики до сложных расчётов. Это IL — Instruction List. Список инструкций.
IL — это почти ассемблер для ПЛК. Каждая строка — одна инструкция. Нет красивых диаграмм, нет удобных условных операторов. Просто инструкции одна за другой.
Почему его используют? Потому что он работает. Быстро, компактно, и его поддерживает любой контроллер, соответствующий стандарту МЭК 61131-3.
В современных проектах IL используют редко — для графики есть FBD, для логики есть ST, для последовательностей есть SFC. Но когда нужна максимальная оптимизация или когда работаешь со старым оборудованием — IL спасает.

Как работает IL

IL строится на двух основных идеях: аккумулятор и метки для переходов.
Аккумулятор — это как специальный регистр. Ты загружаешь в него значение, совершаешь над ним операции, потом присваиваешь результат переменной.
Пример:
LD sensor_1 // Загружаем значение датчика в аккумулятор
AND sensor_2 // И И'м с датчиком 2
OR button_start // Или берём кнопку старта
ST pump_on // Результат присваиваем включению насоса
Это означает: если оба датчика в единице ИЛИ нажата кнопка — включаем насос.
Программа выполняется сверху вниз, строка за строкой. Если нужно изменить порядок, используешь переходы — JMP (прыгнуть на метку) или JMPC (прыгнуть, если в аккумуляторе ИСТИНА).

Синтаксис IL

Каждая инструкция состоит из четырёх полей:
LABEL: OPERATOR OPERAND (* комментарий *)
LABEL — необязательный. Это имя, на которое можно прыгнуть.
OPERATOR — обязательный. LD, ST, AND, OR, JMP и так далее.
OPERAND — почти всегда нужен. Переменная, значение, адрес.
Комментарий — необязательный, в конце строки.
Пример с комментариями:
START: LD temperature (* загружаем температуру *)
GT 80 (* сравниваем с 80 градусов *)
JMPC alarm_on (* если больше, прыгаем на alarm_on *)
JMP check_pressure (* иначе проверяем давление *)
alarm_on: ST fan_power 100 (* включаем вентилятор на 100% *)
check_pressure: (* остальная логика *)

Основные операторы

LD — Загрузить в аккумулятор. LD temperature означает аккумулятор = temperature.
LDN — Загрузить инвертированное значение. LDN sensor означает аккумулятор = НЕ sensor.
ST — Присвоить. ST motor значит motor = аккумулятор.
STN — Присвоить инвертированное. STN led значит led = НЕ аккумулятор.
AND — Логическое И. Аккумулятор = аккумулятор И операнд.
OR — Логическое ИЛИ. Аккумулятор = аккумулятор ИЛИ операнд.
XOR — Исключающее ИЛИ.
NOT — Инвертировать аккумулятор.
ADD, SUB, MUL, DIV — Сложение, вычитание, умножение, деление.
GT, GE, LT, LE, EQ, NE — Сравнения (больше, больше или равно, меньше, и так далее). Результат (ИСТИНА или ЛОЖЬ) остаётся в аккумуляторе.
JMP — Безусловный прыжок на метку.
JMPC — Прыжок если в аккумуляторе ИСТИНА.
JMPCN — Прыжок если в аккумуляторе ЛОЖЬ.
CALL — Вызвать функцию.

Реальный пример: управление насосной станцией

Задача: если давление упало ниже 2 бара, включить насос. Если давление выше 4 бар, выключить. Если давление нормально (2–4 бара), не трогать.
На IL:
LOOP: LD pressure_sensor (* загружаем давление *)
LT 2.0 (* меньше 2 бар? *)
ST pump_on (* если да, включаем *)
JMPC END (* прыгаем в конец *)

LD pressure_sensor (* загружаем давление заново *)
GT 4.0 (* больше 4 бар? *)
STN pump_on (* если да, выключаем *)
JMPC END

(* иначе не меняем состояние *)
END: NOP (* пусто, конец *)
Это компактно и работает. На ST потребовалось бы чуть больше текста:
IF pressure_sensor < 2.0 THEN
pump_on := TRUE;
ELSIF pressure_sensor > 4.0 THEN
pump_on := FALSE;
END_IF;
Оба варианта работают, но IL занимает меньше памяти и выполняется немного быстрее.

Функции на IL

Можно писать функции целиком на IL:
FUNCTION calculate_average: REAL
VAR_INPUT
value1: REAL;
value2: REAL;
value3: REAL;
END_VAR

LD value1
ADD value2
ADD value3
DIV 3.0
ST calculate_average
END_FUNCTION
Функция считает среднее арифметическое трёх значений. На ST это было бы:
FUNCTION calculate_average: REAL
BEGIN
calculate_average := (value1 + value2 + value3) / 3.0;
END_FUNCTION
ST понятнее, но IL работает везде, даже на старых ПЛК.

Когда IL нужен

Оптимизация памяти. Старые ПЛК имели мало памяти. IL занимает меньше места, чем ST.
Критичные по времени операции. Когда нужна максимальная скорость, IL работает быстрее.
Интеграция со старым кодом. Если весь проект написан на IL, добавлять новый код проще на том же языке.
Низкоуровневое программирование. Когда нужно работать с битами, регистрами, прямыми адресами памяти.

Когда IL избежать

Сложная логика. Если много условий и ветвлений, IL становится грудой переходов, которую сложно отладить.
Читаемость кода. Новичок или инженер, привыкший к ST или FBD, потратит время на разбор IL-программы.
Математика. Вычисления на IL выглядят громоздко.
Обслуживание. Год спустя, когда нужно поменять логику, IL-код требует больше времени на понимание.

CODESYS и IL

CODESYS полностью поддерживает IL согласно МЭК 61131-3. Редактор IL в CODESYS — просто текстовый редактор с подсветкой синтаксиса.
Можно писать функции на IL, функциональные блоки на IL, действия в SFC на IL. CODESYS понимает все операторы, проверяет синтаксис, компилирует.
Отладчик CODESYS работает с IL-кодом: видишь, какой оператор выполняется, какое значение в аккумуляторе, можешь прерваться на метке.

Вывод

IL — это язык, который не нужен в новых проектах, но незаменим, когда встречаешься с реальностью. Старые установки, узкие требования по памяти, необходимость максимальной скорости — вот где IL кажется логичным выбором.
Если ты разрабатываешь новую систему, выбери ST или FBD. Это понятнее, удобнее, проще отлаживается. Но если встретишь старый ПЛК с IL-программой, не пугайся. IL — это просто инструмент. И он работает.