+
= все вместе
Вид для печати
Спасибо за подсказки.
Положил в копилку знаний.
Сейчас прошу помочь разобраться как создать функциональный блок на ST.
собственно говоря прочитав хелп к Овенлоджику и действуя по аналогии удалось самостоятельно создать ФБ типа таймер с задержкой отключения (для саморазвития и с прицелом на использование в следующем проекте).
Далее хотел добавить в него на вход импульс включения длительнстью 10ms (по аналогии RTIG), но что то пошло не так.
В редакторе висят 2 ошибки.
Подскажите что делаю не так.
Ну не совсем так. В первом ФБ если подать на вход 1 и убрать то он отсчитывает 10 секунд Если 1 оставить то он продолжает считать бесконечно.
Планировалось сделать что бы внутри ФБ был RTRIG.
Исходя из вашей подсказки после TP_1 в скобках выражение:
T:=10#ms - это время,
а I:=strt, - это как понимать? (м.б входу ФБ присвоить значение переменной strt из основной программы)?
Может есть где то более подробные разъяснения в этой части?
Что должно быть в круглых скобках?
PS.
Сделал так:
вроде заработало.Код:TP_1(I := strt, T := T#10ms);
strt_1 := TP_1.Q;
Правильно я понимаю, что I :=strt - это присвоение входу ФБ TP.SYS значения внешней переменной а strt_1 := TP_1.Q - это присвоение результата работы ФБ уже в следующую переменную?
И забегаявперёд - если нужно будет использовать несколькоблоков TP.SYS то надо будет их нумеровать типа так:
TP_1
TP_2
и т.д.
?
Все примерно так и есть. Посмотрите базовую информацию по ST в справке по OL (там не очень много, но есть) - нет смысла копировать её сюда.
Где-то на форуме была тема с реализацией ФБ типа "TON", "R_TRIG" и т.п. - поищите, если интересно.
И ещё - вы пытаетесь сделать свой "R_TRIG" на основе системного ФБ "TP.SYS" - при том, что можно просто использовать ФБ "R_TRIG.SYS"
Спасибо за подсказки.
Собственно говоря я уже это читал, просто некотрые моменты не понятны (учился давно по другой специальности (ТМС) и тогда комп был редкость. Проги писали на Фортране на листочке и сдавали преподу, а он окидывал взглядом и выносил вердикт - будет работать или нет).
Цель моих действий сделать ФБ который будет осуществлять следующий алгоритм: вращение вперёд-пауза-вращение назад и всё это повторять в течении определённого времени.
Делаю это для общего развития. На стандартных ФБ из овенлоджика уже сделано, теперь хочу освоить хотя бы в общих чертах ST. Привязаться хочу к функции DT потому как делала в ОЛ на BLINK и CTN и столкнулся с тем, что на длительных выдержках время уходит от встроенных часов из за конечной скорости работы программы. Приходилось вводить коррекцию.
Посмотрите конструкцию SWITCH - для реализации на ST это лучше, чем CTN.
По поводу неточности выдержки времени - я не уверен, что пользуясь внутренним таймером процессора вы не получите таких-же погрешностей на больших интервалах времени. И большая выдержка времени - это сколько в данном случае?
От суток до двух суток. Уже не помню какая там цифра набегала. На практике это и не важно в принципе. Просто тогда мы решили добиться точности и я в BLINK записывал 2 значения вкл - 1мкс, выкл 999мкс. Потом CTN ом считал количество секунд. А потом уже в залитой в ПР программе на макете сравнивал с часами реального времени ПРа и панели СП310. Затем уменьшал время выключенного состояния. Но такая регулировка получилась грубой.
В принципе я хотел привязаться к системным перемненым секунды минуты часы для задачи общего времени выполнения цикла. Короткие промежутки вперёд-стоп-назад не так важны.
Но как сделать не соображу пока.
Опять же это чисто для понимания. На практике оно не так уж важно.
В мануале rp_owen_logic.pdf есть такое:
Может к этой величине привязаться. По идее по ней внутренние часы сс мм чч и т.д. и должны работать.Следовательно расхождений выдержки с часами не должно быть.Цитата:
Функция GET_DATE_TIME
Функция GET_DATE_TIME возвращает значение тип DT (4 байта), содержащее данные часов реального
времени, в секундах с 00:00:00 1.01.2000 г., с учетом часового пояса, установленного в приборе
Или я что то не понимаю?
я может не всё понял, в чём проблема, но на форуме уже были жалобы на низкую точность частов в ПР
К точности работы часов реального времени ПР как раз было много претензий.
Если вам точность не сильно важна, то можно использовать их.
По поводу привязки - можете читать через GET_DATE_TIME текущее значение при начале нового периода, прибавлять к нему значение выдержки и ждать достижения этого значения, контролируя текущее значение времени через GET_DATE_TIME
Предлагаю взять этот пример за основу и доработать под Ваши желания:
Вложение 79431
Код:function_block FPRP
var_input
OnOff : bool; // Управление блоком
end_var
var_output
FW, RW : bool; // Сигналы вперед и назад
end_var
var
State : udint; // Текущее состояние
t : udint; // Метка времени события в мс
end_var
if OnOff then
case State of
0: // Пауза 1 сек перед пуском вперед
if time_to_udint(get_time()) - t >= 1000 then
State := 1; FW := true; RW := false; t := time_to_udint(get_time()); // Пуск вперед
end_if
1: // Вращение вперед 2 сек перед паузой
if time_to_udint(get_time()) - t >= 2000 then
State := 2; FW := false; RW := false; t := time_to_udint(get_time()); // Пауза
end_if
2: // Пауза 1 сек перед пуском назад
if time_to_udint(get_time()) - t >= 1000 then
State := 3; FW := false; RW := true; t := time_to_udint(get_time()); // Пуск назад
end_if
3: // Вращение назад 2 сек перед паузой
if time_to_udint(get_time()) - t >= 2000 then
State := 0; FW := false; RW := false; t := time_to_udint(get_time()); // Пауза
end_if
end_case
else
FW := false; RW := false; State := 0; t := 0;
end_if
end_function_block
почему не просто
t : time;
?
вангую что в else тоже
t := time_to_udint(get_time());
Хорошо. Как скажите:
Код:function_block FPRP
var_input
OnOff : bool; // Управление блоком
end_var
var_output
FW, RW : bool; // Сигналы вперед и назад
end_var
var
State : udint; // Текущее состояние
t : time; // Метка времени события
end_var
if OnOff then
case State of
0: // Пауза 1 сек перед пуском вперед
if get_time() - t >= T#1s then
State := 1; FW := true; t := get_time(); // Пуск вперед
end_if
1: // Вращение вперед 2 сек перед паузой
if get_time() - t >= T#2s then
State := 2; FW := false; t := get_time(); // Пауза
end_if
2: // Пауза 1 сек перед пуском назад
if get_time() - t >= T#1s then
State := 3; RW := true; t := get_time(); // Пуск назад
end_if
3: // Вращение назад 2 сек перед паузой
if get_time() - t >= T#2s then
State := 0; RW := false; t := get_time(); // Пауза
end_if
end_case
else
FW := false; RW := false; State := 0; t := T#0s;
end_if
end_function_block
Ну в принципе я так делаю.
Это просто выдержка времени привязанная к секундам.Код:Time_1 := GET_DATE_TIME();
curr_t := DT_TO_UDINT (Time_1);
if strt then
trgt_t := curr_t + ust;
end_if
if trgt_t <= curr_t then
out_dsb := true;
out_enb := false;
else
out_dsb := false;
out_enb := true;
end_if
Насчёт точности - сравнивал время насчитанное моим макросом в пр с временем панели сп310. Там было расхождение. Опять же сейчас я не решаю какую то конкретную задачу, а учусь писать код на ST. Ну естественно надо что то конкретное делать - вот и практикуюсь.
За пример спасибо - буду разбираться.
Вы хотите сказать, что get_time() - T#0s будет < T#1s в течении 1.5 месяцев?
Я думал, Вас беспокоит, что при быстром сбросе OnOff при State := 3; произойдет смена вращения без паузы.
А вообще, я бы с Вами согласился, если бы арифметика была бы со знаком!!!
Давайте предупредим новичков, что get_time() каждые 1.5 месяца обнуляется.
И да, при get_time() = Tmax, какое-то состояние будет выдержано не полностью.
Предлагаю безопасный вариант с предварительной паузой:
Код:function_block FPRP
var_input
OnOff : bool; // Управление блоком
end_var
var_output
FW, RW : bool; // Сигналы вперед и назад
end_var
var
State : udint; // Текущее состояние
t : SYS.TON; // Таймер
end_var
if OnOff then
case State of
0: // Пауза 1 сек перед пуском вперед
t(I:=true, T:=T#1s);
if t.Q then
State := 1; FW := true; t(I:=false); // Пуск вперед
end_if
1: // Вращение вперед 2 сек перед паузой
t(I:=true, T:=T#2s);
if t.Q then
State := 2; FW := false; t(I:=false); // Пауза
end_if
2: // Пауза 1 сек перед пуском назад
t(I:=true, T:=T#1s);
if t.Q then
State := 3; RW := true; t(I:=false); // Пуск назад
end_if
3: // Вращение назад 2 сек перед паузой
t(I:=true, T:=T#2s);
if t.Q then
State := 0; RW := false; t(I:=false); // Пауза
end_if
end_case
else
FW := false; RW := false; State := 0; t(I:=false);
end_if
end_function_block
Не. С 1.5мес я погорячился. Не в этом случае.
Потенциальный крантец (в старом варианте) остается из-за практически всегда отсутствия задержки шага 0 после подъема OnOff.
При шаблонном использовании такой конструкции в шаге 0 могут быть какие-то подготовительные действия которые пойдут лесом. Смысл в таймере шага 0?
TON тут не причем, простое t := get_time(); (а не := 0) в else эту проблему решает.
Ну и лично я (дело вкуса) предпочитаю выходы привязывать не в шаге, а к шагу
Там где есть ENUM это максимально наглядно, да и здесь чуть очевидней.Код:..
end_case
FW := State = 1;
RW := State = 3;
А когда шагов много, еще и
Т.е. самовзвод таймера шагов после любого перехода. Но это норм когда само состояние OnOff = false тоже шаг, а не IF (внешний if для case не использую)Код:..
end_case
if State <> State_ then //State_ - прошлое значение
State_ := State;
t := get_time();
end_if
Так и будет 1 сек если
Код:else
... t := get_time();
а если
то будет 1 цикл, не смотря наКод:else
... t := t#0s;
точнее - практически никогда не будет шага 0Код:0: // Пауза 1 сек перед пуском вперед
if get_time() - t >= T#1s then
State := 1; FW := true; t := get_time(); // Пуск вперед
end_if
Ok. Давайте точнее.Цитата:
Код:else
t := get_time(); // и это значение равно Tmax(1.5мес)
t := udint_to_time(FFFFFFFFh), на калькуляторе можно десятичный вид посмотреть
НеаЦитата:
тогда на следующем цикле get_time() := 0 и:
Код:0 - Tmax > T#1s // Истина в этом же цилк и State := 1 тоже в этом же цикле
0 - FFFFFFFFh = 1, мс => false
1 - FFFFFFFFh = 2, мс => false
....
1000 - FFFFFFFFh = 1001, мс => true
Всё! Сдаюсь!! :rolleyes:
А можно теперь простыми словами разъяснить суть дискуссии?
И каков правильный вариант.
Правильный вариант, когда в ветке else стоит:
Т.е. на каждом цикле когда OnOff = false ФБ будет t := get_time() для того, чтобы в тот момент, когда OnOff станет true, нормально отработала первая пауза.Код:else
t := get_time();
Дискуссия возникла из-за недопонимания того, что в беззнаковой арифметике Xmax = 0 - 1,
а значит пауза отработает правильно даже при обнулении get_time() через 4294967296 мс (или примерно через 50 суток).
Если оставить t := T#0s, то в момент когда переполнится get_time() и будет выполняться State = 3 - произойдет реверс без паузы.
Попробуйте быстро выключить и включить OnOff во время вращения назад - произойдет реверс без паузы, а это опасно в большинстве случаев.
Спасибо, оба варианта интересные.
По аналогии с предложенными вариантами получилось вот так - см аттач.
В else закоментированные строки выдают ошибку, но работает и без них. Что то я не понимаю, но что не понимаю - не понимаю. Подправьте как должно быть. И пару слов пояснений если не затруднит.:)
Код:function_block DRIVER_drm //имя функционального блока.
var_input //объявление входных переменных
strt : bool;//старт
stop : bool;//стоп
pause : bool;//пауза
end_var
var_output //объявление выходных переменных
rt_fw : bool;//вращение вперёд
rt_bw : bool;//вращение назад
end_var
var //объявление локальных переменных
State : udint; // Текущее состояние
t : SYS.TON; // Метка времени события
end_var
//CODE
if strt then
case State of
0: t(I:=true, T := t#1s);
if t.Q then State := 1; t(I := false); end_if
1: t(I:=true, T := t#3s);
if t.Q then State := 2; t(I := false); end_if
2: t(I:=true, T := t#1s);
if t.Q then State := 3; t(I := false); end_if
3: t(I:=true, T := t#3s);
if t.Q then State := 0; t(I := false); end_if
end_case
rt_fw := State = 1;
rt_bw := State = 3;
else
State := 0;
t(I := false);
rt_fw := false;
rt_bw := false;
end_if
end_function_block
мне кажется имеет смысл к этому коду добавить ловлю фронта старта, чтоб если это первый запуск начинать с кейса №1 как просил вопрашающийЦитата:
Цель моих действий сделать ФБ который будет осуществлять следующий алгоритм: вращение вперёд-пауза-вращение назад и всё это повторять в течении определённого времени.
Спасибо. Работает.
Т.Е. надо просто в отсутствии strt устантвить state 0 (первая ступень цикла) и присвоить t : SYS.TON; логический 0.
А что в таком случае будет происходить при переполнении get_time()? Или при использовании SYS.TON это не важно?
Да, неплохо бы. Хотя после снятия сигнала со входа ПР цикл и так начинается сначала. Что было бы полезнее - это по заднему фронту strt делать паузу и потом возобновление процесса с того же момента. А вообще в проекте на стандартных ФБ в Овенлоджике у меня сделано три кнопки старт, пауза и стоп и по передним фронтам от их нажатия осуществляется управление. Но наверное это сильно здесь всё усложнит.Цитата:
мне кажется имеет смысл к этому коду добавить ловлю фронта старта, чтоб если это первый запуск начинать с кейса №1 как просил вопрашающий
Буду думать .
Спасибо за подсказки.
Что мне интересно - это как вот здесь t(I:=true, T := t#1s); менять время t с входной переменной.
Если Вы используете SYS.TON, то get_time() - не нужен и наоборот.
Попробуйте понять как работает этот вариант, где нет ни того ни другого:
capzap предложил Вам добавить в код:Код:function_block DRV
var_input
Start : bool;
Stop : bool;
tStart : udint;
tStop : udint;
end_var
var_output
FW : bool;
RW : bool;
end_var
var
btn : SYS.RS;
bl : SYS.BLINK;
st : SYS.CTN;
end_var
btn(S := Start, R := Stop);
bl(I := btn.Q, Th := udint_to_time(tStart), Tl := udint_to_time(tStop));
st(U := bl.Q, R := st.Q = 2);
FW := (st.Q = 0) and bl.Q;
RW := (st.Q = 1) and bl.Q;
end_function_block
Некоторые пишут так:Код:if strt then OnOff := true; end_if // Фиксируем состояние входа "Старт"
if stop then OnOff := false; end_if // Но "Стоп" является приоритетом, но тогда...
if OnOff then // вместо if strt then
Код:OnOff := (OnOff or strt) and not stop;
Можно входные переменные вставлять в параметры других блоков, например:
Код:t(I:=true, T:=udint_to_time(ust_p)); // где ust_p задается в миллисекундах!!!
Насколько я понял на входе детектор переднего фронта, далее имеем генератор который выдаёт импульсы длительность которых задаётся соответствующими входными переменными и наконец счётчик всё считает и по его результату переключаются входы.
Правильно я понял?
На стандартных ФБ я так примерно и делал, только там BLINK выдавал импульсы в 1 сек, а счётчик их считал (сколько задано по входной переменной). В таком варианте поставив BLINK на паузу (сняв входной сигнал) весь процесс становиться на паузу и продолжается с того же места.
Остальное (как отловить входной фронт и вставить переменную в ФБ) вроде понятно. Надо будет попробовать применить на практике.
Для меня сложно угадать правильный синтаксис. С вашими примерами многое становиться понятно.
Спасибо за подсказки.
Добавлен автостоп по истечении tWork:
Самый лучший способ научиться - это читать чужой код (только хороший код - это проблема).Код:function_block DRV
var_input
Start : bool;
Stop : bool;
tStart : udint;
tStop : udint;
tWork : udint; // Время работы блока
end_var
var_output
FW : bool;
RW : bool;
end_var
var
btn : SYS.RS; // Кнопка фиксирующая Start/Stop
bl : SYS.BLINK; // Генератор времени вращения и паузы
st : SYS.CTN; // Переключатель направления вращения
tW : SYS.TON; // Таймер работы блока
end_var
btn(S := Start, R := Stop or tW.Q); // Кнопка которая фиксирует Start и сбрасывает по Stop или по таймеру tW
tW(I := btn.Q, T := udint_to_time(tWork)); // Таймер, который работает пока кнопка в состоянии Start
bl(I := btn.Q, Th := udint_to_time(tStart), Tl := udint_to_time(tStop)); // Переключатель Вращение/Пауза
st(U := bl.Q, R := st.Q = 2); // Счетчик вращений от 0 до 1
FW := (st.Q = 0) and bl.Q; // Счетчик и переключатель определяют состояние выходов
RW := (st.Q = 1) and bl.Q;
end_function_block
Профи не любят им делиться - хлеб свой отдавать кому-то, но очень любят стебаться над новичками (даже здесь есть дедовщина).
Ну я так и пытаюсь делать.:)
Непонятно как работает во эта строка
st(U := bl.Q, R := st.Q = 2); // Счетчик вращений от 0 до 1
конкретно
R := st.Q = 2 - это что делает?
Счетчик сбрасывается в 0 при R = true, т.е. как только он досчитает до 2, то на следующем цикле счетчик сбросится.
Счетчик можно заменить на направление вращения:
Код:function_block DRV
var_input
Start : bool;
Stop : bool;
tStart : udint;
tStop : udint;
tWork : udint; // Время работы блока
end_var
var_output
FW : bool;
RW : bool;
end_var
var
bl : SYS.BLINK; // Генератор времени работы и паузы
tW : SYS.TON; // Таймер работы блока
tr : SYS.FTRIG; // Переход на паузу
dir : bool; // Направление вращения
OnOff : bool; // Кнопка
end_var
OnOff := (OnOff or Start) and not (Stop or tW.Q);
tW(I := OnOff, T := udint_to_time(tWork));
bl(I := OnOff, Th := udint_to_time(tStart), Tl := udint_to_time(tStop));
tr(I := bl.Q); if tr.Q then dir := not dir; end_if
FW := not dir and bl.Q;
RW := dir and bl.Q;
end_function_block
То есть когда Q = 2 тогда происходит R - reset и опять на входе 0.