Мне надо, что бы QQ была равна первоначальному значению ТТ, т.е. 100.
Вид для печати
Код:VAR CONSTANT
QQ: INT := 100;
END_VAR
Почему???
Вот вполне себе рабочий вариант:
Но, к сожалению, это только для лабораторных работ. Проблема в том, что если переменная TT берет значения откуда-то, то при первом же цикле, еще ДО присвоения данных ТТ "снаружи", QQ становится равно ТТ, и не меняется. А при старте ТТ = 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;
ELSIF DI1 AND TT = 0 THEN
TT :=0;
ELSE
TT := QQ;
END_IF
Думаю дальше... :)
Прочитал про указатели, но как их использовать для моей задачи так и не понял... Люди! Дайте пример... Ну пожалуйста...
Ну-с, приступим... Набросал пример из кина, только имна изменил. Цепляю к сообщению. Как работает, вроде бы понятно.
Но тут одно НО. В самом начале PLC_PRG переменной ТТТ присваивается значение "100". Поэтому RRR тоже будет 100. Я же пытаюсь понять, как это сделать с динамической переменной. Т.е. в процессе она вычисляется, потом по событию она начинает меняться, и вот этот момент мне и надо зафиксировать. Чтобы при пропадании события ТТТ вернулась к запомненному значению.Код: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 ;
Ну, т.е. без глобального цикла будет такое:
Код:TTT := WWW;
IF DI1 THEN
TTT := TTT-10; (* Если DI1 то начинаем менять *)
ELSE
TTT := WWW; (* Если DI1 пропало - вернули как было *)
END_IF
Возникла еще одна проблема - начал разбираться, почему кнопки через раз работают, для чего подключил модуль статистики.
Кино - в приложении. Я так понимаю, что идет банальный перегруз процессора. :eek:
Выход я так понимаю в том, чтобы не использовать oscat... Это печаль...
На всякий случай приоржил сам проект...
1. Используйте функцию SEL или функции выбора IF, Case....
2. Увеличьте мин. время цикла до 2-3 мс. На производстве S-400 работают с циклом 15-17мс, временами до 30мс. Если время критично выводите скоростные задачи на сопроцессор.
Скорее всего у вас в программе ошибки. Я в вашей программе сложных задач для сопроцессора не вижу.
сопроцессор, это если к примеру на одном ПЛК или ПР выполнять небольшую, критичную ко времени задачу. А на другом соединенном с ним по сети крутить основную программу.
К примеру при работе с энкодером или управление резкой изделий. Контроллер может иметь время цикла, не обеспечивающее нужную точность позиционирования или размера.
Да понятно, что не для меня... Просто начал разбираться - да, если пользовать функцию, все работает. А вот с функциональным блоком получается фигня... Т.е. совсем не получается. :( Мне нужны ФБ...
Что-то голова пухнет с непривычки.. :)Код:================= Программа ======================
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
Там что, плавное затухание? Жуть какая. Всё укладывается в две строчки:А если в конфигурацию добавить модуль Statistics и там сделать переменную CycleTime, то можно обойтись одной строкой:Код: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();
Скорость возрастания и затухания регулируется числовой константой. На входе только кнопки DI1/2, на выходе AO1.Код:AO1 := LIMIT(0, AO1 + CycleTime * 0.0016 * (BOOL_TO_REAL(DI1) - BOOL_TO_REAL(DI2)), 10);
Я упрощу, чтобы вы поняли:Цитата:
как это работает
Выражение в скобках даёт −1, 0 или +1 в зависимости от того, какие кнопки нажаты. В общем-то, больше объяснять тут и нечего. В полном варианте вводится поправка на длительность цикла чтобы от неё не было зависимости.Код:AO1 := AO1 + 0.001 * (BOOL_TO_REAL(DI1) - BOOL_TO_REAL(DI2));
Или вы даже не знаете, что в ПЛК ваша программа крутится в бесконечном цикле с периодичностью около 1-50 мс? Тормозить поток, как это делается в прикладном программировании, на контроллерах не принято. Ну а в симуляторе периодичность исполнения программы гораздо реже.
Про большой цикл я в курсе... Только пока не совсем освоил, как им пользоваться... Я до этого только всякие shell да perl пользовал по мелочи...
В этой ветке, малость выше, я пытался научиться просто сохранять значение переменной "в стороне". Пока неудачно... :(
Я так понял, что конструкция "TIME_TO_REAL(TIME() - PrevScanTimestamp)" просто вычилсяет время цикла?
Да, в миллисекундах.Цитата:
Я так понял, что конструкция "TIME_TO_REAL(TIME() - PrevScanTimestamp)" просто вычилсяет время цикла?
Итак, очередной отвал башки...
Вот такая программка работает:
Т.е. при нажатии DI1, АО7 медленно растет до 1000.Код:======================================================
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
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();
Все. Мысль остановилась. Это как???
Я просто хотел этот кусок
вынести в функицию. Потому, что он мне нужен будет с разными значениями Speed_Reg, Limit_Reg, btn_up, btn_dn. Выход-то все равно один - i, нужный для получения индекса массива.Код: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);
И вот это самое i, я хочу периодически сохранять, для чего и затеял функцию.. С ФБ пока не научился...
Это я для примера сюда впихнул кусок...
Весь цимес - в массиве. Он на самом деле большой и нелинейный -там забиты значения квадратичной ф-ции.. А сделано это для облегчения жизни контроллера - этот массив один раз посчитал, и все. Зачем его каждый раз пересчитывать?
Не та сложность вычислений, чтобы прибегать к таблицам поиска. По-моему, здесь лучше на лету считать.Цитата:
Весь цимес - в массиве. Он на самом деле большой и нелинейный -там забиты значения квадратичной ф-ции.. А сделано это для облегчения жизни контроллера - этот массив один раз посчитал, и все. Зачем его каждый раз пересчитывать?
Вместо всего этого...Надо писать так:Код: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_reg или в нуль, i всё равно не выпрыгнет за пределы, т.к. там функция LIMIT. Попробуйте исправленный вариант и сообщите результаты.Код: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();
Большое спасибо за пример с таймингами... Только я не понял, зачем
- вроде нигде не фигурирует в текстах...Код: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
Ну, т.е.
Но, поскольку это происходит в пределах одного цикла, то ничего не происходит. А как это растянуть во времени - не понимаю... :(Код: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).Цитата:
Эта штука, насколько я понял, именно моргает. А мне надо "притушить" и обратно вернуть...