Как сделать триггер в st кодесис
Часовой пояс: UTC + 3 часа (Russia: MSK)
![]() |
![]() |
Запрошенной темы не существует.
Часовой пояс: UTC + 3 часа (Russia: MSK)
![]() |
![]() |
Как реализовать T-триггер в CoDeSys?
Вот на 2-х Функциональных блоках.
TRIG_Vk(CLK:= knopka, Q=> ); (выделяем фронт нажатия кнопки (ФБ R_TRIG))
SR_Vk(SET1:= TRIG_Vk.Q AND NOT SR_Vk.Q1 , RESET:=TRIG_Vk.Q AND SR_Vk.Q1, Q1=> ); (ФБ SR или RS)
03.03.2010, 11:35
спасибо, получилось. правда это тоже 4 блока, но меньше видно нельзя. я проектирую в Fbd:)
03.03.2010, 12:54
Сообщение от Литрович
спасибо, получилось. правда это тоже 4 блока, но меньше видно нельзя. я проектирую в Fbd:)
выделите в отдельный функциональный блок
30.03.2010, 11:49
можно потробнее? желательно на St.
с кодом для фун. блоков и программы.
30.03.2010, 13:35
вопрос новичка в кодесис ,а что в кодесис нет готовых библиотек фб (счетчики ,регистры сдвига,дешифраторы,и.т.д?)
30.03.2010, 14:33
Николаев Андрей
Ну вот, rovki — прям расстроили ;).
Надо подключить библиотеки Util.lib и Standart.lib — там все есть
30.03.2010, 16:53
стыдно,но все это ,в свое время реализовывал на жесткой логике,потом на однокристалках,(локальные сети,бортовые микро эвм,системы диагностики,испытательные стенды,итд,итп) потом на плк без кодесис технологические линии.сейчас другие задачи и участие в форуме -отдушина(ностальгия) по тем временам.и данный вопрос из разряда «для общего развития».:) .будет необходимость-будет и решение.
меня как раз и интересует почему люди имея такую мощь (кодесис) спрашивают как реализовать тот или иной функциональный блок,и существует ли конвертация проектов из одного языка в другой средствами кодесис, внутри кодесис.а на счет фбд -посмотрел -слабовато,ожидал большего, для электронщика ловить нечего,делай все на элементах малой степени интеграции.
Как программировать на codesys 2.3 новичку? Легко
Перед тем, как программировать ПЛК в среде разработки CoDeSyS 2.3 новички часто задаются вопросом: А какие системы требуется установить для корректной работы с аппаратом?? А как конфигурировать входы и выходы контроллера?? А каким образом связать устройство с ПК?? И снова, а как, а как?? Все мы с вами понимаем, устройства сложные и алгоритмы объёмные, и на изучение потребуется время. Я вот думаю, может написать небольшую книжку и назвать codesys для чайников? А вы согласны?
Из этой статьи вы узнаете:
Здравствуйте уважаемые коллеги и гости. Пишет вам автор блога kip-world.ru, Гридин Семён, и в этой статье я вам расскажу, как правильно программировать контроллер. Тема достаточно актуальная, я надеюсь после прочтения статьи, некоторые вопросы отпадут самим собой. =)
Как работает ПЛК?
ПЛК(программируемый логический контроллер) — это устройства полностью автоматизирующие работу аппаратов, различных агрегатов и станков. Фактически, это некий блок, который содержит входы и выходы, для подключения датчиков и исполнительных органов. Внутри прописывается логика.
Вычисления в устройстве выполняются циклически. То есть одни и те же действия выполнения программы выполняются в короткий промежуток времени.
В один цикл осуществляемый прибором выполняются следующие операции:
- Начало цикла;
- Чтение состояния входа;
- Выполнение кода пользователя;
- Запись состояния выходов;
- Обслуживание аппаратных ресурсов;
- Монитор системы исполнения;
- Контроль времени цикла;
- Переход на начало цикла;
Не буду больше разглагольствовать по теории. Давайте сразу перейдём к практике.
Из чего состоит программный комплекс для полноценной работы с ПЛК
Конечно вам поначалу покажется, что слишком много нужно знать, чтобы связать друг с другом основное приложение и утилитки, а потом соединить устройство. Я хочу вам сказать, что ничего сложного в процессе установки и связей — нет. В этом поможет моя статья.
Для начала нам нужно установить основной дистрибутив CoDeSyS 2.3 c официального сайта ОВЕН . А, я предлагаю во многих постах, касающихся программирования, использовать устройство ОВЕН ПЛК63 . Так как это универсальное устройство с экраном. У него на борту есть и дискретные входы, и аналоговые входы, и релейные выходы.
Итак, скачиваем программу:
Затем следует стандартная процедура установки. Указываем путь и все время жмём “Далее”, “Далее”.
Следующим этапом будет установка таргетов для плк. Таргет — это некое описание о конфигурации ПЛК. Инструкция подсказывает CoDeSyS 2.3, какое количество и какие входы/выходы имеет устройство.
Скачиваем также с сайта ОВЕН . Рекомендую установить все таргеты, которые там есть. Чтобы потом не искать и не думать об этом, если придется писать алгоритм на другой ПЛК.
Запускаем автоматический установщик, устанавливаем инструкции. Всё, половину пути мы с вами уже сделали в этой работе! После этих всех процедур можно устанавливать библиотеки, но о них позже. Переходим к следующему пункту.
Рабочее окно программы
Дистрибутив мы с вами установили, таргеты тоже. Давайте мы с вами рассмотрим рабочее окно среды разработки, элементы меню и основные вкладки.
Основное поле на рисунке выше делится на три области:
- Редактор переменных и их типов;
- Дерево объектов;
- Редактор основного алгоритма программы;
Редактор переменных — здесь мы с вами вводим переменные и присваиваем им типы данных. Для тех, кто не знает, переменная — это имя, к которому будет обращаться программа и возвращать результат. А тип данных определяет род информации, диапазон представления чисел и множество других операций.
Дерево объектов — в этом окне располагаются такие объекты, как функции, функциональные блоки, подпрограммы, конфигурация ПЛК, библиотеки. Об этом я расскажу позже.
Редактор программы — тут мы с вами описываем основной алгоритм программы работы контроллера. Пишется на любом языке стандарта МЭК. Более подробно, можете прочитать статью .
Простой пример на ST
Для удобства восприятия информации я постарался структурировать. Поэтапно расписал последовательность действий. Если возникнут вопросы или пожелания, обязательно пишите в комментариях.
Изначально я размещу в статье код на языке ST. Логика работы заключается в следующем: на дискретный вход прибора подаётся сигнал и через задержку времени включается выход. В принципе задача простая, и мы с вами её решим.
Таймеры и триггеры CODESYS. Еще один шаг Arduino к классическому ПЛК
Случается программировать контроллеры (ПЛК) в среде CODESYS. Все, кто имел дело с этой системой, знают, что в любом проекте присутствует библиотека Standard.lib, в которой реализованы базовые таймеры, триггеры, счетчики и некоторое кол-во других функций и функциональных блоков. Многие из этих блоков постоянно используются в программах для ПЛК. А сама библиотека, как и языки программирования CODESYS, является воплощением стандарта IEC 61131-3, т.е. призвана помочь при программировании классических ПЛК задач.
Одна из особенностей программ для ПЛК в том, что основной цикл программы должен выполняться без существенных задержек, в нем не должно быть внутренних циклов с неопределенным временем выхода или синхронных вызовов «задумчивых» функций, особенно это касается коммуникаций по медленным каналам. Обновление входных и выходным образов процесса происходит только на границе основного цикла, и чем дольше мы будем «сидеть» внутри одной итерации цикла, тем меньше мы будет знать о реальном состоянии объекта управления, в конечном итоге сработает watchdog переполнения времени выполнения цикла. Многие могут мне возразить, сказав, что современные ПЛК многозначны, есть с поддержкой аппаратных прерываний. Согласен, но разговор о таких системах не входит в мои планы, я же хочу поговорить о (квази, псевдо — выбирайте) ПЛК однозадачной реализации (без прерываний) на базе микропроцессорной платформы Arduino, в котором есть только один основной цикл. Кстати, не лишним будет сказать, что на написание данной заметки меня сподвигла статья Ардуино-совместимый ПЛК CONTROLLINO, часть 1 о попытке аппаратного воплощения Arduino в пром. ПЛК.
Несколько слов об Arduino. С точки зрения программиста ПЛК, Arduino — это типичный контроллер с одним очень быстрым или, наоборот, очень медленным циклом loop(). На время выполнения цикла не накладывается никаких ограничений, и он может отработать и один, и бесконечное кол-во раз — по замыслу программиста. Когда программа проста и сводится к выполнению последовательных операций, регуляторов, без параллельных событий, то достаточно чередовать операции бесконечными вложенными циклами проверки условий и синхронными задержками типа delay(). Последовательные шаги такой программы будут выполняться буквально построчно, просто и логично. Но, как только возникает необходимость в программировании параллельных операций, необходимо менять парадигму программы.
В однозадачной системе добиться видимой параллельности можно только очень быстрым последовательным сканированием параллельных состояний, не задерживаясь подолгу на каждом вызове функции или проверке условия. С физическими входами-выходами проблем нет, функции отрабатывают достаточно быстро, а вот delay() становится неоправданным тормозом. И вот тут на смену приходят неблокирующие таймеры, те самые, которые в программировании ПЛК являются классикой. Суть в том, что для их работы используется миллисекундный счетчик времени, и все действия привязаны к значениям этого глобального счетчика.
А теперь давайте вспомним ту самую Standard.lib из CODESYS. В ней как раз реализованы МЭК-овские неблокирующие таймеры. Я взял ее за основу и портировал функции таймеров и триггеров в библиотечный код Arduino (С++). Т.е. попытался приблизить Arduino к классическому ПЛК.
Ниже я приведу краткое описание портированных функциональных блоков (FB) CODESYS и их аналоги в моей библиотеке plcStandardLib, все временные диаграммы верны для новой библиотеки Arduino. Подробнее описание исходных блоков можно посмотреть, например, в русскоязычной справке по CODESYS.
TON — функциональный блок «таймер с задержкой включения»
TON(IN, PT, Q, ET)
Входы IN и PT типов BOOL и TIME соответственно. Выходы Q и ET аналогично типов BOOL и TIME. Пока IN равен FALSE, выход Q = FALSE, выход ET = 0. Как только IN становится TRUE, начинается отсчет времени (в миллисекундах) на выходе ET до значения, равного PT. Далее счетчик не увеличивается. Q равен TRUE, когда IN равен TRUE, а ET равен PT, иначе FALSE. Таким
образом, выход Q устанавливается с задержкой PT от фронта входа IN.
В Arduino IDE:
TON TON1(); TON TON1(unsigned long PT); // с заданием интервала времени PT
Q = TON1.Run(boolean IN); // вызов "все в одном" TON1.IN = IN; TON1.Run(); Q = TON1.Q;
Временная диаграмма работы TON:
TOF — функциональный блок «таймер с задержкой выключения»
TOF(IN, PT, Q, ET)
Входы IN и PT типов BOOL и TIME соответственно. Выходы Q и ET аналогично типов BOOL и TIME. Если IN равен TRUE, то выход Q = TRUE и выход ET = 0. Как только IN переходит в FALSE, начинается отсчет времени (в миллисекундах) на выходе ET. При достижении заданной длительности отсчет останавливается. Выход Q равен FALSE, если IN равен FALSE и ET равен PT, иначе — TRUE. Таким образом, выход Q сбрасывается с задержкой PT от спада входа IN.
В Arduino IDE:
Очень похоже на TON, для краткости:
TOF TOF1(unsigned long PT); // с заданием интервала времени PT Q = TOF1.Run(boolean IN); // вызов "все в одном"
Временная диаграмма работы TOF:
TP — функциональный блок «импульс-таймер»
TP(IN, PT, Q, ET)
Входы IN и PT типов BOOL и TIME соответственно. Выходы Q и ET аналогично типов BOOL и TIME. Пока IN равен FALSE, выход Q = FALSE, выход ET = 0. При переходе IN в TRUE выход Q устанавливается в TRUE и таймер начинает отсчет времени (в миллисекундах) на выходе ET до достижения длительности, заданной PT. Далее счетчик не увеличивается. Таким образом, выход Q генерирует импульс длительностью PT по фронту входа IN.
В Arduino IDE:
Очень похоже на TON, для краткости:
TP TP1(unsigned long PT); // с заданием интервала времени PT Q = TP1.Run(boolean IN); // вызов "все в одном"
Временная диаграмма работы TP:
R_TRIG — функциональный блок «дeтектор фронта»
Функциональный блок R_TRIG генерирует импульс по переднему фронту входного сигнала. Выход Q равен FALSE до тех пор, пока вход CLK равен FALSE. Как только CLK получает значение TRUE, Q устанавливается в TRUE. При следующем вызове функционального блока выход сбрасывается в FALSE. Таким образом, блок выдает единичный импульс при каждом переходе CLK из FALSE в TRUE.
Пример CODEDESYS на языке ST:
RTRIGInst : R_TRIG ; RTRIGInst(CLK:= VarBOOL1); VarBOOL2 := RTRIGInst.Q;
В Arduino IDE:
R_TRIG R_TRIG1;
Q = R_TRIG1.Run(boolean CLK); // вызов "все в одном" R_TRIG1.CLK = CLK; R_TRIG1.Run(); Q = R_TRIG1.Q;
F_TRIG — функциональный блок «дeтектор спада»
Функциональный блок F_TRIG генерирует импульс по заднему фронту входного сигнала.
Выход Q равен FALSE до тех пор, пока вход CLK равен TRUE. Как только CLK получает значение FALSE, Q устанавливается в TRUE. При следующем вызове функционального блока выход сбрасывается в FALSE. Таким образом, блок выдает единичный импульс при каждом переходе CLK из TRUE в FALSE.
В Arduino IDE:
F_TRIG F_TRIG1; Q = F_TRIG1.Run(boolean CLK); // вызов "все в одном"
RS_TRIG — функциональный блок RS-триггер / SR_TRIG — функциональный блок SR-триггер
Переключатель с доминантой выключения, RS-триггер:
Q1 = RS (SET, RESET1)
Переключатель с доминантой включения:
Q1 = SR (SET1, RESET)
Входные переменные SET и RESET1 — как и выходная переменная Q1 типа BOOL.
В Arduino IDE:
RS_TRIG RS_TRIG1; Q = RS_TRIG1.Run(boolean SET, boolean RESET); // вызов "все в одном"
SR_TRIG SR_TRIG1; Q = SR_TRIG1.Run(boolean SET, boolean RESET); // вызов "все в одном"
Исходный код и пример
plcStandardLib_1.h
/* * plcStandardLib_1.h * * Created on: 01.01.2017 * Author: Admin */ #ifndef PLCSTANDARDLIB_1_H_ #define PLCSTANDARDLIB_1_H_ #if ARDUINO >= 100 #include #else #include #endif /* ------------------- TON ------------------- */ class TON < public: TON(); TON(unsigned long PT); boolean Run(boolean IN); boolean Q; // выходная переменная boolean IN; // входная переменная unsigned long PT; // входная переменная unsigned long ET; // выходная переменная - текущее значение таймера private: boolean _M; // внутренний флаг unsigned long _StartTime; >; /* ------------------- TOF ------------------- */ class TOF < public: TOF(); TOF(unsigned long PT); boolean Run(boolean IN); boolean Q; // выходная переменная boolean IN; // входная переменная unsigned long PT; // входная переменная unsigned long ET; // выходная переменная - текущее значение таймера private: boolean _M; // внутренний флаг unsigned long _StartTime; >; /* ------------------- TP ------------------- */ class TP < public: TP(); TP(unsigned long PT); boolean Run(boolean IN); boolean Q; // выходная переменная boolean IN; // входная переменная unsigned long PT; // входная переменная unsigned long ET; // выходная переменная - текущее значение таймера private: boolean _M; // внутренний флаг unsigned long _StartTime; >; /* ------------------- R_TRIG ------------------- */ class R_TRIG // детектор фронта сигнала < public: R_TRIG(); boolean Run(boolean CLK); boolean CLK; // входная переменная boolean Q; // выходная переменная private: boolean _M; // внутренний флаг >; /* ------------------- F_TRIG ------------------- */ class F_TRIG // детектор спада сигнала < public: F_TRIG(); boolean Run(boolean CLK); boolean CLK; // входная переменная boolean Q; // выходная переменная private: boolean _M; // внутренний флаг >; /* ------------------- RS_TRIG ------------------- */ class RS_TRIG // детектор спада сигнала < public: RS_TRIG(); boolean Run(); boolean Run(boolean SET, boolean RESET); boolean SET; // установка триггера boolean RESET; // сброс триггера boolean Q; // выходная переменная //private: >; /* ------------------- SR_TRIG ------------------- */ class SR_TRIG // детектор спада сигнала < public: SR_TRIG(); boolean Run(); boolean Run(boolean SET, boolean RESET); boolean SET; // установка триггера boolean RESET; // сброс триггера boolean Q; // выходная переменная //private: >; #endif /* PLCSTANDARDLIB_H_ */
plcStandardLib_1.cpp
/* * plcStandardLib_1.h * * Created on: 01.01.2017 * Author: Admin */ #include "plcStandardLib_1.h" /* ------------------- TON ------------------- */ TON::TON() < IN = false; PT = 0; _M = false; _StartTime = 0; Q = false; ET = 0; >TON::TON(unsigned long PT) < IN = false; TON::PT = PT; _M = false; _StartTime = 0; Q = false; ET = 0; >boolean TON::Run(boolean IN) < TON::IN = IN; if (!TON::IN) < Q = false; ET = 0; _M = false; >else < if (!_M) < _M = true; // взводим флаг М _StartTime = millis(); // ET = 0; // сразу = 0 >else < if (!Q) ET = millis() - _StartTime; // вычисляем время >if (ET >= PT) Q = true; > return Q; > /* ------------------- TOF ------------------- */ TOF::TOF() < IN = false; PT = 0; _M = false; _StartTime = 0; Q = false; ET = 0; >TOF::TOF(unsigned long PT) < IN = false; TOF::PT = PT; _M = false; _StartTime = 0; Q = false; ET = 0; >boolean TOF::Run(boolean IN) < TOF::IN = IN; if (TOF::IN) < Q = true; ET = 0; _M = true; >else < if (_M) < _M = false; // сбрасываем флаг М _StartTime = millis(); // ET = 0; // сразу = 0 >else < if (Q) ET = millis() - _StartTime; // вычисляем время >if (ET >= PT) Q = false; > return Q; > /* ------------------- TP ------------------- */ TP::TP() < IN = false; PT = 0; _M = false; _StartTime = 0; Q = false; ET = 0; >TP::TP(unsigned long PT) < IN = false; TP::PT = PT; _M = false; _StartTime = 0; Q = false; ET = 0; >boolean TP::Run(boolean IN) < TP::IN = IN; if (!_M) < if (TP::IN) < _M = true; // взводим флаг М _StartTime = millis(); if (ET < PT) Q = true; >> else < if (Q) < ET = millis() - _StartTime; // вычисляем время if (ET >= PT) Q = false; > else < if (!TP::IN) < _M = false; ET = 0; >> > return Q; > /* ------------------- R_TRIG ------------------- */ R_TRIG::R_TRIG() < CLK = false; _M = false; Q = false; >boolean R_TRIG::Run(boolean CLK) < R_TRIG::CLK = CLK; Q = R_TRIG::CLK && !_M; _M = R_TRIG::CLK; return Q; >F_TRIG::F_TRIG() < CLK = false; _M = true; Q = false; >boolean F_TRIG::Run(boolean CLK) < F_TRIG::CLK = CLK; Q = !F_TRIG::CLK && !_M; _M = !F_TRIG::CLK; return Q; >/* ------------------- RS_TRIG ------------------- */ RS_TRIG::RS_TRIG() < SET = false; RESET = false; Q = false; >boolean RS_TRIG::Run(boolean SET, boolean RESET) < RS_TRIG::SET = SET; RS_TRIG::RESET = RESET; Q = !RESET and (SET or Q); return Q; >boolean RS_TRIG::Run() < Q = !RESET and (SET or Q); return Q; >/* ------------------- SR_TRIG ------------------- */ SR_TRIG::SR_TRIG() < SET = false; RESET = false; Q = false; >boolean SR_TRIG::Run(boolean SET, boolean RESET) < SR_TRIG::SET = SET; SR_TRIG::RESET = RESET; Q = SET or (!RESET and Q); return Q; >boolean SR_TRIG::Run()
plcStandardLib_1_example.ino
#include "plcStandardLib_1.h" #define LED 13 #define ButtonIn 7 TON TON1(500); // Инициализация задержки включения, 500мс. TON TON2(1000); // Инициализация задержки включения, 1000мс. TOF TOF1(500); // Инициализация задержки выключения, 500мс. TP TP1(300); // Инициализация единичного импульса, 300мс. TP TP2(200); // Инициализация единичного импульса, 200мс. R_TRIG R_TRIG1; // Инициализация триггера фронта для кнопки void setup() < pinMode(ButtonIn, INPUT_PULLUP); pinMode(LED, OUTPUT); >void loop() < digitalWrite(LED, TP1.Run(R_TRIG1.Run(TON1.Run(digitalRead(ButtonIn))))); // TON1 - фильтрует дребезг контакта // R_TRIG1 - детектирует фронт сигнала // TP1 - генерирует импульс по фронту digitalWrite(LED, TP2.Run(TON2.Run(!TON2.Q))); // генератор импульса на базе TON и TP // TON2.Run(!TON2.Q)) - генератор единичного импульса // TP2 - генерирует импульс по фронту digitalWrite(LED, TOF1.Run(TON1.Run(digitalRead(ButtonIn)))); // Задержка включения и отключения >
Например, чтобы отфильтровать дребезг контактов кнопки (при размыкании тоже!) достаточно вот такого кода:
FiltredButtonIn = TON1.Run(digitalRead(ButtonIn))
В качестве заключения: вот так в CODESYS выглядит работа генератора импульса на базе цепочки таймеров TON и TP. В начале TON охватывается обратной связью с инверсией, и из него получается генератор единичного импульса, который запускает работу импульс-генератора TP. В моем примере Arduino аналог этого выглядит так:
digitalWrite(LED, TP2.Run(TON2.Run(!TON2.Q)));