Codesys 3.5 Обучение программированию.
265 subscribers
38 photos
13 videos
1 file
63 links
Download Telegram
Гораздо понятнее и надёжнее вызывать экземпляр таймера каждый цикл:

fbTimer(
IN := xSignal,
PT := T#5s
);


А затем использовать его выход:

IF fbTimer.Q THEN
xAlarm := TRUE;
END_IF


Так алгоритм легко читать:

xSignal определяет состояние входа таймера на каждом цикле ПЛК.

━━━━━━━━━━━━━━━━━━

🔎 Для чего нужен ET?

ET показывает текущее прошедшее время.

Например:

fbTimer.ET


может иметь значения:

T#500ms
T#2s
T#4s
T#5s


Это удобно использовать для:

• диагностики программы
• отображения времени на HMI
• индикации процесса задержки
• анализа работы алгоритма
• отладки

Например, оператору можно показать:

«Авария будет подтверждена через 2.3 секунды».

Для основной логики обычно используют Q, а ET — как дополнительную информацию.

━━━━━━━━━━━━━━━━━━

📌 Коротко: алгоритм TON

Если:

IN = FALSE

получаем:

ET = T#0s
Q = FALSE


Если:

IN = TRUE

начинается отсчёт.

Пока:

ET < PT


получаем:

Q = FALSE

Когда:

ET ≥ PT


получаем:

Q = TRUE

Если после этого:

IN = FALSE

таймер снова сбрасывается.

━━━━━━━━━━━━━━━━━━

💡 Где TON используется на практике?

Очень часто:

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

Главный вопрос при выборе TON:

«Нужно ли мне убедиться, что условие непрерывно выполняется заданное время?»

Если да — TON обычно подходит.

━━━━━━━━━━━━━━━━━━

🧠 Запомните

TON — это не просто команда:

«подождать 5 секунд».

Это проверка:

«Оставался ли вход IN непрерывно в TRUE в течение заданного времени PT?»

Даже кратковременный переход IN в FALSE приводит к сбросу отсчёта.

И именно поэтому таймер иногда кажется «неработающим», хотя на самом деле работает совершенно правильно.

━━━━━━━━━━━━━━━━━━

❓ Проверим себя

Пусть задано:

PT := T#5s


Сигнал ведёт себя так:

TRUE  → 4.9 сек
FALSE → 0.1 сек
TRUE → 4.9 сек


Станет ли Q = TRUE хотя бы на одном цикле?

💬 Напишите свой ответ в комментариях 👇

#CODESYS #ПЛК #АСУТП #StructuredText #ST #ПрограммированиеПЛК #Автоматизация
🧠 РАЗБОР КОДА: TON

Сигнал xSignal был:

TRUE  → 4.9 сек
FALSE → 0.1 сек
TRUE → 4.9 сек


Таймер:

fbTimer(
IN := xSignal,
PT := T#5s
);


❓ Станет ли Q = TRUE?

Попробуйте ответить до того, как откроете разбор.

👇 Правильный ответ — в следующем посте.

#CODESYS #ПЛК #TON #StructuredText
🧠 РАЗБОР КОДА: TON

Разберём задачу.

Есть таймер:

fbTimer(
IN := xSignal,
PT := T#5s
);


Сигнал на входе меняется так:

TRUE  → 4.9 сек
FALSE → 0.1 сек
TRUE → 4.9 сек


Начальное состояние:

Q  = FALSE
ET = T#0s


❓ Что произойдёт с выходом Q?

А) Q станет TRUE, потому что суммарно сигнал был TRUE больше 5 секунд

Б) Q станет TRUE во втором интервале TRUE

В) Q ни разу не станет TRUE

Г) Q кратковременно станет TRUE в момент сброса

Сначала выберите вариант, потом откройте ответ 👇

🔎 Открыть правильный ответ

✅ Правильный ответ — В.

Q ни разу не станет TRUE.

Почему?

В первом интервале:

xSignal = TRUE → 4.9 сек

До PT = 5 сек не хватает 0.1 секунды, поэтому Q остаётся FALSE.

Затем сигнал становится FALSE.

Для TON это означает сброс:

ET → T#0s
Q → FALSE

После этого, когда xSignal снова становится TRUE, таймер начинает отсчёт заново.

Во втором интервале сигнал снова активен только 4.9 секунды, а этого опять недостаточно.

Поэтому Q ни разу не станет TRUE.


💡 Главная мысль

TON считает не суммарное время активности сигнала, а время, в течение которого вход IN находится в TRUE непрерывно.

Как только:

