Просмотр полной версии : Помогите разобраться...
Люди! Не дайте мозгу взорваться...
Написал небольшую тестовую программку. Объясните, пожалуйста, почему !YES работает, а !NO не работает? Разница только в том, что есть IF...
Скриншоты и файлы прикладываю...
да легче простого, ВЫ поставили импульсный тригер и выставили условие по его выходу, а если внутрь условия программа не забирается, то и выполнять в теле условия ни чего не будет, так что таймер внутри ИФа стоит без дела
Дак в том то и суть, что внутрь IF забирается. Проверял тестовой переменной. Просто TOF как бы "подвисает" о включенном состоянии- т.е. CLK и Q остаются в состоянии TRUE. А почему такое происходит - загадка...
1.Зачем вызывают ФБ ?
2.Чем таймеры отличается от ФБ ?
3.Что такое TOF ?
4.А когда и с какими параметрами он вызывается у Вас ?
Ну и делаем выводы
Просто TOF как бы "подвисает" о включенном состоянии- т.е. CLK и Q остаются в состоянии TRUE. А почему такое происходит - загадка.Q внутри условия хоть как будет TRUE потому что само условие у вас как раз по переменной Q. А ещё мы не знаем, как там у вас CLK изменяется.
Что такое TOF ?TON вверх ногами.
Q внутри условия хоть как будет TRUE потому что само условие у вас как раз по переменной Q. А ещё мы не знаем, как там у вас CLK изменяется.TON вверх ногами.
CLK меняется просто - это нажатие кнопочки. Я все равно не понимаю.... В моем понимании, вся программа PLC_PRG вместе с ФБ и прочим крутятся в цикле. Т. е. я нажал кнопку, Q включилось и сразу же выключилось - это же выход детектора переднего фронта, и начал работать отсчет времени TOF. Ведь в !YES же все так и работает...:confused:
Честное слово, голова уже не работает... Написал достаточно большой кусок своей первой программы, понадобился таймер, и на тебе...:(
Q включилось и сразу же выключилось - это же выход детектора переднего фронта, и начал работать отсчет времени TOFНет. Как только Q перешёл в FALSE, таймер останавливается потому что вы его больше не вызываете — не заходите в условие. Вообще таймер под триггером это... как-то нетрадиционно что ли.
Нет. Как только Q перешёл в FALSE, таймер останавливается потому что вы его больше не вызываете — не заходите в условие. Вообще таймер под триггером это... как-то нетрадиционно что ли.
Черд... Пока ехал до работы меня это таки осенило... :)
Теперь осталось понять, как правильно вызывать таймер по IF... :confused:
вообще Вы написали два куска, Вас чем правильный то вариант неустраивает?
!YES - это то, на чем я тренируюсь. Беру кусок кода и проверяю, как он работает.. А !NO - это то, что мне надо.
!NO на самом деле - это кусок длинного IF - ELSIF... В одном месте мне надо в течении 3-х секунд передавать TRUE одному ФБ... TOF мне показался наиболее подходящим... А как еще эту задачу можно решить?
WHILE по времени вообще пользоваться нельзя, как я понял - сторожевой таймер будет ругаться...
за блоком условий прописываете строчку таймера, а в условиях присваиваете входам таймера соответствующие значения, в этом случае у Вас все будет получатся.
Так?
trig1(CLK := up);
IF (trig1.Q ) THEN
TOF1.IN := trig1.Q;
END_IF
TOF1(IN := trig1.Q,PT:= T#3s);
IF TOF1.Q THEN
OUT1 := TOF1.ET;
END_IF
Как-то не очень... :( И до 3-х секунд не доходит.. :)
попробуйте так
ЗЫ теперь Вы сбрасываете вход, подачей на него одиночного импульса и соответственно таймер снова не работает
Спасибо огромное. Так работает... :rolleyes:
И еще один дурацкий вопрос: Где более правильно обзывать входы ПЛК? В глобальных переменных, или как я - в конфигураторе ПКЛ?
Я так понимаю, что если в глобальных переменных, то получится более "универсальный" код, однако при замене платформы адреса все равно съедут... Или я неправ?
Или может есть какой способ "перетаскивания" из конфигуратора ПЛК в GLOBAL_VARS ? Типа обозвал входы-выходы, написал комментарии - а оно бабах, и красота... :)
И что бы два раза не вставать - как вообще меняется платформа? Вот написал я под ПЛК110-60, а потом решил использовать этот же проект на ПЛК-100... В настройках целевой платформы я поменял платформу, а дальше?
Объявлять переменные можно где удобней. Особой разницы нет. Если вы меняете платформу, то в любом случае необходимо ручками переписывать переменные, хоть объявленные в конфигураторе, хоть по прямым адресам.
Теперь осталось понять, как правильно вызывать таймер по IF...С условием по выходу триггера проще делать без таймера:
VAR
timeout: TIME;
output: BOOL;
END_VAR
IF что-нибудь THEN
timeout := TIME() + T#5s;
END_IF
output := TIME() >= timeout;
С условием по выходу триггера проще делать без таймера:
VAR
timeout: TIME;
output: BOOL;
END_VAR
IF что-нибудь THEN
timeout := TIME() + T#5s;
END_IF
output := TIME() >= timeout;
Сейчас кодесиса под рукой нет... Я правильно понимаю эту конструкцию, что
"Если 'что-нибуть' TRUE, то через 5 сек. output будет TRUE"... Так?
А у нас TIME() меньше 0 при старте ?
То что изображено - это NOT TOF.Q
А у нас TIME() меньше 0 при старте ?К первому циклу уже больше, если не ошибаюсь.
"Если 'что-нибуть' TRUE, то через 5 сек. output будет TRUE"... Так?В данном случае немного иначе: «если „что-нибудь“ TRUE, то на следующие 5 секунд output сделать FALSE, в остальное время держать TRUE».
Таймер в таких ситуациях задалбывает требованием его вызывать. Если б хоть можно было писать timer(IN := ..., NOT Q => output) — ан нет, хрен тебе (при том, что в диаграммах отрицание ставится на любой вход или выход). В итоге лишние строки. Конечно, когда условие включения таймера держится, то лучше использовать нормальные TON, TOF и т.п.
VAR
timeout: TIME;
output: BOOL;
END_VAR
IF что-нибудь THEN
timeout := TIME() + T#5s;
END_IF
output := TIME() >= timeout;
А ничего, что при постоянном что-нибудь=TRUE output будет иногда подниматься ?
При «постоянном что-нибудь=TRUE» output будет постоянно FALSE. Ну и вообще я не просто так ограничил применимость этого решения:
С условием по выходу триггера
Авторы Arian-5 тоже так предполагали :)
Т.е. этот код зависит не только от "что-нибудь" ?
В 1996 году отличились французы. Из-за ошибок в программном обеспечении 4 июня был прерван полет космической ракеты Ariane 5. Убытки в результате составили более 500 миллионов долларов. А причина крылась в том, что по недосмотру переменная, которая описывала горизонтальную скорость ракеты, была представлена целым 16-битным числом. В результате, как только эта значение переменной превысило 32 768 (2 в 15-й степени), система управления ракетой, что называется, “подвисла”, а “сошедшую с ума” ракету пришлось уничтожить.
уже в 90-х годах. На этот раз пострадали бравые американские военные ракетчики, принимавшие участие в операции “Буря в пустыне”. К их удивлению, ракеты “Пэтриот”, использовавшиеся для перехвата в воздухе иракских ракет, периодически проходили мимо цели. Разбирательства начались после того, как пропущенная иракская ракета привела к гибели 28 американских солдат. Первоначально зародилось подозрение, что часть “Пэтриотов” технически неисправны. Однако созданная по этому поводу комиссия с удивлением обнаружила, что все обследованные ракеты абсолютно работоспособны. Вы уже, наверное, догадались, где пряталась ошибка? Конечно же, в программном обеспечении, использовавшимся американским зенитно-ракетным комплексом. Выяснилось, что система создавалась в расчете на то, что время ее непрерывной работы не будет превышать 14 часов, однако на практике комплексы непрерывно работали по 100 и более часов.Вроде бы пустяк, но оказалось, что используемое для определения времени программное обеспечение накапливало ошибки. За 100 часов работы набегала разница в 0,34 секунды. Программисты, оказывается, знали об этом, да посчитали факт несущественным...
При «постоянном что-нибудь=TRUE» output будет постоянно FALSE. Ну и вообще я не просто так ограничил применимость этого решения ..c условием по выходу триггера
Да хоть и триггером - те же проблемы. И тогда же :
ввиду, когда пройдет 49 с хвостиком суток от включения плк
Бывает такая нездоровая фигня как стабильное питание и безаварийная работа. Ну мы ж сразу закладываем броски и ПНР бесконечно-периодический...
наверное имеется ввиду, когда пройдет 49 с хвостиком суток от включения плк
Э-э-э... А почему 49 с хвостиком суток??? :eek:
Ну вот. А человек защиту на АЭС делал.
А почему
Дело в том, что TIME() возвращает не время, а кольцо всевла... времени. Тип TIME - 32 бита, и читаем про Arian
А кто-нибудь вообще проверял, как стандартные таймеры воспринимают заход на второй круг? Забавно будет узнать, что и там гарантий нет =)
var
tof1 : TOF := ( PT := T#5s);
end_var
tof1( in := что-нибуть );
output := not tof1.q;Не лукавьте, заведённый от триггера таймер тут же остановится.
Но вообще соглашусь. Занимаясь круглосуточными процессами, надо знать про 49 суток и обходить стандартные средства только с большой осторожностью.
Вот. Конструктив.
А как через 0 (??). У неактивных отсчет сброшен - это гарантия.
1.TON - ET как бы и не может перейти PT. (есть конечно некоторая неясность - но зна-а-ачительно реже она может быть)
2.TOF - а тоже самое.
3.TP - а ему зачем быть другим.
Вроде какие-то флажки (M) имеют - наверное проблема ими так и решена.
PS
Прерывание по времени и i:=i+1. На одном этом и строится любое кол-во таймеров.
следить за разностью
И 0 не страшен. + флажочек.
Ой, да ладно! Чё за ней следить?
http://images.devs-on.net/Image/sQhvL7NT2TwWi16B-CoDeSysUntitled.png
заведённый от триггера таймер тут же остановится.
Вам нужен отсчет от фронта ? - TP.
tp(in:=что-нибудь);
output:=not tp.q;
Ну нужно четкое задание - как часто могут быть импульсы (наложутся ли ?), какая длинна их может быть, может ли быть при старте (вкл.пит) 1 ? Все это надо учесть. И после этого - решать таймеры, time() или еще как.
бинго, главное самому попробовать не правда лиЭто-то я знал. Просто не так понял слова «следить за разностью». И облом со сравнением тут не отменяется.
облом со сравнением тут не отменяется.
Отменяется.
if что-нить then
tm:=time() + 5s;
end_if
output := TIME() >= timeout;
супротив
if что-нить then
tm:=time();
end_if
output:=(time()-tm)>5s;
+флажок
как плотник супротив столяра
Я не про это конкретно сравнение. Ну да ладно. Всё, что надо, прожевали. Удаляюсь.
Всем спасибо за участие! Буду пробовать дальше... :)
Ты это, аккуратней там с АЭС.. Вишь как бывает
Что бы два раза не вставать...
Я тут приложил свой первый проектик умнодомный... Что-то у меня там там в ФБ Prisut не хочет переменная назначаться... Очередная загадка...:confused:
Поможите, кто чем может. На всякие формулы и вычисления можно не обращать внимания - они работают как надо... :)
какой смысл вкладывали в эту фразу?
У Вас TOF_DOWN_D.Q вначале ИСТИНА, соответственно тело условия выполняется, а в нем (в теле) есть присвоение выходу некоторого значения и логичнее всего, это ноль, а далее в следующей строчке после блока, выход присваивается тому что подается на вход, так что идущие приращения до этой точки теряют смысл, тем более Вы из инта превращаете в байт, что ведет к загрублению и опять же в начале все будет равно нулю
Утром глянул на написанное - действительно фигня какая-то... :mad:
Да и TOF здесь не подойдет... Попробую фомализовать задачу.
Добавил в ФБ Prisut входную переменную pp - сигнал с датчика движения..
IF NOT pp THEN (*пропало движение*)
"начинаем медленно уменьшать яркость (OUT1) до нуля";
ELSIF pp AND OUT1 > 0 THEN (*пропало движение, но кто-то обратно вошел или пошевелился*)
OUT1 := light; (*возвращаем яркость как было*)
END_IF
Как сделать медленный декремент? :confused:
Вот моя глушилка резких движений для аналогового выхода:
VAR
MaxOutputDelta: REAL;
PrevScanTimestamp: TIME;
END_VAR
MaxOutputDelta := TIME_TO_REAL(TIME() - PrevScanTimestamp) * 0.016;
AO1 := AO1 + LIMIT(-MaxOutputDelta, Power - AO1, MaxOutputDelta);
PrevScanTimestamp := TIME();AO1 — аналоговый выход, Power — уставка, 0.016 — максимальное изменение в вольтах за миллисекунду.
Так. Оставим пока в покое медленный декремент, продолжим мозгоштурм.
Программа:
PROGRAM PLC_PRG
VAR
TT : INT := 100;
QQ : INT;
END_VAR
QQ := TT;
IF DI1 AND TT >0 THEN
TT := TT - 1;
ELSE
TT := QQ;
END_IF
В этом случае QQ будет равно тому, до чего успела уменьшится TT.
А как сохранить эту QQ ??? :confused:
VAR RETAIN
QQ: INT;
END_VAR
VAR RETAIN
QQ: INT;
END_VAR
Это немного не то. Я так понимаю, что VAR RETAIN - это энергонезависимая переменная. Но она все равно будет меняться с TT.
по этому коду переменная будет равна нулю, Вы ноль хотите сохранить?
Какая переменная? QQ ? Я же говорю, что она меняется так же, как ТТ... от 100 до 0..
Но, решение почти есть:
PROGRAM PLC_PRG
VAR
TT : INT := 100;
QQ : INT;
INIT_VAR : BOOL := 1;
END_VAR
IF INIT_VAR THEN
QQ := TT;
INIT_VAR := 0;
END_IF
IF DI1 AND TT >0 THEN
TT := TT - 1;
ELSE
TT := QQ;
END_IF
Осталось вписать его в мой проект... :)
Ну так поставьте задачу лучше. Что вы понимаете под сохранением в данном случае?
Но, решение почти естьВ вашем решении можно выкинуть INIT_VAR вместе с ифом и написать TT, QQ: INT := 100 ничего при этом не потеряв.
Ну так поставьте задачу лучше. Что вы понимаете под сохранением в данном случае?
Мне надо, что бы QQ была равна первоначальному значению ТТ, т.е. 100.
VAR CONSTANT
QQ: INT := 100;
END_VAR
это не верно, а косомсу со своим решением тоже самое, наверное неверно, по окончании когда ТТ станет равным нулю, QQ тоже будет равно нулю
Почему???
Вот вполне себе рабочий вариант:
PROGRAM PLC_PRG
VAR
TT : INT := 100;
QQ : INT;
INIT_VAR : BOOL := 1;
END_VAR
IF INIT_VAR THEN
QQ := TT;
INIT_VAR := 0;
END_IF
IF DI1 AND TT >0 THEN
TT := TT - 1;
ELSIF DI1 AND TT = 0 THEN
TT :=0;
ELSE
TT := QQ;
END_IF
Но, к сожалению, это только для лабораторных работ. Проблема в том, что если переменная TT берет значения откуда-то, то при первом же цикле, еще ДО присвоения данных ТТ "снаружи", QQ становится равно ТТ, и не меняется. А при старте ТТ = 0.
Думаю дальше... :)
ну раз начали уже задумываться о таких проблемах, вот Вам наводочка, не замечали никогда что некоторые функции написанные не Вами имеют в качестве входов не переменную, а указатель , как считаете для чего это? :)
А можно попросить показать пример использования? А я пока теорию почитаю... :)
Прочитал про указатели, но как их использовать для моей задачи так и не понял... Люди! Дайте пример... Ну пожалуйста...
пожалуйста пример
За фильм - "ОСКАР", однозначно!
Осталось понять, как мне это приспособить к моему проекту.. :)
ну раз в голову не приходят ни какие преимущества, значит еще рано осваивать указатели :)
Ну-с, приступим... Набросал пример из кина, только имна изменил. Цепляю к сообщению. Как работает, вроде бы понятно.
PROGRAM PLC_PRG
VAR
TTT : INT;
RRR : INT;
QQQ : INT;
END_VAR
TTT := 100;
RRR := TTT;
PT (ADR(TTT));
QQQ := TTT;
FUNCTION PT: INT
VAR_INPUT
INP :POINTER TO INT;
END_VAR
VAR
END_VAR
INP^:= INP^ +30 ;
Но тут одно НО. В самом начале PLC_PRG переменной ТТТ присваивается значение "100". Поэтому RRR тоже будет 100. Я же пытаюсь понять, как это сделать с динамической переменной. Т.е. в процессе она вычисляется, потом по событию она начинает меняться, и вот этот момент мне и надо зафиксировать. Чтобы при пропадании события ТТТ вернулась к запомненному значению.
Ну, т.е. без глобального цикла будет такое:
TTT := WWW;
IF DI1 THEN
TTT := TTT-10; (* Если DI1 то начинаем менять *)
ELSE
TTT := WWW; (* Если DI1 пропало - вернули как было *)
END_IF
Возникла еще одна проблема - начал разбираться, почему кнопки через раз работают, для чего подключил модуль статистики.
Кино - в приложении. Я так понимаю, что идет банальный перегруз процессора. :eek:
Выход я так понимаю в том, чтобы не использовать oscat... Это печаль...
На всякий случай приоржил сам проект...
lara197a
23.09.2012, 20:15
1. Используйте функцию SEL или функции выбора IF, Case....
2. Увеличьте мин. время цикла до 2-3 мс. На производстве S-400 работают с циклом 15-17мс, временами до 30мс. Если время критично выводите скоростные задачи на сопроцессор.
Скорее всего у вас в программе ошибки. Я в вашей программе сложных задач для сопроцессора не вижу.
1. Используйте функцию SEL или функции выбора IF, Case....
Да у меня все на IF...
2. Увеличьте мин. время цикла до 2-3 мс. На производстве S-400 работают с циклом 15-17мс, временами до 30мс. Если время критично выводите скоростные задачи на сопроцессор.
Стесняюсь спросить, а как вывести задачи на сопроцессор???
Скорее всего у вас в программе ошибки. Я в вашей программе сложных задач для сопроцессора не вижу.
Пока грубых не вижу... Попробую время цикла увеличить... Все время забваю, что это возможно...
lara197a
23.09.2012, 20:36
сопроцессор, это если к примеру на одном ПЛК или ПР выполнять небольшую, критичную ко времени задачу. А на другом соединенном с ним по сети крутить основную программу.
К примеру при работе с энкодером или управление резкой изделий. Контроллер может иметь время цикла, не обеспечивающее нужную точность позиционирования или размера.
сопроцессор, это если к примеру на одном ПЛК или ПР выполнять небольшую, критичную ко времени задачу. А на другом соединенном с ним по сети крутить основную программу.
К примеру при работе с энкодером или управление резкой изделий. Контроллер может иметь время цикла, не обеспечивающее нужную точность позиционирования или размера.
Ага.. Спасибо... Прям кластер получается... :)
я не делал пример под Вас, просто показал работу, что переменная может быть на "верху", т.е. видна всем, а какие то манипуляции с ней могут происходить в функциях, блоках, причем в нутри не надо объявлять ни какие новые переменные, что то им присваивать, делать ошибки, возвращать результат через другую переменную, а конкретно одна переменная отвечает за сохранение результата, если в одном блоке ей что то присвоили, то в других блоках с этой переменной уже будут считатся и по необходимости брать её значение, когда нужно
Да понятно, что не для меня... Просто начал разбираться - да, если пользовать функцию, все работает. А вот с функциональным блоком получается фигня... Т.е. совсем не получается. :( Мне нужны ФБ...
================= Программа ======================
PROGRAM PLC_PRG
VAR
TTT : INT;
RRR : INT;
QQQ : INT;
WWW : PT;
END_VAR
TTT := 100;
RRR := TTT;
IF DI1 THEN
WWW (INP := ADR(TTT));
QQQ := WWW.XXX;
ELSE
QQQ := RRR;
END_IF
================= ФБ ======================
FUNCTION_BLOCK PT
VAR_INPUT
INP :POINTER TO INT;
END_VAR
VAR_OUTPUT
XXX : INT;
END_VAR
VAR
SSS : INT;
I: INT := 0;
END_VAR
SSS := INP^ +30 ;
IF I < 9 THEN
SSS := SSS + 3;
I := I+1;
ELSE
XXX := SSS;
END_IF
Что-то голова пухнет с непривычки.. :)
Где-то я написал фигню, а где - не пойму...
Ладно, оставим указатели в покое. До поры.. :)
Итак, есть такая программка:
PROGRAM PLC_PRG
VAR
ARR : ARRAY[0..100] OF INT :=
0, 55, 60, 61, 62, 62, 63, 65, 66, 68, 70, 71, 74, 76,
79, 81, 84, 87, 91, 94, 98, 102, 106, 110, 115, 119, 124,
129, 134, 140, 146, 151, 157, 163, 170, 176, 183, 190, 197,
204, 212, 220, 228, 236, 244, 252, 261, 270, 279, 288, 298,
307, 317, 327, 337, 347, 358, 369, 380, 391, 402, 413, 425,
437, 449, 461, 474, 486, 499, 512, 526, 539, 552, 566, 580,
594, 609, 623, 638, 653, 668, 683, 699, 714, 730, 746, 763,
779, 796, 812, 830, 847, 864, 882, 899, 917, 936, 954, 972,
991, 1000;
i : INT;
TT : WORD;
END_VAR
IF DI1 THEN
IF i <= 100 THEN
AO7 := ARR[i];
i :=LIMIT(0, i+1, 100);
TT := TT+1;
END_IF
ELSE
AO7 := ARR[i];
END_IF
IF DI2 THEN
IF i > 0 THEN
AO7 := ARR[i];
i := i-1;
ELSIF i = 0 THEN
AO7 := 0;
END_IF
ELSE
AO7 := ARR[i];
END_IF
В режиме эмуляции работает медленно. На контроллере -мигом. Это понятно. Есть ли какой-нибудь способ сделать вот это
IF i <= 100 THEN
AO7 := ARR[i];
i :=LIMIT(0, i+1, 100);
TT := TT+1;
END_IF
медленно? Желательно получить регулируемый срок выполнения...
Там что, плавное затухание? Жуть какая. Всё укладывается в две строчки:
VAR
PrevScanTimestamp: TIME;
END_VAR
AO1 := LIMIT(0, AO1 + TIME_TO_REAL(TIME() - PrevScanTimestamp) * 0.008 * (BOOL_TO_REAL(DI1) - BOOL_TO_REAL(DI2)), 10);
PrevScanTimestamp := TIME();А если в конфигурацию добавить модуль Statistics и там сделать переменную CycleTime, то можно обойтись одной строкой:
AO1 := LIMIT(0, AO1 + CycleTime * 0.0016 * (BOOL_TO_REAL(DI1) - BOOL_TO_REAL(DI2)), 10); Скорость возрастания и затухания регулируется числовой константой. На входе только кнопки DI1/2, на выходе AO1.
Там что, плавное затухание? Жуть какая. Всё укладывается в две строчки
Ай, спасибо, Аболит! :)
Только не совсем понимаю, как это работает... :(
как это работаетЯ упрощу, чтобы вы поняли:
AO1 := AO1 + 0.001 * (BOOL_TO_REAL(DI1) - BOOL_TO_REAL(DI2));Выражение в скобках даёт −1, 0 или +1 в зависимости от того, какие кнопки нажаты. В общем-то, больше объяснять тут и нечего. В полном варианте вводится поправка на длительность цикла чтобы от неё не было зависимости.
Или вы даже не знаете, что в ПЛК ваша программа крутится в бесконечном цикле с периодичностью около 1-50 мс? Тормозить поток, как это делается в прикладном программировании, на контроллерах не принято. Ну а в симуляторе периодичность исполнения программы гораздо реже.
Я упрощу, чтобы вы поняли:
AO1 := AO1 + 0.001 * (BOOL_TO_REAL(DI1) - BOOL_TO_REAL(DI2));Выражение в скобках даёт −1, 0 или +1 в зависимости от того, какие кнопки нажаты. В общем-то, больше объяснять тут и нечего. В полном варианте вводится поправка на длительность цикла чтобы от неё не было зависимости.
Или вы даже не знаете, что в ПЛК ваша программа крутится в бесконечном цикле с периодичностью около 1-50 мс? Тормозить поток, как это делается в прикладном программировании, на контроллерах не принято. Ну а в симуляторе периодичность исполнения программы гораздо реже.
Про большой цикл я в курсе... Только пока не совсем освоил, как им пользоваться... Я до этого только всякие shell да perl пользовал по мелочи...
В этой ветке, малость выше, я пытался научиться просто сохранять значение переменной "в стороне". Пока неудачно... :(
Я так понял, что конструкция "TIME_TO_REAL(TIME() - PrevScanTimestamp)" просто вычилсяет время цикла?
Я так понял, что конструкция "TIME_TO_REAL(TIME() - PrevScanTimestamp)" просто вычилсяет время цикла?Да, в миллисекундах.
Итак, очередной отвал башки...
Вот такая программка работает:
================================================== ====
PROGRAM PLC_PRG
VAR
i : INT;
ARR : ARRAY[0..10] OF INT := 0,100,200,300,400,500,600,700,800,900,1000;
PrevScanTimestamp: TIME;
Speed_Reg : REAL := 0.08;
Limit_Reg : INT := 10;
btn_up: BOOL;
btn_dn: BOOL;
END_VAR
btn_up := DI1;
btn_dn := DI2;
IF i <= Limit_Reg THEN
i := LIMIT(0, i + REAL_TO_INT(TIME_TO_REAL(TIME() - PrevScanTimestamp) * Speed_Reg * (BOOL_TO_REAL(btn_up) - BOOL_TO_REAL(btn_dn))), Limit_Reg);
PrevScanTimestamp := TIME();
AO7 := ARR[i];
ELSIF (i=0 AND btn_dn) THEN
AO7 := ARR[0];
ELSE
AO7 := ARR[i];
END_IF
================================================== =
Т.е. при нажатии DI1, АО7 медленно растет до 1000.
А вот такая - уже не работает:
================================================== =
PROGRAM PLC_PRG
VAR
ARR : ARRAY[0..10] OF INT := 0,100,200,300,400,500,600,700,800,900,1000;
TT : INT;
END_VAR
TT := dim(btn_up:=DI1,btn_dn:=DI2,Speed_Reg:=0.08,Limit_ Reg:=100);
AO7 := ARR[TT];
FUNCTION DIM : INT
VAR_INPUT
btn_up: BOOL;
btn_dn: BOOL;
Speed_Reg : REAL;
Limit_Reg : INT;
END_VAR
VAR
i : INT;
PrevScanTimestamp: TIME;
END_VAR
IF i <= Limit_Reg THEN
i := LIMIT(0, i + REAL_TO_INT(TIME_TO_REAL(TIME() - PrevScanTimestamp) * Speed_Reg * (BOOL_TO_REAL(btn_up) - BOOL_TO_REAL(btn_dn))), Limit_Reg);
PrevScanTimestamp := TIME();
dim := i;
ELSIF (i=0 AND btn_dn) THEN
dim := 0;
ELSE
dim := i;
END_IF
================================================== =
Т.е. все, что я сделал - это вынес регулировку в функцию...
Подозреваю, что все дело опять в злосчастном "глобальном цикле" - т.е. при нажатии DI1 функция отрабатывает, только почему-то мгновенно - без плавного возрастания(почему?), а поскольку у ф-ции нет мозгов, то бишь памяти, то при отпускании кнопки, все пропадает...
З.Ы. Ради интереса попробовал вместо ф-ции использовать ФБ - работает как надо... =\
:) указатели спасут мир
Мир - может быть.. Но не меня... :(
Я в функцию передаю булевые значени, а получаю INT.
Попробовал сделать тип ф-ции как POINTER TO INT, теперь не понимаю, как получить значение... =\
===================================
PROGRAM PLC_PRG
VAR
TT : POINTER TO INT;
RR : INT;
END_VAR
TT := QQ(INPUT:=DI1);
RR := ??? что должно быть тут - непонятно пока... :(
===================================
FUNCTION QQ : POINTER TO INT
VAR_INPUT
INPUT : BOOL;
END_VAR
VAR
II : INT;
END_VAR
IF INPUT THEN
QQ^ := 125;
END_IF
===================================
Все что надо сохранять в функции делайте указателями или глобальными переменными
Глобальные переменные в ф-циях вроде как моветон, хотя и допускаются.. Или я не прав?
Я не пойму, к чему вот это и подобное надо городить:
================================================== ====
PROGRAM PLC_PRG
VAR
i : INT;
ARR : ARRAY[0..10] OF INT := 0,100,200,300,400,500,600,700,800,900,1000;
PrevScanTimestamp: TIME;
Speed_Reg : REAL := 0.08;
Limit_Reg : INT := 10;
btn_up: BOOL;
btn_dn: BOOL;
END_VAR
btn_up := DI1;
btn_dn := DI2;
IF i <= Limit_Reg THEN
i := LIMIT(0, i + REAL_TO_INT(TIME_TO_REAL(TIME() - PrevScanTimestamp) * Speed_Reg * (BOOL_TO_REAL(btn_up) - BOOL_TO_REAL(btn_dn))), Limit_Reg);
PrevScanTimestamp := TIME();
AO7 := ARR[i];
ELSIF (i=0 AND btn_dn) THEN
AO7 := ARR[0];
ELSE
AO7 := ARR[i];
END_IF
================================================== =Зачем там этот массив нужен, ей богу? Почему нельзя так?
PROGRAM PLC_PRG
VAR
PrevScanTimestamp: TIME;
Speed_Reg : REAL := 0.08;
Limit_Reg : INT := 10;
END_VAR
AO7 := LIMIT(0, AO7 + REAL_TO_INT(TIME_TO_REAL(TIME() - PrevScanTimestamp) * Speed_Reg * (BOOL_TO_REAL(DI1) - BOOL_TO_REAL(DI2))), Limit_Reg);
PrevScanTimestamp := TIME();М?
весь прикол что если по моему, то в функцию надо передать указатель на инт, а про выход функции вообще забыть, пусть там будет буль
Все. Мысль остановилась. Это как???
Я просто хотел этот кусок
i := LIMIT(0, i + REAL_TO_INT(TIME_TO_REAL(TIME() - PrevScanTimestamp) * Speed_Reg * (BOOL_TO_REAL(btn_up) - BOOL_TO_REAL(btn_dn))), Limit_Reg);
вынести в функицию. Потому, что он мне нужен будет с разными значениями Speed_Reg, Limit_Reg, btn_up, btn_dn. Выход-то все равно один - i, нужный для получения индекса массива.
И вот это самое i, я хочу периодически сохранять, для чего и затеял функцию.. С ФБ пока не научился...
Я не пойму, к чему вот это и подобное надо городить:Зачем там этот массив нужен, ей богу? Почему нельзя так?
Это я для примера сюда впихнул кусок...
Весь цимес - в массиве. Он на самом деле большой и нелинейный -там забиты значения квадратичной ф-ции.. А сделано это для облегчения жизни контроллера - этот массив один раз посчитал, и все. Зачем его каждый раз пересчитывать?
можно же не только входные указатели объявлять но и результирующие
Вот я и спросил, как получить значение по указателю...
PROGRAM PLC_PRG
VAR
TT : POINTER TO INT;
RR : INT;
END_VAR
TT := QQ(INPUT:=DI1); (*QQ возвращает POINTER TO INT*)
RR := ??? что должно быть тут - непонятно пока... :(
Весь цимес - в массиве. Он на самом деле большой и нелинейный -там забиты значения квадратичной ф-ции.. А сделано это для облегчения жизни контроллера - этот массив один раз посчитал, и все. Зачем его каждый раз пересчитывать?Не та сложность вычислений, чтобы прибегать к таблицам поиска. По-моему, здесь лучше на лету считать.
По-моему, здесь лучше на лету считать.
Други! А бывает такое, что в эмуляторе работает, а в контроллере - нет?
Вот с приложенным проектом такая бодяга случилась... А почему - не пойму...
Вместо всего этого...
IF i <= Limit_Reg THEN
i := LIMIT(0, i + REAL_TO_INT(TIME_TO_REAL(TIME() - PrevScanTimestamp) * Speed_Reg * (BOOL_TO_REAL(btn_up) - BOOL_TO_REAL(btn_dn))), Limit_Reg);
AO7 := ARR[i];
ELSIF (i=0 AND btn_dn) THEN
AO7 := ARR[0];
ELSE
AO7 := ARR[i];
END_IF
PrevScanTimestamp := TIME();Надо писать так:
i := LIMIT(0, i + REAL_TO_INT(TIME_TO_REAL(TIME() - PrevScanTimestamp) * Speed_Reg * (BOOL_TO_REAL(btn_up) - BOOL_TO_REAL(btn_dn))), Limit_Reg);
AO7 := ARR[i];
PrevScanTimestamp := TIME();Остальной код ничего не делает. Если вы не нажимаете ни одну кнопку, второе слагаемое будет нулём и i будет постоянным. Когда вы упрётесь в Limit_reg или в нуль, i всё равно не выпрыгнет за пределы, т.к. там функция LIMIT. Попробуйте исправленный вариант и сообщите результаты.
Попробуйте исправленный вариант и сообщите результаты.
Попробовал, сообщаю - не работает.. :(
ЗЫ. F5 жму со страшной силой...
не хотите уточнить, какой способ Вы сейчас используете, реакция быстрых входов несколько отличается от обычных, если сейчас используется энкодер, то причина нерабоающей программы возможно в этом
Большое спасибо за пример с таймингами... Только я не понял, зачем
isnan: ARRAY [0..3] OF BYTE:=0,0,128,127;
- вроде нигде не фигурирует в текстах...
В данный момент прикрутил следующие костыли:
(* Плавное включение полного света *)
IF Full THEN
IF Count_R < 100 THEN
RRR := RRR + 0.2;
Count_R := REAL_TO_INT(RRR);
ELSE
Full := FALSE;
RRR := 0;
END_IF
END_IF
Как поведет себя эта конструкция, если контроллер будет нагружен - вопрос. Сейчас, на одной лампе - работает достаточно плавно... :)
Что касается неработающего примера - то там энкодер не использую - просто кнопки. И энкодер прикручен к обычным входах, не быстрым...
Да, насколько я понял, в тайминге скорость нарастания\ падения регулируется tik(IN:=NOT tik.Q, PT:=T#50ms); Так?
переменная осталась как бонус, она нужна для реалов и то не во всех случаях, тем более в проге их нет.
По поводу таймера, можно еще связать его период с длительностью удержания кнопок, но это должна быть другая прога без массива
Может в проге и нет, но без нее не работает... Мистика... Требую пояснений! :)
всё можно проще
За алгоритм спасибо, но это малость не то, что мне надо... :) В общем, оставил пока так...
Раз уж пошла такая пьянка, подскажите, есть ли более простой способ узнать длительность нажатия кнопки, кроме как ловить передний и задний фронты?
Сейчас у меня это так:
VAR
Btn : BOOL;
Fr: R_TRIG;
Bk: F_TRIG;
In: TIME;
Out:TIME;
Long : TIME;
END_VAR
Btn := DI1;
Fr(CLK:=Btn); (* Передний фронт *)
Bk(CLK:=Btn); (* Задний фронт *)
IF Fr.Q THEN
In := TIME();
END_IF
IF Bk.Q THEN
Out := TIME();
END_IF
Long := Out - In;
Как-то не изящно смотрится.. :)
Угумс:
VAR
stopwatch: TON;
result: TIME;
btn: BOOL;
END_VAR
IF NOT btn AND stopwatch.IN THEN
result := stopwatch.ET;
END_IF
stopwatch(IN := btn, PT := DWORD_TO_TIME(NOT 0));Ну или вариант для тех, кого в детстве пугали таймерами:
VAR
timestamp, result: TIME;
btn: BOOL;
END_VAR
IF btn AND timestamp = T#0s THEN
timestamp := TIME();
ELSIF NOT btn AND timestamp <> T#0s THEN
result := TIME() - timestamp;
timestamp := T#0s;
END_IF
Очередной дурацкий вопрос - как "мигнуть" светом? Мигнуть - это не выключить-включить, а притушить на 30% и вернуть обратно... Или при малой яркости прибавить 30% и вернуть.. Проблема - в скорости цикла. Т.е. если в одном цикле я уменьшаю яркость, а в следующем увеличиваю - лампа не успевает отработать. Пропускать некоторое количество циклов счетчиком - не самый удачный вариант, но другие пока не лезут в голову... А как два раза мигнуть?
Я себе для «морзянки» лампочками и зумерами вот чо написал:
FUNCTION_BLOCK BLINK_SEQ (* Custom sequence blinker *)
VAR_INPUT
SEQ: ARRAY [0..15] OF TIME; (* The sequence to blink upon (even are ON times, odd are OFF times) *)
ENABLED: BOOL; (* Rising edge starts blinking *)
END_VAR
VAR_OUTPUT
OUT: BOOL;
END_VAR
VAR
i: INT;
starter: R_TRIG;
sequencer: TON;
END_VAR
starter(CLK := ENABLED);
IF starter.Q THEN
i := 0;
END_IF
IF ENABLED AND i < 15 THEN
sequencer(IN := TRUE, PT := SEQ[i]);
IF sequencer.Q THEN
i := i + 1;
WHILE SEQ[i] = T#0s AND i < 15 DO i := i + 1; END_WHILE
sequencer(IN := FALSE);
END_IF
OUT := (i MOD 2 = 0) AND ENABLED AND SEQ[MAX(0, i - 1)] > T#0s;
ELSE
OUT := FALSE;
END_IF
Я себе для «морзянки» лампочками и зумерами вот чо написал:
Круто, конечно, но малость не то. Эта штука, насколько я понял, именно моргает. А мне надо "притушить" и обратно вернуть... :)
Генератор из util.lib вроде как больше подходит, но там не задать начальные значения, и как остановить его не понял...
Ну, т.е.
VAR
Light : INT; (*яркость в %*)
Light_temp : INT; (*яркость в % для запоминания*)
OUT : INT; (*это то, что передается в лампочку*)
EN : BOOL; (* Включалка *)
END_VAR
================================================== ========
Light_temp := Light;
IF EN THEN
IF Light < 70 THEN
Light := Light + 30;
Out := Light;
ELSIF Light >= 70 THEN
Light := Light - 30;
Out := Light;
END_IF
OUT := Light_temp;
================================================== ========
Но, поскольку это происходит в пределах одного цикла, то ничего не происходит. А как это растянуть во времени - не понимаю... :(
Эта штука, насколько я понял, именно моргает. А мне надо "притушить" и обратно вернуть...Из «моргать» сделать «тушить» можно одной строчкой. А в простейшем случае — одним оператором (SEL).
Из «моргать» сделать «тушить» можно одной строчкой. А в простейшем случае — одним оператором (SEL).
Что-то я как тот доцент... Недопонял... Нельзя ли поподробнее???
output := power - power * 0.3 * BOOL_TO_REAL(dim); (* -30% когда dim = TRUE *)С выше приведённым BLINK_SEQ можно так:
PROGRAM PLC_PRG
VAR
blinker: BLINK_SEQ;
power, output: REAL;
END_VAR
VAR CONSTANT
(* Два коротких, один длинный *)
testSequence: ARRAY[0..15] OF TIME := T#200ms, T#200ms, T#200ms, T#200ms, T#500ms;
END_VAR
blinker(ENABLED := TIME_TO_DWORD(TIME()) MOD 3000 > 100, SEQ := testSequence); (* Раз в три секунды *)
output := power - power * 0.3 * BOOL_TO_REAL(blinker.OUT); (* Мигание в тушение на 30% *)
output := power - power * 0.3 * BOOL_TO_REAL(dim); (* -30% когда dim = TRUE *)С выше приведённым BLINK_SEQ можно так:
PROGRAM PLC_PRG
VAR
blinker: BLINK_SEQ;
power, output: REAL;
END_VAR
VAR CONSTANT
(* Два коротких, один длинный *)
testSequence: ARRAY[0..15] OF TIME := T#200ms, T#200ms, T#200ms, T#200ms, T#500ms;
END_VAR
blinker(ENABLED := TIME_TO_DWORD(TIME()) MOD 3000 > 100, SEQ := testSequence); (* Раз в три секунды *)
output := power - power * 0.3 * BOOL_TO_REAL(blinker.OUT); (* Мигание в тушение на 30% *)
Огромное спасибо! Буду пробовать...
Если б хоть можно было писать timer(IN := ..., NOT Q => output) — ан нет, хрен тебеЯ знал! Я знал!
14547
В Стандарте есть эта фишка! Кодесис в очередной раз подтверждает своё весьма условное соответствие. Причём третья версия тоже это не поддерживает.
а где написано про инверсию выхода?Я выдержку из стандарта рисунком привёл. Не видно разве? Там формальное описание грамматики языка ST, согласно которому перед именем выхода программист может сказать NOT.
Хм и что с того, я например видел в описалове стандарта и такую записьНе нашёл ничего подобного в самом стандарте. Вновь обратимся к разделу с грамматикой:
B.1.2.3.2 Time of day and datePRODUCTION RULES:
time_of_day ::= ('TIME_OF_DAY' | 'TOD') '#' daytime
daytime ::= day_hour ':' day_minute ':' day_secondПробуем TOD#20:12 — работает. Так что в данном случае всё соответствует.
Да что вы возитесь с какими-то библиотеками, таймерами? Для отработки любых задержек - можно и без библиотек обходиться, есть стандартная функция TIME(), возвращает время в миллисекундах (с момента включения ПЛК), и этого достаточно. Если нужно отсчитать какую-то задержку с определённого момента - в требуемый момент присваиваете своей переменной текущее значение TIME(), а в последующих циклах проверяете условие: если разность текущего TIME() и той переменной превысила требуемую величину, то задержка закончилась.
А с чего тогда взяли, что цитируете стандарт?http://i.imgur.com/jUG7VHv.png
Где Вам приходилось использовать присвоение ::=Это не присвоение. Это знак определения в форме Бэкуса-Наура (https://ru.wikipedia.org/wiki/Форма_Бэкуса_—_Наура). И процитированный кусок — не ST-код, а выдержка из формального описания синтаксиса языка ST вообще.
я разве про tod#xx:xx выложил код, t# - работает?Я про то, что по стандарту t#20:12 быть не может в отличие от NOT перед обратным присвоением. Короче, то, что не поддерживается t#20:12 — правильно, а что не поддерживается NOT Q => boolVar — неправильно.
Powered by vBulletin® Version 4.2.3 Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot