Codesys 3.5 Обучение программированию.
263 subscribers
38 photos
13 videos
1 file
63 links
Download Telegram
👋 Новым подписчикам и участникам чата — добро пожаловать!
Меня зовут Андрей, я автор курсов «Школы АСУ ТП». Для тех, кто хочет разобраться в программировании ПЛК и применять знания на практике, расскажу о двух моих курсах.
📘 Основы программирования ПЛК в CODESYS 3.5
Открыли CODESYS, а дальше непонятно: с чего начать проект, где писать код и как проверить, что программа работает?
Этот курс — для знакомства со средой и освоения основ. Разбираем структуру проекта, переменные, программы и функциональные блоки, запуск на виртуальном контроллере и проверку работы в онлайн-режиме.
Цель — не просто повторить пример, а понять, как устроена и выполняется программа ПЛК.
👉 Программа курса и запись: https://codesys3-5.skillspace.ru/l/programmirovanie-v-srede-codesys-3-5⁠�
🤝4
📊 Обработка аналоговых сигналов ПЛК в CODESYS 3.5
Датчик подключён. Но как превратить его сигнал в понятные значения температуры, давления или уровня? Как задать уставки и обработать ошибки?
В этом курсе разбираем сигналы 0–10 В и 4–20 мА, преобразование исходных значений в инженерные единицы, масштабирование, пороги и уставки, сигнализацию, ручной режим и визуализацию.
Практическая часть — разработка функционального блока в CODESYS 3.5 на языке ST.
От сигнала датчика — к его обработке и использованию в программе.
👉 Программа курса и запись: https://codesys3-5.skillspace.ru/l/obrabotka-analogovyh-signalov-plk-0b9b92⁠�
💡 Только начинаете? Начните с основ CODESYS. Уже знакомы со средой и хотите разобраться в работе с датчиками — переходите к курсу по аналоговым сигналам.
CODESYS 3.5: от первых программ ПЛК до обработки сигналов датчиков

Работаете с автоматикой или начинаете изучать программирование контроллеров? В «Школе АСУ ТП» есть два курса под разные задачи.

📘 Основы программирования ПЛК в CODESYS 3.5

Начните с основ: разберитесь в программировании контроллеров и освойте работу в среде CODESYS 3.5. Курс подойдёт для первого знакомства с программированием ПЛК и систематизации базовых знаний.

👉 "Посмотреть программу курса и записаться" (https://codesys3-5.skillspace.ru/l/programmirovanie-v-srede-codesys-3-5)

📊 Обработка аналоговых сигналов ПЛК в CODESYS 3.5

Разберитесь с сигналами 4–20 мА и 0–10 В, масштабированием и обработкой показаний датчиков. Для тех, кто хочет понимать, как входной сигнал превращается в значение давления, температуры или уровня в программе контроллера.

👉 "Посмотреть программу курса и записаться" (https://codesys3-5.skillspace.ru/l/obrabotka-analogovyh-signalov-plk-0b9b92)

Знакомьтесь с материалами и присоединяйтесь к сообществу:

📣 "Telegram-канал о CODESYS — материалы по программированию ПЛК" (https://t.me/codesys4)

💬 "Telegram-чат CODESYS 3.5 — вопросы, обсуждения и обмен опытом" (https://t.me/codesys35)
​⚡ Почему IF срабатывает много раз, хотя кнопку нажали только один раз?

Разберём простую, но очень важную особенность работы ПЛК.

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

Пишем:

IF xButton THEN
iCounter := iCounter + 1;
END_IF


На первый взгляд всё логично:

🔘 нажали кнопку
➕ счётчик увеличился на 1

Но после одного нажатия вдруг получаем:

iCounter = 73


😳 Почему не 1?

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

🔄 Всё дело в цикле ПЛК

ПЛК выполняет программу не один раз.

Он повторяет её циклически — снова и снова.

Допустим, время цикла задачи:

10 мс

А кнопку оператор удерживал примерно:

1 секунду

За это время программа выполнится примерно:

1000 мс / 10 мс = 100 циклов


И всё это время:

xButton = TRUE

Значит, в каждом цикле снова выполняется:

iCounter := iCounter + 1;


📈 Поэтому одно физическое нажатие может привести к десяткам или даже сотням выполнений одной и той же команды.

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

🎯 Нужно ловить не состояние, а событие

Нас интересует не то, сколько времени xButton находится в состоянии TRUE.

Нам нужен только момент перехода:

FALSE → TRUE

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

В CODESYS для его обнаружения есть стандартный функциональный блок:

R_TRIG

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

✅ Правильный вариант

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

fbTrig(CLK := xButton);

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


Теперь выход fbTrig.Q становится TRUE только на один цикл ПЛК — в момент появления переднего фронта.

Получаем:

🔘 нажали → +1
⏳ держим кнопку → ничего
🔘 отпустили → ничего
🔘 нажали снова → +1

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

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

Когда мы пишем:

IF xButton THEN

мы спрашиваем:

«Сигнал сейчас активен?»

А R_TRIG отвечает на другой вопрос:

«Сигнал появился именно в этом цикле?»

Это две совершенно разные задачи.

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

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

Обработка фронта постоянно встречается в программах ПЛК:

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

То есть везде, где действие должно выполниться один раз по событию, а не каждый цикл ПЛК.

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

💡 Запомните

IF проверяет состояние.

R_TRIG ловит момент появления сигнала.

Небольшая разница в коде — огромная разница в поведении программы.

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

💬 А как вы обрабатываете фронты в своих проектах?

Используете стандартные R_TRIG / F_TRIG или пишете свою логику через запоминание предыдущего состояния?

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

На картинке — простая ситуация из программы ПЛК.

xButton = TRUE в течение 5 циклов подряд.

Начальное значение:

iCounter = 0

❓ Какое значение будет у iCounter после пятого цикла?

Сначала попробуйте ответить самостоятельно.

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

#CODESYS #ПЛК #StructuredText #ПрограммированиеПЛК
🧠 РАЗБОР КОДА

Исходный код:

IF xButton THEN
iCounter := iCounter + 1;
END_IF


Начальное значение:

iCounter = 0

Сигнал:

xButton = TRUE

в течение 5 циклов ПЛК подряд.

❓ Что будет в iCounter после пятого цикла?

А) 1
Б) 5
В) 0
Г) Зависит от времени цикла

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

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

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