IN = FALSE

таймер сбрасывается.

💬 Получилось ответить правильно до открытия спойлера?

#CODESYS #ПЛК #TON #StructuredText #ПрограммированиеПЛК
👍3
⚡ R_TRIG и F_TRIG в CODESYS

Сигнал может оставаться TRUE или FALSE сотни циклов.

Но иногда нам нужен только один конкретный момент:

⬆️ сигнал появился
⬇️ сигнал исчез

Для этого в CODESYS используются R_TRIG и F_TRIG.

👇 В следующем посте подробно разберём, как они работают и где применяются.

#CODESYS #ПЛК #StructuredText #R_TRIG #F_TRIG
⚡ R_TRIG и F_TRIG в CODESYS: как поймать момент изменения сигнала?

В программах ПЛК постоянно приходится работать с дискретными сигналами:

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

Но очень важно различать:

состояние сигнала

и

момент изменения сигнала.

Именно для второго случая в CODESYS используются функциональные блоки R_TRIG и F_TRIG.

━━━━━━━━━━━━━━━━━━

🔹 Состояние и событие — не одно и то же

Допустим:

xButton = TRUE


Если написать:

IF xButton THEN
iCounter := iCounter + 1;
END_IF


команда будет выполняться каждый цикл ПЛК, пока кнопка остаётся нажатой.

Например, если цикл программы составляет 10 мс, а кнопку держат одну секунду, код может выполниться около 100 раз.

Но иногда нам нужно выполнить действие только один раз:

именно в момент нажатия.

Для этого необходимо определить переход:

FALSE → TRUE


Это называется передним фронтом.

━━━━━━━━━━━━━━━━━━

⬆️ R_TRIG — передний фронт

R_TRIG отслеживает переход:

FALSE → TRUE


Объявим функциональный блок:

VAR
xButton : BOOL;
fbRise : R_TRIG;
END_VAR


Вызываем его каждый цикл:

fbRise(CLK := xButton);


Когда xButton изменится:

FALSE → TRUE


выход:

fbRise.Q


станет TRUE только на один цикл программы.

После этого, даже если xButton продолжает оставаться TRUE:

fbRise.Q = FALSE


до следующего нового фронта.

━━━━━━━━━━━━━━━━━━

🧠 Пример

Пусть сигнал выглядит так:

цикл 1 → FALSE
цикл 2 → FALSE
цикл 3 → TRUE
цикл 4 → TRUE
цикл 5 → TRUE


Выход R_TRIG:

цикл 1 → FALSE
цикл 2 → FALSE
цикл 3 → TRUE
цикл 4 → FALSE
цикл 5 → FALSE


То есть импульс появляется только в момент изменения сигнала.

━━━━━━━━━━━━━━━━━━

✅ Практический пример с кнопкой

Нужно увеличивать счётчик один раз при каждом нажатии:

VAR
xButton : BOOL;
fbButton : R_TRIG;
iCounter : INT;
END_VAR

fbButton(CLK := xButton);

IF fbButton.Q THEN
iCounter := iCounter + 1;
END_IF


Теперь:

🔘 нажали → +1

⏳ держим → ничего

🔘 отпустили → ничего

🔘 нажали снова → ещё +1

━━━━━━━━━━━━━━━━━━

⬇️ А что делает F_TRIG?

F_TRIG работает наоборот.

Он определяет переход:

TRUE → FALSE


То есть задний фронт сигнала.

Объявление:

VAR
xSignal : BOOL;
fbFall : F_TRIG;
END_VAR


Вызов:

fbFall(CLK := xSignal);


Когда сигнал изменится:

TRUE → FALSE


получим:

fbFall.Q = TRUE


на один цикл ПЛК.

━━━━━━━━━━━━━━━━━━

🔄 R_TRIG и F_TRIG рядом

Для одного сигнала можно использовать оба блока:

fbRise(CLK := xSignal);
fbFall(CLK := xSignal);


Получаем:

FALSE → TRUE
↑
R_TRIG.Q

TRUE → FALSE
↓
F_TRIG.Q


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

━━━━━━━━━━━━━━━━━━

🏭 Где применяется R_TRIG?

Передний фронт очень часто нужен для:

• обработки нажатия кнопки
• запуска последовательности
• подсчёта событий
• регистрации появления аварии
• формирования однократной команды
• переключения режима
• увеличения или уменьшения значения
• запуска операции с HMI

Например:

IF fbStart.Q THEN
eState := STATE_STARTING;
END_IF


Переход выполняется один раз — именно при появлении команды.

━━━━━━━━━━━━━━━━━━

🏭 Где применяется F_TRIG?

Задний фронт полезен, когда действие должно происходить при исчезновении сигнала.

Например:

• оператор отпустил кнопку
• механизм остановился
• датчик перестал быть активным
• команда была снята
• аварийное условие исчезло

Пример:

fbFall(CLK := xMotorRun);

IF fbFall.Q THEN
iStopCounter := iStopCounter + 1;
END_IF


Счётчик увеличится именно в момент перехода:

TRUE → FALSE


━━━━━━━━━━━━━━━━━━

⚠️ Важный момент
R_TRIG и F_TRIG должны помнить состояние входа на предыдущем вызове.

Поэтому экземпляр функционального блока нужно вызывать циклически.

Например:

fbRise(CLK := xSignal);


на каждом выполнении задачи.

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

━━━━━━━━━━━━━━━━━━

⚠️ Ещё одна частая ошибка

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

Плохо:

fbTrig(CLK := xStart);
...
fbTrig(CLK := xReset);


У блока есть внутреннее состояние.

Для разных сигналов лучше создавать разные экземпляры:

fbStart : R_TRIG;
fbReset : R_TRIG;


и вызывать их отдельно:

fbStart(CLK := xStart);
fbReset(CLK := xReset);


Так программа будет понятнее и предсказуемее.

━━━━━━━━━━━━━━━━━━

💡 Главное различие

Обычный:

IF xSignal THEN


отвечает на вопрос:

«Сигнал сейчас TRUE?»

R_TRIG отвечает:

«Сигнал только что изменился с FALSE на TRUE?»

А F_TRIG:

«Сигнал только что изменился с TRUE на FALSE?»

━━━━━━━━━━━━━━━━━━

📌 Запомните

⬆️ R_TRIG

FALSE → TRUE


Передний фронт — сигнал появился.

⬇️ F_TRIG

TRUE → FALSE


Задний фронт — сигнал исчез.

Выход Q активируется только на один цикл программы при обнаружении соответствующего перехода.

━━━━━━━━━━━━━━━━━━

❓ Проверим себя

Сигнал изменяется так:

FALSE
FALSE
TRUE
TRUE
FALSE
FALSE
TRUE


Сколько раз за это время сработает R_TRIG, а сколько — F_TRIG?

💬 Напишите ответ в комментариях, не подглядывая в документацию 👇

#CODESYS #ПЛК #АСУТП #StructuredText #ST #R_TRIG #F_TRIG #ПрограммированиеПЛК
🧠 РАЗБОР КОДА: R_TRIG и F_TRIG

Посмотрите на изменение сигнала по циклам ПЛК:

Цикл:    1      2      3      4      5      6      7
Signal: FALSE FALSE TRUE TRUE FALSE FALSE TRUE


Для этого сигнала работают два блока:

fbRise(CLK := xSignal);
fbFall(CLK := xSignal);


где:

⬆️ fbRise — R_TRIG
⬇️ fbFall — F_TRIG

❓ Сколько раз за эти 7 циклов сработают R_TRIG и F_TRIG?

А) R_TRIG — 1, F_TRIG — 1
Б) R_TRIG — 2, F_TRIG — 1
В) R_TRIG — 2, F_TRIG — 2
Г) R_TRIG — 3, F_TRIG — 2

Выберите вариант до того, как посмотрите разбор 👇

Правильный ответ — в следующем посте.

#CODESYS #ПЛК #R_TRIG #F_TRIG #StructuredText
✅ РАЗБОР ЗАДАЧИ: R_TRIG и F_TRIG

Исходный сигнал:

Цикл:    1      2      3      4      5      6      7
Signal: FALSE FALSE TRUE TRUE FALSE FALSE TRUE


❓ Сколько раз сработают R_TRIG и F_TRIG?

А) R_TRIG — 1, F_TRIG — 1
Б) R_TRIG — 2, F_TRIG — 1
В) R_TRIG — 2, F_TRIG — 2
Г) R_TRIG — 3, F_TRIG — 2

Сначала выберите вариант, затем откройте разбор 👇

🔎 Открыть правильный ответ

✅ Правильный ответ — Б.

R_TRIG сработает 2 раза.
F_TRIG сработает 1 раз.

Разберём по шагам.

⬆️ Между циклами 2 и 3:

FALSE → TRUE

Это передний фронт.

Значит:

R_TRIG.Q = TRUE

только на один цикл.

На следующем цикле сигнал всё ещё TRUE, но нового фронта уже нет.

⬇️ Между циклами 4 и 5:

