PDA

Просмотр полной версии : Помогите разобраться...



kosmas
16.09.2012, 21:42
Люди! Не дайте мозгу взорваться...
Написал небольшую тестовую программку. Объясните, пожалуйста, почему !YES работает, а !NO не работает? Разница только в том, что есть IF...
Скриншоты и файлы прикладываю...

kosmas
16.09.2012, 22:40
да легче простого, ВЫ поставили импульсный тригер и выставили условие по его выходу, а если внутрь условия программа не забирается, то и выполнять в теле условия ни чего не будет, так что таймер внутри ИФа стоит без дела
Дак в том то и суть, что внутрь IF забирается. Проверял тестовой переменной. Просто TOF как бы "подвисает" о включенном состоянии- т.е. CLK и Q остаются в состоянии TRUE. А почему такое происходит - загадка...

Валенок
16.09.2012, 22:54
1.Зачем вызывают ФБ ?
2.Чем таймеры отличается от ФБ ?
3.Что такое TOF ?
4.А когда и с какими параметрами он вызывается у Вас ?
Ну и делаем выводы

Yegor
17.09.2012, 06:18
Просто TOF как бы "подвисает" о включенном состоянии- т.е. CLK и Q остаются в состоянии TRUE. А почему такое происходит - загадка.Q внутри условия хоть как будет TRUE потому что само условие у вас как раз по переменной Q. А ещё мы не знаем, как там у вас CLK изменяется.
Что такое TOF ?TON вверх ногами.

kosmas
17.09.2012, 06:35
Q внутри условия хоть как будет TRUE потому что само условие у вас как раз по переменной Q. А ещё мы не знаем, как там у вас CLK изменяется.TON вверх ногами.

CLK меняется просто - это нажатие кнопочки. Я все равно не понимаю.... В моем понимании, вся программа PLC_PRG вместе с ФБ и прочим крутятся в цикле. Т. е. я нажал кнопку, Q включилось и сразу же выключилось - это же выход детектора переднего фронта, и начал работать отсчет времени TOF. Ведь в !YES же все так и работает...:confused:

Честное слово, голова уже не работает... Написал достаточно большой кусок своей первой программы, понадобился таймер, и на тебе...:(

Yegor
17.09.2012, 07:15
Q включилось и сразу же выключилось - это же выход детектора переднего фронта, и начал работать отсчет времени TOFНет. Как только Q перешёл в FALSE, таймер останавливается потому что вы его больше не вызываете — не заходите в условие. Вообще таймер под триггером это... как-то нетрадиционно что ли.

kosmas
17.09.2012, 08:45
Нет. Как только Q перешёл в FALSE, таймер останавливается потому что вы его больше не вызываете — не заходите в условие. Вообще таймер под триггером это... как-то нетрадиционно что ли.

Черд... Пока ехал до работы меня это таки осенило... :)

Теперь осталось понять, как правильно вызывать таймер по IF... :confused:

kosmas
17.09.2012, 08:51
вообще Вы написали два куска, Вас чем правильный то вариант неустраивает?

!YES - это то, на чем я тренируюсь. Беру кусок кода и проверяю, как он работает.. А !NO - это то, что мне надо.
!NO на самом деле - это кусок длинного IF - ELSIF... В одном месте мне надо в течении 3-х секунд передавать TRUE одному ФБ... TOF мне показался наиболее подходящим... А как еще эту задачу можно решить?
WHILE по времени вообще пользоваться нельзя, как я понял - сторожевой таймер будет ругаться...

kosmas
17.09.2012, 10:22
за блоком условий прописываете строчку таймера, а в условиях присваиваете входам таймера соответствующие значения, в этом случае у Вас все будет получатся.


Так?


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-х секунд не доходит.. :)

kosmas
17.09.2012, 10:33
попробуйте так
ЗЫ теперь Вы сбрасываете вход, подачей на него одиночного импульса и соответственно таймер снова не работает

Спасибо огромное. Так работает... :rolleyes:

kosmas
17.09.2012, 12:25
И еще один дурацкий вопрос: Где более правильно обзывать входы ПЛК? В глобальных переменных, или как я - в конфигураторе ПКЛ?

Я так понимаю, что если в глобальных переменных, то получится более "универсальный" код, однако при замене платформы адреса все равно съедут... Или я неправ?

Или может есть какой способ "перетаскивания" из конфигуратора ПЛК в GLOBAL_VARS ? Типа обозвал входы-выходы, написал комментарии - а оно бабах, и красота... :)

И что бы два раза не вставать - как вообще меняется платформа? Вот написал я под ПЛК110-60, а потом решил использовать этот же проект на ПЛК-100... В настройках целевой платформы я поменял платформу, а дальше?

aserr
17.09.2012, 12:37
Объявлять переменные можно где удобней. Особой разницы нет. Если вы меняете платформу, то в любом случае необходимо ручками переписывать переменные, хоть объявленные в конфигураторе, хоть по прямым адресам.

Yegor
17.09.2012, 13:04
Теперь осталось понять, как правильно вызывать таймер по IF...С условием по выходу триггера проще делать без таймера:
VAR
timeout: TIME;
output: BOOL;
END_VAR

IF что-нибудь THEN
timeout := TIME() + T#5s;
END_IF

output := TIME() >= timeout;

kosmas
17.09.2012, 22:20
С условием по выходу триггера проще делать без таймера:
VAR
timeout: TIME;
output: BOOL;
END_VAR

IF что-нибудь THEN
timeout := TIME() + T#5s;
END_IF

output := TIME() >= timeout;

Сейчас кодесиса под рукой нет... Я правильно понимаю эту конструкцию, что
"Если 'что-нибуть' TRUE, то через 5 сек. output будет TRUE"... Так?

Валенок
17.09.2012, 22:43
А у нас TIME() меньше 0 при старте ?
То что изображено - это NOT TOF.Q

Yegor
18.09.2012, 06:08
А у нас TIME() меньше 0 при старте ?К первому циклу уже больше, если не ошибаюсь.
"Если 'что-нибуть' TRUE, то через 5 сек. output будет TRUE"... Так?В данном случае немного иначе: «если „что-нибудь“ TRUE, то на следующие 5 секунд output сделать FALSE, в остальное время держать TRUE».

Таймер в таких ситуациях задалбывает требованием его вызывать. Если б хоть можно было писать timer(IN := ..., NOT Q => output) — ан нет, хрен тебе (при том, что в диаграммах отрицание ставится на любой вход или выход). В итоге лишние строки. Конечно, когда условие включения таймера держится, то лучше использовать нормальные TON, TOF и т.п.

Валенок
18.09.2012, 15:37
VAR
timeout: TIME;
output: BOOL;
END_VAR

IF что-нибудь THEN
timeout := TIME() + T#5s;
END_IF

output := TIME() >= timeout;
А ничего, что при постоянном что-нибудь=TRUE output будет иногда подниматься ?

Yegor
18.09.2012, 15:40
При «постоянном что-нибудь=TRUE» output будет постоянно FALSE. Ну и вообще я не просто так ограничил применимость этого решения:
С условием по выходу триггера

Валенок
18.09.2012, 15:46
Авторы Arian-5 тоже так предполагали :)

Т.е. этот код зависит не только от "что-нибудь" ?


В 1996 году отличились французы. Из-за ошибок в программном обеспечении 4 июня был прерван полет космической ракеты Ariane 5. Убытки в результате составили более 500 миллионов долларов. А причина крылась в том, что по недосмотру переменная, которая описывала горизонтальную скорость ракеты, была представлена целым 16-битным числом. В результате, как только эта значение переменной превысило 32 768 (2 в 15-й степени), система управления ракетой, что называется, “подвисла”, а “сошедшую с ума” ракету пришлось уничтожить.


уже в 90-х годах. На этот раз пострадали бравые американские военные ракетчики, принимавшие участие в операции “Буря в пустыне”. К их удивлению, ракеты “Пэтриот”, использовавшиеся для перехвата в воздухе иракских ракет, периодически проходили мимо цели. Разбирательства начались после того, как пропущенная иракская ракета привела к гибели 28 американских солдат. Первоначально зародилось подозрение, что часть “Пэтриотов” технически неисправны. Однако созданная по этому поводу комиссия с удивлением обнаружила, что все обследованные ракеты абсолютно работоспособны. Вы уже, наверное, догадались, где пряталась ошибка? Конечно же, в программном обеспечении, использовавшимся американским зенитно-ракетным комплексом. Выяснилось, что система создавалась в расчете на то, что время ее непрерывной работы не будет превышать 14 часов, однако на практике комплексы непрерывно работали по 100 и более часов.Вроде бы пустяк, но оказалось, что используемое для определения времени программное обеспечение накапливало ошибки. За 100 часов работы набегала разница в 0,34 секунды. Программисты, оказывается, знали об этом, да посчитали факт несущественным...

Валенок
18.09.2012, 15:56
При «постоянном что-нибудь=TRUE» output будет постоянно FALSE. Ну и вообще я не просто так ограничил применимость этого решения ..c условием по выходу триггера
Да хоть и триггером - те же проблемы. И тогда же :

ввиду, когда пройдет 49 с хвостиком суток от включения плк


Бывает такая нездоровая фигня как стабильное питание и безаварийная работа. Ну мы ж сразу закладываем броски и ПНР бесконечно-периодический...

kosmas
18.09.2012, 16:04
наверное имеется ввиду, когда пройдет 49 с хвостиком суток от включения плк

Э-э-э... А почему 49 с хвостиком суток??? :eek:

Валенок
18.09.2012, 16:09
Ну вот. А человек защиту на АЭС делал.



А почему
Дело в том, что TIME() возвращает не время, а кольцо всевла... времени. Тип TIME - 32 бита, и читаем про Arian

Yegor
18.09.2012, 16:52
А кто-нибудь вообще проверял, как стандартные таймеры воспринимают заход на второй круг? Забавно будет узнать, что и там гарантий нет =)
var
tof1 : TOF := ( PT := T#5s);
end_var

tof1( in := что-нибуть );
output := not tof1.q;Не лукавьте, заведённый от триггера таймер тут же остановится.

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

Валенок
18.09.2012, 17:06
Вот. Конструктив.
А как через 0 (??). У неактивных отсчет сброшен - это гарантия.
1.TON - ET как бы и не может перейти PT. (есть конечно некоторая неясность - но зна-а-ачительно реже она может быть)
2.TOF - а тоже самое.
3.TP - а ему зачем быть другим.
Вроде какие-то флажки (M) имеют - наверное проблема ими так и решена.

PS
Прерывание по времени и i:=i+1. На одном этом и строится любое кол-во таймеров.

Валенок
18.09.2012, 17:20
следить за разностью
И 0 не страшен. + флажочек.

Yegor
18.09.2012, 17:34
Ой, да ладно! Чё за ней следить?

http://images.devs-on.net/Image/sQhvL7NT2TwWi16B-CoDeSysUntitled.png

Валенок
18.09.2012, 17:44
заведённый от триггера таймер тут же остановится.
Вам нужен отсчет от фронта ? - TP.
tp(in:=что-нибудь);
output:=not tp.q;

Ну нужно четкое задание - как часто могут быть импульсы (наложутся ли ?), какая длинна их может быть, может ли быть при старте (вкл.пит) 1 ? Все это надо учесть. И после этого - решать таймеры, time() или еще как.

Yegor
18.09.2012, 17:45
бинго, главное самому попробовать не правда лиЭто-то я знал. Просто не так понял слова «следить за разностью». И облом со сравнением тут не отменяется.

Валенок
18.09.2012, 17:47
облом со сравнением тут не отменяется.
Отменяется.


if что-нить then
tm:=time() + 5s;
end_if
output := TIME() >= timeout;
супротив


if что-нить then
tm:=time();
end_if
output:=(time()-tm)>5s;
+флажок
как плотник супротив столяра

Yegor
18.09.2012, 17:52
Я не про это конкретно сравнение. Ну да ладно. Всё, что надо, прожевали. Удаляюсь.

kosmas
18.09.2012, 18:05
Всем спасибо за участие! Буду пробовать дальше... :)

Валенок
18.09.2012, 18:07
Ты это, аккуратней там с АЭС.. Вишь как бывает

kosmas
19.09.2012, 22:16
Что бы два раза не вставать...

Я тут приложил свой первый проектик умнодомный... Что-то у меня там там в ФБ Prisut не хочет переменная назначаться... Очередная загадка...:confused:

Поможите, кто чем может. На всякие формулы и вычисления можно не обращать внимания - они работают как надо... :)

kosmas
20.09.2012, 08:53
какой смысл вкладывали в эту фразу?
У Вас TOF_DOWN_D.Q вначале ИСТИНА, соответственно тело условия выполняется, а в нем (в теле) есть присвоение выходу некоторого значения и логичнее всего, это ноль, а далее в следующей строчке после блока, выход присваивается тому что подается на вход, так что идущие приращения до этой точки теряют смысл, тем более Вы из инта превращаете в байт, что ведет к загрублению и опять же в начале все будет равно нулю

Утром глянул на написанное - действительно фигня какая-то... :mad:
Да и TOF здесь не подойдет... Попробую фомализовать задачу.

Добавил в ФБ Prisut входную переменную pp - сигнал с датчика движения..
IF NOT pp THEN (*пропало движение*)
"начинаем медленно уменьшать яркость (OUT1) до нуля";
ELSIF pp AND OUT1 > 0 THEN (*пропало движение, но кто-то обратно вошел или пошевелился*)
OUT1 := light; (*возвращаем яркость как было*)
END_IF

Как сделать медленный декремент? :confused:

Yegor
20.09.2012, 09:34
Вот моя глушилка резких движений для аналогового выхода:
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 — максимальное изменение в вольтах за миллисекунду.

kosmas
20.09.2012, 12:13
Так. Оставим пока в покое медленный декремент, продолжим мозгоштурм.

Программа:


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:

Yegor
20.09.2012, 12:22
VAR RETAIN
QQ: INT;
END_VAR

kosmas
20.09.2012, 12:37
VAR RETAIN
QQ: INT;
END_VAR

Это немного не то. Я так понимаю, что VAR RETAIN - это энергонезависимая переменная. Но она все равно будет меняться с TT.

kosmas
20.09.2012, 12:40
по этому коду переменная будет равна нулю, Вы ноль хотите сохранить?

Какая переменная? 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


Осталось вписать его в мой проект... :)

Yegor
20.09.2012, 12:41
Ну так поставьте задачу лучше. Что вы понимаете под сохранением в данном случае?
Но, решение почти естьВ вашем решении можно выкинуть INIT_VAR вместе с ифом и написать TT, QQ: INT := 100 ничего при этом не потеряв.

kosmas
20.09.2012, 12:44
Ну так поставьте задачу лучше. Что вы понимаете под сохранением в данном случае?

Мне надо, что бы QQ была равна первоначальному значению ТТ, т.е. 100.

Yegor
20.09.2012, 12:48
VAR CONSTANT
QQ: INT := 100;
END_VAR

kosmas
20.09.2012, 14:36
это не верно, а косомсу со своим решением тоже самое, наверное неверно, по окончании когда ТТ станет равным нулю, 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.

Думаю дальше... :)

kosmas
20.09.2012, 22:17
ну раз начали уже задумываться о таких проблемах, вот Вам наводочка, не замечали никогда что некоторые функции написанные не Вами имеют в качестве входов не переменную, а указатель , как считаете для чего это? :)

А можно попросить показать пример использования? А я пока теорию почитаю... :)

kosmas
22.09.2012, 22:24
Прочитал про указатели, но как их использовать для моей задачи так и не понял... Люди! Дайте пример... Ну пожалуйста...

kosmas
23.09.2012, 11:52
пожалуйста пример
За фильм - "ОСКАР", однозначно!
Осталось понять, как мне это приспособить к моему проекту.. :)

kosmas
23.09.2012, 16:32
ну раз в голову не приходят ни какие преимущества, значит еще рано осваивать указатели :)

Ну-с, приступим... Набросал пример из кина, только имна изменил. Цепляю к сообщению. Как работает, вроде бы понятно.



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

kosmas
23.09.2012, 17:48
Возникла еще одна проблема - начал разбираться, почему кнопки через раз работают, для чего подключил модуль статистики.
Кино - в приложении. Я так понимаю, что идет банальный перегруз процессора. :eek:

Выход я так понимаю в том, чтобы не использовать oscat... Это печаль...

На всякий случай приоржил сам проект...

lara197a
23.09.2012, 20:15
1. Используйте функцию SEL или функции выбора IF, Case....
2. Увеличьте мин. время цикла до 2-3 мс. На производстве S-400 работают с циклом 15-17мс, временами до 30мс. Если время критично выводите скоростные задачи на сопроцессор.
Скорее всего у вас в программе ошибки. Я в вашей программе сложных задач для сопроцессора не вижу.

kosmas
23.09.2012, 20:20
1. Используйте функцию SEL или функции выбора IF, Case....
Да у меня все на IF...



2. Увеличьте мин. время цикла до 2-3 мс. На производстве S-400 работают с циклом 15-17мс, временами до 30мс. Если время критично выводите скоростные задачи на сопроцессор.
Стесняюсь спросить, а как вывести задачи на сопроцессор???




Скорее всего у вас в программе ошибки. Я в вашей программе сложных задач для сопроцессора не вижу.
Пока грубых не вижу... Попробую время цикла увеличить... Все время забваю, что это возможно...

lara197a
23.09.2012, 20:36
сопроцессор, это если к примеру на одном ПЛК или ПР выполнять небольшую, критичную ко времени задачу. А на другом соединенном с ним по сети крутить основную программу.
К примеру при работе с энкодером или управление резкой изделий. Контроллер может иметь время цикла, не обеспечивающее нужную точность позиционирования или размера.

kosmas
23.09.2012, 20:38
сопроцессор, это если к примеру на одном ПЛК или ПР выполнять небольшую, критичную ко времени задачу. А на другом соединенном с ним по сети крутить основную программу.
К примеру при работе с энкодером или управление резкой изделий. Контроллер может иметь время цикла, не обеспечивающее нужную точность позиционирования или размера.

Ага.. Спасибо... Прям кластер получается... :)

kosmas
24.09.2012, 15:49
я не делал пример под Вас, просто показал работу, что переменная может быть на "верху", т.е. видна всем, а какие то манипуляции с ней могут происходить в функциях, блоках, причем в нутри не надо объявлять ни какие новые переменные, что то им присваивать, делать ошибки, возвращать результат через другую переменную, а конкретно одна переменная отвечает за сохранение результата, если в одном блоке ей что то присвоили, то в других блоках с этой переменной уже будут считатся и по необходимости брать её значение, когда нужно

Да понятно, что не для меня... Просто начал разбираться - да, если пользовать функцию, все работает. А вот с функциональным блоком получается фигня... Т.е. совсем не получается. :( Мне нужны ФБ...


================= Программа ======================
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


Что-то голова пухнет с непривычки.. :)
Где-то я написал фигню, а где - не пойму...

kosmas
24.09.2012, 17:52
Ладно, оставим указатели в покое. До поры.. :)

Итак, есть такая программка:


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

медленно? Желательно получить регулируемый срок выполнения...

Yegor
25.09.2012, 05:14
Там что, плавное затухание? Жуть какая. Всё укладывается в две строчки:
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.

kosmas
25.09.2012, 17:19
Там что, плавное затухание? Жуть какая. Всё укладывается в две строчки

Ай, спасибо, Аболит! :)

Только не совсем понимаю, как это работает... :(

Yegor
25.09.2012, 19:13
как это работаетЯ упрощу, чтобы вы поняли:
AO1 := AO1 + 0.001 * (BOOL_TO_REAL(DI1) - BOOL_TO_REAL(DI2));Выражение в скобках даёт −1, 0 или +1 в зависимости от того, какие кнопки нажаты. В общем-то, больше объяснять тут и нечего. В полном варианте вводится поправка на длительность цикла чтобы от неё не было зависимости.

Или вы даже не знаете, что в ПЛК ваша программа крутится в бесконечном цикле с периодичностью около 1-50 мс? Тормозить поток, как это делается в прикладном программировании, на контроллерах не принято. Ну а в симуляторе периодичность исполнения программы гораздо реже.

kosmas
25.09.2012, 22:24
Я упрощу, чтобы вы поняли:
AO1 := AO1 + 0.001 * (BOOL_TO_REAL(DI1) - BOOL_TO_REAL(DI2));Выражение в скобках даёт −1, 0 или +1 в зависимости от того, какие кнопки нажаты. В общем-то, больше объяснять тут и нечего. В полном варианте вводится поправка на длительность цикла чтобы от неё не было зависимости.

Или вы даже не знаете, что в ПЛК ваша программа крутится в бесконечном цикле с периодичностью около 1-50 мс? Тормозить поток, как это делается в прикладном программировании, на контроллерах не принято. Ну а в симуляторе периодичность исполнения программы гораздо реже.

Про большой цикл я в курсе... Только пока не совсем освоил, как им пользоваться... Я до этого только всякие shell да perl пользовал по мелочи...
В этой ветке, малость выше, я пытался научиться просто сохранять значение переменной "в стороне". Пока неудачно... :(

Я так понял, что конструкция "TIME_TO_REAL(TIME() - PrevScanTimestamp)" просто вычилсяет время цикла?

Yegor
26.09.2012, 05:07
Я так понял, что конструкция "TIME_TO_REAL(TIME() - PrevScanTimestamp)" просто вычилсяет время цикла?Да, в миллисекундах.

kosmas
26.09.2012, 08:57
Итак, очередной отвал башки...
Вот такая программка работает:

================================================== ====
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 функция отрабатывает, только почему-то мгновенно - без плавного возрастания(почему?), а поскольку у ф-ции нет мозгов, то бишь памяти, то при отпускании кнопки, все пропадает...


З.Ы. Ради интереса попробовал вместо ф-ции использовать ФБ - работает как надо... =\

kosmas
26.09.2012, 10:22
:) указатели спасут мир
Мир - может быть.. Но не меня... :(

Я в функцию передаю булевые значени, а получаю 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
===================================




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

Глобальные переменные в ф-циях вроде как моветон, хотя и допускаются.. Или я не прав?

Yegor
26.09.2012, 11:07
Я не пойму, к чему вот это и подобное надо городить:

================================================== ====
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();М?

kosmas
26.09.2012, 11:42
весь прикол что если по моему, то в функцию надо передать указатель на инт, а про выход функции вообще забыть, пусть там будет буль

Все. Мысль остановилась. Это как???

Я просто хотел этот кусок

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, я хочу периодически сохранять, для чего и затеял функцию.. С ФБ пока не научился...


Я не пойму, к чему вот это и подобное надо городить:Зачем там этот массив нужен, ей богу? Почему нельзя так?

Это я для примера сюда впихнул кусок...
Весь цимес - в массиве. Он на самом деле большой и нелинейный -там забиты значения квадратичной ф-ции.. А сделано это для облегчения жизни контроллера - этот массив один раз посчитал, и все. Зачем его каждый раз пересчитывать?

kosmas
26.09.2012, 11:52
можно же не только входные указатели объявлять но и результирующие

Вот я и спросил, как получить значение по указателю...


PROGRAM PLC_PRG
VAR
TT : POINTER TO INT;
RR : INT;
END_VAR

TT := QQ(INPUT:=DI1); (*QQ возвращает POINTER TO INT*)

RR := ??? что должно быть тут - непонятно пока... :(

Yegor
26.09.2012, 12:42
Весь цимес - в массиве. Он на самом деле большой и нелинейный -там забиты значения квадратичной ф-ции.. А сделано это для облегчения жизни контроллера - этот массив один раз посчитал, и все. Зачем его каждый раз пересчитывать?Не та сложность вычислений, чтобы прибегать к таблицам поиска. По-моему, здесь лучше на лету считать.

kosmas
28.09.2012, 15:36
По-моему, здесь лучше на лету считать.

Други! А бывает такое, что в эмуляторе работает, а в контроллере - нет?
Вот с приложенным проектом такая бодяга случилась... А почему - не пойму...

Yegor
28.09.2012, 16:29
Вместо всего этого...
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. Попробуйте исправленный вариант и сообщите результаты.

kosmas
28.09.2012, 16:56
Попробуйте исправленный вариант и сообщите результаты.

Попробовал, сообщаю - не работает.. :(

ЗЫ. F5 жму со страшной силой...

kosmas
28.09.2012, 23:16
не хотите уточнить, какой способ Вы сейчас используете, реакция быстрых входов несколько отличается от обычных, если сейчас используется энкодер, то причина нерабоающей программы возможно в этом

Большое спасибо за пример с таймингами... Только я не понял, зачем

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); Так?

kosmas
29.09.2012, 13:05
переменная осталась как бонус, она нужна для реалов и то не во всех случаях, тем более в проге их нет.
По поводу таймера, можно еще связать его период с длительностью удержания кнопок, но это должна быть другая прога без массива

Может в проге и нет, но без нее не работает... Мистика... Требую пояснений! :)

kosmas
01.10.2012, 09:51
всё можно проще

За алгоритм спасибо, но это малость не то, что мне надо... :) В общем, оставил пока так...

kosmas
01.10.2012, 14:13
Раз уж пошла такая пьянка, подскажите, есть ли более простой способ узнать длительность нажатия кнопки, кроме как ловить передний и задний фронты?

Сейчас у меня это так:


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;


Как-то не изящно смотрится.. :)

Yegor
01.10.2012, 20:01
Угумс:
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

kosmas
02.10.2012, 09:06
Угумс:

Вот спасибо!

kosmas
16.11.2012, 09:03
Очередной дурацкий вопрос - как "мигнуть" светом? Мигнуть - это не выключить-включить, а притушить на 30% и вернуть обратно... Или при малой яркости прибавить 30% и вернуть.. Проблема - в скорости цикла. Т.е. если в одном цикле я уменьшаю яркость, а в следующем увеличиваю - лампа не успевает отработать. Пропускать некоторое количество циклов счетчиком - не самый удачный вариант, но другие пока не лезут в голову... А как два раза мигнуть?

Yegor
16.11.2012, 11:23
Я себе для «морзянки» лампочками и зумерами вот чо написал:
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

kosmas
19.11.2012, 10:25
Я себе для «морзянки» лампочками и зумерами вот чо написал:

Круто, конечно, но малость не то. Эта штука, насколько я понял, именно моргает. А мне надо "притушить" и обратно вернуть... :)

Генератор из util.lib вроде как больше подходит, но там не задать начальные значения, и как остановить его не понял...

kosmas
19.11.2012, 10:43
Ну, т.е.


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;
================================================== ========

Но, поскольку это происходит в пределах одного цикла, то ничего не происходит. А как это растянуть во времени - не понимаю... :(

Yegor
19.11.2012, 11:13
Эта штука, насколько я понял, именно моргает. А мне надо "притушить" и обратно вернуть...Из «моргать» сделать «тушить» можно одной строчкой. А в простейшем случае — одним оператором (SEL).

kosmas
20.11.2012, 22:06
Из «моргать» сделать «тушить» можно одной строчкой. А в простейшем случае — одним оператором (SEL).

Что-то я как тот доцент... Недопонял... Нельзя ли поподробнее???

Yegor
21.11.2012, 09:28
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% *)

kosmas
21.11.2012, 13:00
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% *)

Огромное спасибо! Буду пробовать...

Yegor
29.09.2014, 14:40
Если б хоть можно было писать timer(IN := ..., NOT Q => output) — ан нет, хрен тебеЯ знал! Я знал!

14547

В Стандарте есть эта фишка! Кодесис в очередной раз подтверждает своё весьма условное соответствие. Причём третья версия тоже это не поддерживает.

Yegor
29.09.2014, 17:59
а где написано про инверсию выхода?Я выдержку из стандарта рисунком привёл. Не видно разве? Там формальное описание грамматики языка 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 — работает. Так что в данном случае всё соответствует.

Boris_K
29.09.2014, 19:08
Да что вы возитесь с какими-то библиотеками, таймерами? Для отработки любых задержек - можно и без библиотек обходиться, есть стандартная функция TIME(), возвращает время в миллисекундах (с момента включения ПЛК), и этого достаточно. Если нужно отсчитать какую-то задержку с определённого момента - в требуемый момент присваиваете своей переменной текущее значение TIME(), а в последующих циклах проверяете условие: если разность текущего TIME() и той переменной превысила требуемую величину, то задержка закончилась.

Yegor
30.09.2014, 09:16
А с чего тогда взяли, что цитируете стандарт?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 — неправильно.