Почему?

Условие xButton = TRUE выполняется во всех пяти циклах.

Поэтому команда:

iCounter := iCounter + 1;

тоже выполняется пять раз.

Получаем:

0 → 1 → 2 → 3 → 4 → 5

Время цикла здесь не влияет на результат, потому что в условии уже сказано: xButton оставался TRUE ровно 5 циклов.


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

IF проверяет состояние сигнала в каждом цикле.

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

FALSE → TRUE

нужно использовать определение переднего фронта, например R_TRIG.

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

#CODESYS #ПЛК #StructuredText #ПрограммированиеПЛК
⏱ TON в CODESYS

Почему таймер иногда «не срабатывает»?

Разбираем принцип работы TON, сброс при IN = FALSE и типичные ошибки в программе.

👇 Подробный разбор — в следующем посте.
⏱ TON в CODESYS: как работает таймер задержки включения

TON — один из самых используемых таймеров в программах ПЛК.

Задержка включения двигателя, подтверждение аварии, фильтрация кратковременных сигналов, контроль превышения температуры — во всех этих задачах часто используется именно TON.

Но иногда возникает ситуация:

«На входе уже был TRUE, а таймер почему-то не сработал».

Разберём, как TON работает на самом деле.

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

🔹 Что делает TON?

TON — это таймер задержки включения.

Объявим экземпляр таймера:

VAR
fbTimer : TON;
xSignal : BOOL;
xAlarm : BOOL;
END_VAR


Вызовем его:

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


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

IN — вход запуска таймера

PT — заданное время задержки

Q — выход таймера

ET — прошедшее время

В нашем примере:

PT := T#5s


Это означает, что IN должен непрерывно оставаться в состоянии TRUE в течение 5 секунд.

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

🔄 Что происходит после IN = TRUE?

Пока:

IN = FALSE

таймер находится в сброшенном состоянии:

Q  = FALSE
ET = T#0s


Как только:

IN = TRUE

начинается отсчёт времени.

Условно процесс выглядит так:

0 сек → ET = 0 сек
1 сек → ET = 1 сек
2 сек → ET = 2 сек
3 сек → ET = 3 сек
4 сек → ET = 4 сек
5 сек → ET = 5 сек


Пока:

ET < PT


выход:

Q = FALSE


Когда заданное время достигнуто:

ET ≥ PT


получаем:

Q = TRUE


Если IN продолжает оставаться TRUE, выход Q тоже остаётся активным.

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

⚠️ Самая важная особенность TON

Теперь представим такую ситуацию:

xSignal = TRUE   → 4.8 сек
xSignal = FALSE → 0.1 сек
xSignal = TRUE → снова


До заданных 5 секунд не хватило всего 0.2 секунды.

Что произойдёт?

Как только:

IN = FALSE

обычный TON сбрасывается:

ET → T#0s
Q → FALSE


Когда IN снова станет TRUE, отсчёт начнётся заново.

Предыдущие 4.8 секунды таймер не запоминает.

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

🧠 TON считает непрерывное время

Это ключевой момент.

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

«Сколько всего времени сигнал был TRUE?»

Он отвечает:

«Как долго сигнал непрерывно находится в TRUE?»

Например:

TRUE  → 3 сек
FALSE → 1 сек
TRUE → 3 сек


Суммарно сигнал был активен:

3 + 3 = 6 секунд


Но при:

PT := T#5s


выход Q не станет TRUE.

Потому что ни один непрерывный участок TRUE не длился 5 секунд.

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

🏭 Практический пример: задержка аварии

Допустим, есть температура:

rTemperature

Нужно сформировать аварию только в том случае, если температура выше 90 °C непрерывно держится 5 секунд.

Сначала получаем условие превышения:

xHighTemp := rTemperature > 90.0;


Затем запускаем таймер:

fbHighTemp(
IN := xHighTemp,
PT := T#5s
);

xAlarm := fbHighTemp.Q;


Что произойдёт?

Если температура кратковременно поднимется выше 90 °C, например на 300 мс:

xHighTemp = TRUE


таймер начнёт отсчёт.

Но как только температура снова опустится ниже 90 °C:

xHighTemp = FALSE


таймер сбросится.

Авария не появится.

Если же температура остаётся выше 90 °C непрерывно 5 секунд:

fbHighTemp.Q = TRUE


и сформируется аварийный сигнал.

Так TON позволяет отфильтровать кратковременные выбросы.

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

⚠️ Частая ошибка: вызывать таймер только внутри IF

Иногда встречается такой код:

IF xSignal THEN
fbTimer(
IN := TRUE,
PT := T#5s
);
END_IF


Проблема в том, что когда xSignal = FALSE, функциональный блок вообще не вызывается.

Его входы и внутреннее состояние в этом цикле не обновляются обычным способом.
Гораздо понятнее и надёжнее вызывать экземпляр таймера каждый цикл:

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