TRUE → FALSE

Это задний фронт.

Значит:

F_TRIG.Q = TRUE

только на один цикл.

⬆️ Между циклами 6 и 7 снова происходит:

FALSE → TRUE

Поэтому R_TRIG срабатывает второй раз.

Итого:

R_TRIG — 2 срабатывания
F_TRIG — 1 срабатывание


💡 Главная мысль

R_TRIG реагирует только на:

FALSE → TRUE

F_TRIG — только на:

TRUE → FALSE

Само длительное состояние TRUE или FALSE новых импульсов не создаёт.

💬 Получилось ответить правильно до открытия спойлера?

#CODESYS #ПЛК #R_TRIG #F_TRIG #StructuredText #ПрограммированиеПЛК
📘 Новая книга по CODESYS 3.5

Подготовил практическую книгу по программированию ПЛК в CODESYS.

Внутри — понятные объяснения, примеры на Structured Text и разборы типовых задач.

📎 Книгу прикрепляю к этому посту.

Если материал окажется полезным — поделитесь постом с друзьями и коллегами, которым интересны ПЛК, CODESYS и промышленная автоматизация.

🔁 Репосты очень помогают развивать канал и делать больше полезных материалов.

#CODESYS #ПЛК #АСУТП #StructuredText #ПрограммированиеПЛК
👍1🤝1
🔀 CASE в CODESYS: когда алгоритм начинает тонуть в IF

Небольшой проект легко написать на нескольких IF.

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

Для последовательных алгоритмов часто удобнее связка:

ENUM + CASE

Например:

IDLE → STARTING → WORKING → STOPPING → IDLE


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

👇 В следующем посте — подробный пример на Structured Text.

📤 Если тема актуальна — перешлите пост коллеге, который программирует ПЛК.

#CODESYS #ПЛК #StructuredText #CASE #ПрограммированиеПЛК
🔀 CASE в CODESYS: как строить понятные пошаговые алгоритмы

По мере роста программы ПЛК часто появляется что-то похожее:

IF xStart AND NOT xStep1 THEN
...
END_IF;

IF xStep1 AND xSensor THEN
...
END_IF;

IF xStep2 AND fbTimer.Q THEN
...
END_IF;


Сначала всё выглядит нормально.

Но потом появляются:

• дополнительные условия
• блокировки
• таймеры
• аварийные переходы
• ручной режим
• повторный запуск

И через некоторое время становится трудно ответить на простой вопрос:

«На каком этапе сейчас находится алгоритм?»

Для последовательных процессов часто удобнее использовать CASE и явно хранить текущее состояние.

━━━━━━━━━━━━━━━━━━

🏭 Пример задачи

Пусть после команды «Пуск» механизм должен:

1️⃣ включить двигатель

2️⃣ дождаться срабатывания датчика

3️⃣ работать ещё 5 секунд

4️⃣ остановиться

У процесса можно выделить четыре состояния:

IDLE
STARTING
WORKING
STOPPING


Создадим перечисление:

TYPE E_State :
(
IDLE,
STARTING,
WORKING,
STOPPING
);
END_TYPE


И переменную текущего состояния:

VAR
eState : E_State := IDLE;
END_VAR


Теперь вместо нескольких BOOL-переменных вида:

xStep1
xStep2
xStep3
xStep4


мы имеем одну:

eState

которая однозначно показывает текущий этап.

━━━━━━━━━━━━━━━━━━

⏱ Сначала подготовим таймер

Пусть в состоянии WORKING механизм должен работать 5 секунд.

Объявим:

VAR
eState : E_State := IDLE;
fbWorkTimer : TON;

xStart : BOOL;
xSensor : BOOL;
xMotor : BOOL;
END_VAR


Таймер удобно вызывать циклически:

fbWorkTimer(
IN := eState = WORKING,
PT := T#5s
);


Когда состояние становится WORKING, таймер начинает отсчёт.

При выходе из этого состояния:

IN = FALSE


и TON сбрасывается.

━━━━━━━━━━━━━━━━━━

🔧 Теперь сам CASE

CASE eState OF

IDLE:
xMotor := FALSE;

IF xStart THEN
eState := STARTING;
END_IF;


STARTING:
xMotor := TRUE;

IF xSensor THEN
eState := WORKING;
END_IF;


WORKING:
xMotor := TRUE;

IF fbWorkTimer.Q THEN
eState := STOPPING;
END_IF;


STOPPING:
xMotor := FALSE;
eState := IDLE;

END_CASE


Теперь структура алгоритма читается почти как описание технологического процесса.

