Работа с переменными и типами данных в CoDeSys 3.5 — это как фундамент для программирования в CFC (Continuous Function Chart) на промышленных контроллерах. Переменные — это ячейки для хранения данных: чисел, текстов или сигналов. Типы данных определяют, что именно можно в них хранить и как с этим работать. Здесь мы разберем всё подробнее: как объявлять переменные, какие типы данных использовать, где их применять, и создадим пример программы для расчета суммы двух чисел.
Что такое переменные и как их объявлять
Переменные в CoDeSys — это как коробки на складе: в них хранятся данные, которые программа использует для управления оборудованием, расчетов или вывода информации на HMI. Вы можете задавать начальные значения или оставлять их пустыми, в зависимости от задачи.
Объявление переменных: со значением и без
С начальным значением
Вы задаете значение при объявлении переменной, чтобы программа стартовала с предсказуемыми данными. Это помогает избежать ошибок из-за случайных значений.
Пример:VAR
iCounter: INT := 100; // Начальное значение 100
sMessage: STRING := 'System Ready'; // Начальная строка
xFlag: BOOL := TRUE; // Начальное значение TRUE
END_VAR
Без начального значения
Если начальное значение не указано, CoDeSys присваивает значение по умолчанию: 0 для чисел, FALSE для BOOL, пустая строка для STRING. Это удобно, если значение будет задано позже, например, на основе сигнала с датчика.
Пример:VAR
iValue: INT; // По умолчанию 0
xSwitch: BOOL; // По умолчанию FALSE
sText: STRING; // По умолчанию пустая строка
END_VAR
Совет: Всегда инициализируйте переменные, которые влияют на логику программы, чтобы избежать сбоев. Например, счетчик без начального значения может начать работу с мусорными данными. Если значение будет задаваться динамически (например, с датчика), можно оставить переменную без инициализации.
Что такое константы и как их использовать
Константы — это значения, которые задаются один раз и не меняются во время выполнения программы. Они используются для хранения фиксированных параметров, таких как физические константы, настройки или предельные значения. В CoDeSys константы объявляются с ключевым словом VAR CONSTANT или VAR_GLOBAL CONSTANT, чтобы программа не могла случайно их изменить.
Объявление константы
С начальным значением
Константы всегда должны иметь начальное значение, так как они неизменяемы. Они объявляются в секции VAR_CONSTANT (для локальных констант) или VAR_GLOBAL CONSTANT (для глобальных констант).
Пример:VAR CONSTANT
iMaxCount: INT := 1000; // Максимальное значение счетчика
fPi: REAL := 3.14159; // Число Пи
sVersion: STRING := 'v1.0'; // Версия программы
END_VAR
VAR_GLOBAL CONSTANT
iMaxTemp: INT := 150; // Максимальная температура
END_VAR
Особенности:
- Константы нельзя изменить в программе, даже в режиме отладки.
- Локальные константы (VAR_CONSTANT) доступны только в блоке, где объявлены.
- Глобальные константы (VAR_GLOBAL CONSTANT) доступны во всем проекте через точку (например, .iMaxTemp).
- Константы не могут быть входными/выходными переменными (VAR_IN_OUT, VAR_INPUT, VAR_OUTPUT).
Где использовать:
- Для фиксированных настроек, таких как максимальная скорость двигателя или версия программы.
- Для физических констант, например, ускорение свободного падения (g = 9.81).
- Для ограничений диапазона, которые не должны меняться.
Когда применять:
- В основном блоке (PLC_PRG) для глобальных настроек, доступных всему проекту.
- Во вспомогательных блоках для локальных констант, например, фиксированных коэффициентов в вычислениях.
Совет: Используйте понятные имена для констант (например, iMaxCount вместо c1), чтобы код был читаемым. Проверяйте, что значения констант не выходят за пределы типа данных (например, INT не должен превышать 32767).
Типы переменных: где и для чего использовать
Переменные в CoDeSys различаются по области видимости (где они доступны) и назначению (для чего нужны). Вот подробный разбор с рекомендациями по применению.
Локальные переменные (VAR)
Объявляются внутри программы или функционального блока между VAR и END_VAR. Доступны только в этом блоке, как личные заметки инженера.
Где использовать: Для временных данных, например, промежуточных расчетов или флагов в основном блоке программы (PLC_PRG) или во вспомогательных блоках.
Когда применять: Для хранения временных результатов, например, суммы двух чисел в вычислении.
Пример:VAR
iTempResult: INT := 0; // Для временных вычислений
xTempFlag: BOOL; // Временный флаг
END_VAR
Глобальные переменные (VAR_GLOBAL)
Объявляются в списке глобальных переменных или в программном объекте между VAR_GLOBAL и END_VAR. Доступны во всем проекте через точку (например, .iGlobalCounter). Если локальная переменная имеет такое же имя, она имеет приоритет в своем блоке.
Где использовать: Для данных, которые нужны нескольким частям программы: общие настройки, значения датчиков, глобальные счетчики.
В основном блоке или вспомогательном? В основном блоке (PLC_PRG), если данные используются повсеместно.
Когда применять: Для хранения общего счетчика продукции или параметров соединения.
Пример:VAR_GLOBAL
iGlobalCounter: INT := 0; // Общий счетчик для проекта
sDeviceName: STRING := 'PLC1'; // Имя устройства
END_VAR
Входные переменные (VAR_INPUT)
Объявляются между VAR_INPUT и END_VAR. Используются для получения данных от внешних источников, таких как датчики или другие блоки.
Где использовать: Во вспомогательных функциональных блоках для обработки входных сигналов.
Когда применять: Для передачи значения температуры или сигнала запуска в блок.
Пример:VAR_INPUT
iSensorValue: INT; // Значение с датчика
xStartSignal: BOOL; // Сигнал запуска
END_VAR
Выходные переменные (VAR_OUTPUT)
Объявляются между VAR_OUTPUT и END_VAR. Передают результаты работы блока, например, на HMI или в другой блок.
Где использовать: Во вспомогательных блоках для вывода результатов обработки.
Когда применять: Для отправки сигнала управления или результата вычислений.
Пример:VAR_OUTPUT
xResult: BOOL; // Результат обработки
iOutputValue: INT; // Выходное значение
END_VAR
Входные/выходные переменные (VAR_IN_OUT)
Объявляются между VAR_IN_OUT и END_VAR. Могут быть прочитаны и изменены, передаются по ссылке (нельзя использовать константы или биты напрямую). Изменения сохраняются в вызывающем коде.
Где использовать: Во вспомогательных блоках, когда нужно изменить входные данные и вернуть результат, например, для обработки массивов или строк.
Когда применять: Для модификации данных, таких как строки или массивы.
Особенности: Длина строк в VAR_IN_OUT должна совпадать с вызывающей переменной, чтобы избежать ошибок.
Пример:VAR_IN_OUT
sData: STRING(255) := 'Input'; // Строка для чтения и записи
arrData: ARRAY[0..9] OF INT; // Массив для обработки
END_VAR
Временные переменные (VAR_TEMP)
Объявляются между VAR_TEMP и END_VAR. Инициализируются заново при каждом вызове блока, не сохраняют значения.
Где использовать: Во вспомогательных блоках для промежуточных вычислений, которые не нужно хранить.
Когда применять: Для временного хранения результата в цикле или функции.
Пример:VAR_TEMP
iTempCalc: INT; // Временный результат
xTempFlag: BOOL; // Временный флаг
END_VAR
Статические переменные (VAR_STAT)
Объявляются между VAR_STAT и END_VAR. Сохраняют значение между вызовами блока.
Где использовать: Во вспомогательных блоках для данных, которые нужно сохранять, например, счетчики вызовов функции.
Когда применять: Для подсчета вызовов блока или хранения промежуточного состояния.
Пример:VAR_STAT
iCallCount: INT := 0; // Счетчик вызовов блока
END_VAR
Внешние переменные (VAR_EXTERNAL)
Объявляются между VAR_EXTERNAL и END_VAR для доступа к глобальным переменным. Требуют наличия соответствующей глобальной переменной.
Где использовать: Во вспомогательных блоках для работы с глобальными данными.
Когда применять: Для доступа к глобальному счетчику или настройкам из функционального блока.
Пример:VAR_EXTERNAL
iGlobalValue: INT; // Ссылка на глобальную переменную
END_VAR
Реманентные переменные (RETAIN, PERSISTENT)
- RETAIN: Сохраняют значения после перезапуска или сбоя питания, если контроллер поддерживает энергонезависимую память (например, NVRAM).
- PERSISTENT: Сохраняют значения даже после холодного сброса или загрузки новой программы, но только в специальном списке перманентных переменных.
Где использовать: В основном блоке для важных данных, которые не должны теряться, например, счетчики продукции или время работы.
Когда применять: Для данных, которые нужно сохранить при отключении питания.
Пример:VAR RETAIN
iProductCount: INT := 0; // Счетчик продукции
END_VAR
VAR_GLOBAL PERSISTENT RETAIN
iTotalTime: DINT := 0; // Общее время работы
END_VAR
Конфигурационные переменные (VAR_CONFIG)
Объявляются между VAR_CONFIG и END_VAR в списке глобальных переменных. Привязывают переменные с неполными адресами (например, %I*) к конкретным физическим входам/выходам.
Где использовать: В основном блоке для настройки подключения к физическим входам/выходам.
Когда применять: Для привязки сигнала с датчика к конкретному входу контроллера.
Пример:VAR_CONFIG
PLC_PRG.myBlock.xInput AT %IX1.0: BOOL; // Привязка к входу
END_VAR
Переменные экземпляра (VAR_INST)
Объявляются между VAR_INST и END_VAR в методах. Сохраняются в стеке экземпляра функционального блока, не инициализируются при каждом вызове.
Где использовать: Во вспомогательных блоках, в методах, для хранения состояния.
Когда применять: Для запоминания последнего значения в методе.
Пример:VAR_INST
iLastValue: INT := 0; // Сохранение последнего значения
END_VAR
Указатели THIS и SUPER
- THIS: Указывает на текущий экземпляр функционального блока. Используется в методах для доступа к собственным переменным или методам.
- SUPER: Указывает на базовый функциональный блок для вызова его методов в объектно-ориентированном программировании.
Где использовать: Во вспомогательных блоках при работе с объектно-ориентированным подходом.
Когда применять: Для вызова метода текущего блока или родительского класса.
Пример:THIS^.METH_DoIt(); // Вызов метода текущего блока
SUPER^.METH_DoIt(); // Вызов метода базового блока
Что использовать в основном блоке, а что во вспомогательных
Основной блок программы (PLC_PRG):
- Используйте VAR, VAR_GLOBAL, VAR_CONFIG, RETAIN, PERSISTENT для данных, которые нужны всему проекту: настройки, счетчики, привязка к входам/выходам.
- Это место для общей логики, например, координации работы всего оборудования.
Вспомогательные блоки (функциональные блоки, методы):
- Используйте VAR_INPUT, VAR_OUTPUT, VAR_IN_OUT, VAR_TEMP, VAR_STAT, VAR_INST, THIS, SUPER для обработки данных внутри конкретного блока.
- Это модули для выполнения задач, таких как обработка сигнала или вычисление.
Базовые типы данных
Типы данных определяют, какие значения можно хранить в переменных и как с ними работать. Вот подробный разбор с примерами и рекомендациями.
Целочисленные типы данных
Целочисленные типы используются для работы с целыми числами. Они бывают знаковыми (могут быть отрицательными) и беззнаковыми (только положительные). Вот основные типы:
- BYTE: 0..255, 8 бит. Для небольших значений, например, кодов ошибок.
- WORD: 0..65535, 16 бит. Для средних значений, например, счетчиков импульсов.
- DWORD: 0..4294967295, 32 бит. Для больших значений, например, адресов памяти.
- LWORD: 0..2⁶⁴-1, 64 бит. Для очень больших значений (используется редко).
- SINT: -128..127, 8 бит. Для маленьких знаковых чисел.
- USINT: 0..255, 8 бит. Как BYTE, но с явной беззнаковостью.
- INT: -32768..32767, 16 бит. Основной тип для счетчиков, индексов.
- UINT: 0..65535, 16 бит. Для беззнаковых счетчиков.
- DINT: -2147483648..2147483647, 32 бит. Для больших знаковых чисел, например, сумм.
- UDINT: 0..4294967295, 32 бит. Для больших беззнаковых чисел.
- LINT: -2⁶³..2⁶³-1, 64 бит. Для очень больших знаковых чисел (редко).
- ULINT: 0..2⁶⁴-1, 64 бит. Для очень больших беззнаковых чисел (редко).
Примечание: Конверсия из большего типа (например, DINT) в меньший (например, INT) может привести к потере данных, если значение выходит за границы. Например, DINT 50000 в INT обрежется, так как INT ограничен 32767.
Числа с плавающей запятой (REAL, LREAL)
REAL:
- Диапазон: от -3.402823e+38 до 3.402823e+38.
- Память: 32 бита.
- Применение: Для дробных чисел, таких как температура, давление или уровень жидкости.
Пример:fTemperature: REAL := 25.5; // Температура в градусах
LREAL:
- Диапазон: от -1.7976931348623158e+308 до 1.7976931348623158e+308.
- Память: 64 бита.
- Применение: Для высокоточных расчетов, например, в сложных инженерных задачах.
Пример:fHighPrecision: LREAL := 3.14159265358979; // Число Пи с высокой точностью
Важно:
- Поддержка LREAL зависит от контроллера. Проверьте документацию устройства, чтобы убедиться, что он поддерживает 64-битные числа.
- Конверсия из LREAL в REAL может привести к потере точности.
- Если REAL или LREAL выходит за целочисленный диапазон при конверсии в INT или DINT, результат может быть непредсказуемым. Используйте функции CheckRangeSigned или CheckRangeUnsigned для проверки диапазона.
Строки (STRING, WSTRING)
STRING:
- Хранит строку символов в формате ASCII (1 байт на символ).
- По умолчанию выделяется 80 символов, если длина не указана. Можно указать длину, например, STRING(35).
- Требует 1 байт на символ + 1 дополнительный байт для хранения информации о строке.
- Если строка длиннее указанного размера, CoDeSys обрезает её справа.
- Применение: Для вывода сообщений на HMI, логов или имен устройств.
Пример:str: STRING(35) := 'This is a String'; // Строка на 35 символов
WSTRING:
- Хранит строку символов в формате Unicode (2 байта на символ).
- Требует 1 WORD (2 байта) на символ + 1 WORD дополнительно.
- Применение: Для текстов с не-латинскими символами, например, кириллица, китайские иероглифы или эмодзи.
Пример:wstr: WSTRING := "Привет, мир!"; // Unicode строка с кириллицей
Совет: Используйте STRING для текстов на латинице, а WSTRING для русского или других языков. Проверяйте длину строк в VAR_IN_OUT, чтобы избежать ошибок.
Типы данных времени
Используются для работы с таймерами, расписаниями и логированием событий. Хранятся как DWORD (в миллисекундах или секундах).
- TIME: 0..4294967295 мс, 32 бит. Для интервалов времени, например, задержек.
- TIME_OF_DAY (TOD): 00:00:00.000..23:59:59.999, 32 бит. Для времени суток.
- DATE: 1970-01-01..2106-02-07, 32 бит. Для дат.
- DATE_AND_TIME (DT): 1970-01-01 00:00:00..2106-02-07 06:28:15, 32 бит. Для комбинации даты и времени.
Пример:tDelay: TIME := T#500ms; // Задержка 500 мс
todTime: TIME_OF_DAY := TOD#14:30:00; // 14:30
dtNow: DATE_AND_TIME := DT#2025-09-08-15:00:00; // Дата и время
LTIME:
- Диапазон: от 0 до 213503d23h34m33s709ms551us615ns.
- Память: 64 бита.
- Применение: Для высокоточных таймеров с разрешением в наносекундах.
Пример:ltTimer: LTIME := LTIME#1000d15h23m12s34ms2us44ns; // Высокоточный таймер
Совет: Используйте TIME для простых задержек, а LTIME для задач, где нужна наносекундная точность, например, синхронизация сложных процессов.
Пользовательские и сложные типы данных
Массивы (ARRAY):
Хранят набор данных одного типа, например, последовательность чисел или сигналов.
Пример:arrValues: ARRAY[0..9] OF INT; // Массив из 10 чисел
Применение: Для хранения данных с датчиков, результатов измерений или логов.
Структуры (DUT):
Пользовательские типы данных, создаваемые через объект DUT или в блоке. Позволяют объединить разные типы данных.
Пример:TYPE SensorData:
STRUCT
iValue: INT; // Значение с датчика
xActive: BOOL; // Состояние датчика
END_STRUCT
END_TYPE
Применение: Для группировки связанных данных, например, параметров датчика.
Перечисления:
Набор именованных значений для упрощения чтения кода.
Пример:TYPE States: (Off, On, Error); END_TYPE
Применение: Для описания режимов работы, например, состояния машины (вкл, выкл, ошибка).
Указатели (POINTER) и ссылки (REFERENCE):
Указывают на адрес данных или ссылаются на переменные. Используются для сложной работы с данными.
Пример:pData: POINTER TO INT; // Указатель на целое число
Применение: Для динамической работы с данными в сложных алгоритмах.
Поддиапазонные типы:
Ограничивают диапазон значений базового типа для контроля допустимых значений.
VAR
X : INT (-95..95);
Y : UINT (0..100);
END_VAR
Пример: расчет суммы двух чисел (INT)
Давайте создадим программу на CFC для расчета суммы двух чисел типа INT. Программа принимает два входных числа, складывает их с помощью функционального блока ADD и выводит результат в выходную переменную.
Шаги создания программы
Создание проекта
Запустите CoDeSys 3.5 и создайте новый проект: Файл → Новый проект. Выберите устройство, например, ОВЕН СПК110.
Важно: По умолчанию CoDeSys создает файл PLC_PRG в языке ST. Чтобы использовать CFC, удалите его:
- В дереве проекта найдите PLC_PRG (PRG), щелкните правой кнопкой мыши и выберите Удалить.
- Создайте новый файл: правой кнопкой мыши на Application → Добавление объекта → POU. В открывшемся окне укажите:
Наименование: PLC_PRG
Тип: Программа
Язык: CFC
Нажмите OK, чтобы создать программу в CFC.
Объявление переменных
В редакторе PLC_PRG объявите переменные с начальными значениями:
// Объявление переменных
VAR
Number1: INT := 10; // Первое число
Number2: INT := 20; // Второе число
SumResult: INT; // Результат суммы
END_VAR
Проверка работы:
- Нажмите Онлайн → Логин для подключения к контроллеру. Если у вас нет устройства, нажмите в окне Онлайн → Эмуляция → Логин.
- Запустите его (Отладка → Старт).
После запуска, у вас откроется окно где будет виден список переменных и их значений, а так же CFC представление программы.
На CFC представлении в светло-коричневых квадратиках указано текущее значение в конкретном месте (или в конкретной переменной).
Заключение
Переменные, константы и типы данных в CFC — это основа надежных программ автоматизации. Локальные и глобальные переменные, а также константы подходят для основного блока программы (PLC_PRG), где хранятся общие данные, настройки и фиксированные параметры. Входные, выходные, временные и статические переменные лучше использовать во вспомогательных блоках для обработки конкретных задач. Целочисленные типы (INT, DINT), числа с плавающей запятой (REAL, LREAL), строки (STRING, WSTRING) и типы времени (TIME, LTIME) покрывают большинство сценариев, а массивы, структуры и перечисления добавляют гибкости.