━━━━━━━━━━━━━━━━━━

🔎 Разберём по шагам

1. IDLE

Механизм находится в ожидании:

xMotor := FALSE;


При появлении команды:

xStart = TRUE


выполняется:

eState := STARTING;


━━━━━━━━━━━━━━━━━━

2. STARTING

Включаем двигатель:

xMotor := TRUE;


И ждём подтверждающий сигнал от датчика:

IF xSensor THEN
eState := WORKING;
END_IF;


Пока датчик не сработал, алгоритм остаётся в STARTING.

━━━━━━━━━━━━━━━━━━

3. WORKING

Двигатель продолжает работать:

xMotor := TRUE;


А таймер:

fbWorkTimer.Q


станет TRUE после пяти секунд пребывания в состоянии WORKING.

После этого:

eState := STOPPING;


━━━━━━━━━━━━━━━━━━

4. STOPPING

Останавливаем двигатель:

xMotor := FALSE;


и возвращаем автомат в исходное состояние:

eState := IDLE;


Цикл завершён.

━━━━━━━━━━━━━━━━━━

🧠 Что здесь даёт CASE?

Представим:

eState = STARTING


Тогда выполняется только логика ветки:

STARTING:


Код IDLE, WORKING и STOPPING в этом выполнении CASE не выполняется.

Поэтому состояния процесса хорошо отделены друг от друга.

━━━━━━━━━━━━━━━━━━

⚠️ Важная особенность переходов

Допустим, внутри STARTING выполняется:

eState := WORKING;


Это не означает, что ветка WORKING немедленно начнёт выполняться ниже в том же CASE.

Текущая ветка уже была выбрана.
При следующем выполнении этого CASE значение eState будет прочитано снова, и тогда будет выбрана ветка:

WORKING:


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

━━━━━━━━━━━━━━━━━━

✅ Почему ENUM лучше набора чисел?

Можно было бы написать:

iState := 0;
iState := 10;
iState := 20;
iState := 30;


Но что означает:

iState = 20?

Без дополнительного описания — непонятно.

А:

eState = WORKING


читается сразу.

Именно поэтому для состояний удобно использовать ENUM.

━━━━━━━━━━━━━━━━━━

🔧 Диагностика тоже становится проще

Если механизм остановился и оператор сообщает:

«Почему он не продолжает работу?»

в онлайн-режиме можно посмотреть:

eState = STARTING


После этого понятно:

алгоритм ждёт условия перехода из STARTING.

В нашем случае первым делом стоит проверить:

xSensor

Это значительно удобнее, чем искать комбинацию из нескольких:

xStep1, xStep2, xStep3...

━━━━━━━━━━━━━━━━━━

🏗 Где такой подход особенно полезен?

ENUM + CASE хорошо подходит для:

• последовательностей запуска оборудования
• пошаговых технологических процессов
• дозирования
• промывки
• продувки
• перемещения механизмов
• автоматических циклов
• управления режимами
• конечных автоматов
• процедур запуска и останова

━━━━━━━━━━━━━━━━━━

⚠️ Но CASE — не замена каждому IF

Не нужно превращать вообще всю программу в одну гигантскую машину состояний.

Если необходимо просто проверить условие:

IF rTemperature > 90.0 THEN
xHighTemp := TRUE;
END_IF;


обычный IF прекрасно подходит.

CASE особенно полезен тогда, когда есть:

состояния → условия перехода → следующее состояние.

━━━━━━━━━━━━━━━━━━

💡 Главная мысль

IF отвечает на вопрос:

«Выполняется ли условие?»

А связка:

ENUM + CASE


помогает описать:

«В каком состоянии находится алгоритм и куда он должен перейти дальше?»

Для небольшого кода разница может быть незаметна.

Но в большом промышленном проекте понятная структура программы сильно упрощает:

• разработку
• ПНР
• диагностику
• сопровождение
• последующую доработку

Хороший код должен быть понятен не только ПЛК, но и инженеру, который откроет проект через год.

━━━━━━━━━━━━━━━━━━

💬 А как вы строите пошаговые алгоритмы?

Используете CASE, отдельные BOOL для каждого шага или свой шаблон машины состояний?

📤 Если статья оказалась полезной — отправьте её другу или коллеге, который работает с CODESYS и ПЛК.

🔁 Можно также репостнуть материал в профильный чат или канал — так о наших технических разборах узнает больше инженеров.

#CODESYS #ПЛК #АСУТП #StructuredText #ST #CASE #ENUM #ПрограммированиеПЛК
👍3