PDA

Просмотр полной версии : ПЛК110.60-М + МУ110-6У. Ступеньки на выходе МУ110-6У.



Daedal
16.06.2017, 11:53
Всем доброго дня!

1. Настроил связь указанных модулей по Modbus-RTU.
2. Активировал таймер, по которому формирую тестовый сигнал - пилу.
3. Передаю на МУ110-6У.
4. Вижу на выходе картину, которую прикрепил тут.
5. В это время(определял на глаз, что именно в это время) вижу статус соединения, как ошибка 255 - тайм-аут..

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

Архив проекта во вложении. Если надо, могу сделать скрины всех настроек.

Перепробовал множество значений для тайм-аута соединения TimeOut : TIME:=T#Хms, время выполнения цикла ПЛК тоже менял вплоть до 50 мс, скорость соединения тоже менял на 9600.

Подскажите, пожалуйста, откуда берётся ступенька на выходе МУ110-6У? Как избавиться от тайм-аута?
Спасибо.

Daedal
17.06.2017, 18:08
Приём, приём.

Помогите, люди добрые!
В понедельник надо это на объекте использовать, я уже весь мозг сломал - не пойму, в чём дело!

Филоненко Владислав
18.06.2017, 09:28
Передавать надо промежуточный буфер. а у Вас передаются данные из меняющейся переменной. Вот и выходят казусы.
Введите промежуточную переменную, к-я не будет меняться во время ВСЕЙ длительности передачи, т.е. менять её только ПЕРЕД началом новой передачи

Daedal
18.06.2017, 10:45
Ввёл промежуточную переменную, присваиваю ей значение счётчика таймера до работы с МУ100-6У - не помогло.

Тем более, я писал, что такой же эффект, когда я работаю без прерывания, в основном теле цикла ПЛК.
Следовательно, переменная меняется один раз за цикл и, конечно, не в момент передачи на МУ110-6У.

В первом посте я прикрепил проект, если будет возможность проверьте сами, что с переменной будет тоже самое.

ASo
18.06.2017, 10:49
1. Передача WORD в 6У идет старшим байтом вперед. Переставьте местами элементы массива.
2. Измените процедуру обмена, Вы не даете работать библиотечной ФБ.

Daedal
18.06.2017, 10:53
1. Это известно. Но буфер подключается после его наполнения, т.е. в каком порядке я его наполняю не имеет значения.
Проверил - не помогло.
2. Что Вы имеете ввиду, расскажите, пожалуйста, поподробнее.

ASo
18.06.2017, 10:57
1. Я имел ввиду - поменяйте местами индексы.
2. Вы должны вызывать ФБ с Enable:=false до получения Complete=true. Дайте ФБ получить подтверждение от устройства.

Daedal
18.06.2017, 11:08
2. Сделал. Результат - Errorcode - 255, т.е. тайм-аут.. Ставил время 50 мс и тайм-аут 30 мс - не помогло.

Ожидание в state 1, в котором я отключаю порт, сделал так же, как и для state 0, где я передаю данные на МУ110-6У:

IF WR_M_REG.Complete = TRUE THEN
state:= 0;
END_IF

capzap
18.06.2017, 11:09
при записи можно не обращать внимание на 255, хотите от этого избавиться чередуйте с чтением чего нибудь или правте библиотеку

Daedal
18.06.2017, 11:12
Я и не обращаю, но пила не формируется т.к. WR_M_REG.Complete всегда FALSE

Следовательно, я не могу перейти на state = 0 и в следующем цикле ПЛК я оказываюсь на state = 1, т.е. не передаю данные на МУ100-6У, а сразу отключаю его.

Или мне всё-таки не нужно ждать ответ от устройства на state 1?

Daedal
18.06.2017, 11:25
Убрал вообще state 1 - ничего не поменялось.

Я не понимаю эту штуковину..:confused:

capzap
18.06.2017, 12:13
по документации рекомендуемое время таймаута 10мс, почему у Вас 3мс? В таком коде как у Вас почему он должен переходить в следующий кейс, если функция записи постоянно висит во включенном состоянии, где пересброс? Хотя бы в условии проверки комплита, сделайте WR_M_REG.enable:=FALSE;

Daedal
18.06.2017, 12:24
Тайм-ауты делал самые разные, в т.ч. и 10 мс. Сейчас поставил 10 мс - без изменений.
В следующий кейс он переходит по комплиту, WR_M_REG.Enable никак не мешает это делать. В следующем кейсе я как раз и делаю WR_M_REG.enable:=FALSE;

Сделал, как Вы предложили - без изменений.

Попробовал вместо пилы подать всегда константу - выбросов-ступенек нет.
Т.е. только когда значение меняется, что-то происходит..

ASo
18.06.2017, 12:30
Тайм-ауты делал самые разные, в т.ч. и 10 мс. Сейчас поставил 10 мс - без изменений.
В следующий кейс он переходит по комплиту, WR_M_REG.Enable никак не мешает это делать.Мешает, вы постоянно инициируете новую посылку, не давая получить ответ от предыдущей.

Daedal
18.06.2017, 12:36
я это исправил - без изменений.
тем более, что переход я делаю по комплиту, т.е. когда текущая посылка завершена. О чём Вы говорите?
В первом посте есть архив проекта, попробуйте, пожалуйста.

Попутно ещё вопрос, может имеет отношение к проблеме:

Почему WR_M_REG.RegCnt = 0 всегда? Я ведь записываю 1 регистр и вижу на выходе МУ110-6У результат, пусть и с багом.

capzap
18.06.2017, 13:55
В следующем кейсе я как раз и делаю WR_M_REG.enable:=FALSE;

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

ЗЫ Проект не открывал в КДС, но при просмотре в архиве, видел что конфигурация заполнена какими то модулями по модбас и овен, а тут еще и библиотека что то пытается писать, Вам это точно всё нужно?

Daedal
18.06.2017, 14:08
не переходит из state 1(отключаю порт после отправки данных) в state 0(передаю данные) т.к. условие:
IF WR_M_REG.Complete = TRUE THEN
state:= 0;
END_IF
всегда ложное. Т.е. комплит на стадии ОТКЛЮЧЕНИЯ порта не приходит никогда, получается даже по тайм-ауту..
Следовательно я на СЛЕДУЮЩЕМ цикле ПЛК не могу ничего передать.
Т.к. я один раз передаю, потому жду комплита на стадии отключения и не дожидаюсь его никогда.
Я этот комплит поставил по совету Aso, у меня его не было. Сейчас я его убрал.

В проекте есть Modbus TCP, Овенов никаких нет. Но в примере, который я выслал сюда Modbus TCP не активен, а в реалии он нужен, я его просто не подчистил в конфигурации ПЛК.

capzap
18.06.2017, 14:15
уж не знаю что Вы там называете закрытием порта, комплит естественно не наступит если энабле будет фальш, сомневаюсь что Вам именно это советовали. Чтоб перейти в нулевой кейс, в первом кейсе нужно реализовать таймер, по истечении которого переходить к очередной записи

Daedal
18.06.2017, 14:28
уж не знаю что Вы там называете закрытием порта, комплит естественно не наступит если энабле будет фальш, сомневаюсь что Вам именно это советовали.
Логично, я просто делаю всё, что советуют, чтобы не было : "Я написал, Вы не сделали." Сам совет немного выше есть. Но это не суть.


Чтоб перейти в нулевой кейс, в первом кейсе нужно реализовать таймер, по истечении которого переходить к очередной записи

Только так и никак иначе? Т.е. это стандартное поведение в таком случае? Проверяю это сейчас.

Daedal
18.06.2017, 15:15
Счётчик не помогает. Картина просто растягивается по времени.

Есть ли разница в работе, если настраивать работу Modbus RTU в конфигурации задач ПЛК?

Спецы, как вы настраиваете работу ПЛК110 + МУ110-6У ?

capzap
18.06.2017, 15:46
Сам совет немного выше есть
смысл был в том, чтоб в ФБ на вход, enable подавать положительный импульс а не держать постоянно TRUE.
С таймером или без, не так важно, просто он бы давал постоянство в изменении сигнала, если единичные изменения проходят, значит дело не в передаче данных, а в блоке, который формирует пилу, где то там видимо проблема, поставте счетчик, который будет отлавливать знак из разницы текущей отправки от предыдущей, если счетчик начнет прибавлять на спаде или подъеме пилы, значит не верно пила формируется

Daedal
18.06.2017, 16:15
Заменил способ работы с портом - сделал через конфигуратор задач - всё хорошо, пила без ступенек!

Прошу всё-таки подсказать, как работать с портом на языке ST? Или все делают через конфигуратор?
Может ли работы на ST иметь баг в этой части?

Спасибо.

Daedal
18.06.2017, 16:16
смысл был в том, чтоб в ФБ на вход, enable подавать положительный импульс а не держать постоянно TRUE.
С таймером или без, не так важно, просто он бы давал постоянство в изменении сигнала, если единичные изменения проходят, значит дело не в передаче данных, а в блоке, который формирует пилу, где то там видимо проблема, поставте счетчик, который будет отлавливать знак из разницы текущей отправки от предыдущей, если счетчик начнет прибавлять на спаде или подъеме пилы, значит не верно пила формируется

Пила формируется верно, её я сразу проверил.

capzap
18.06.2017, 17:32
На картинке я вижу что в модуль приходит пила и к ней плюсом генерация прямоугольных импульсов

Daedal
18.06.2017, 17:37
На картинке я вижу что в модуль приходит пила и к ней плюсом генерация прямоугольных импульсов

Это и была проблема. Сейчас прямоугольников нет. Пост №22 не пропустили?

capzap
18.06.2017, 19:27
Это и была проблема. Сейчас прямоугольников нет. Пост №22 не пропустили?

а я повторяю, разбирайтесь, что подаете на вход функции записи. Через библиотеку всё работает. Научитесь выполнять действия по таймеру, изучите представленные генераторы сигналов в оскат бибке и возможно в util что то есть

Daedal
18.06.2017, 19:41
а я повторяю, разбирайтесь, что подаете на вход функции записи.
Я с удовольствием разберусь, если Вы мне поможете.

Я подаю на вход функции записи чистую пилу - это проверено, в коде это можно увидеть.



Через библиотеку всё работает. Научитесь выполнять действия по таймеру

Таймер работает по прерыванию, его вызов не должен никак влиять на работу основной программы.
В примерах и документации я нигде не встречал иного.
В теле прерывания простая вычислительная операция - приращение счётчика.
Что я тут упускаю?



изучите представленные генераторы сигналов в оскат бибке и возможно в util что то есть

Если делать пилу без библиотек, то обязательно будет баг?

У меня пример-то простой, счётчик и всё. Зачем тут библиотека?

Может быть Вы выложите пример куска кода, где реализована работа через библиотеку?

capzap
18.06.2017, 20:16
Все что передал контроллер в модуль, Вы увидели на своей осциллограмме, если бы не было обмена полностью или частично, не было бы и пилы вовсе
Выполняя ПОУ в разных задачах, надо усвоить правило, что общую глобальную переменную в одном месте только читают, в другом только записывают
Библиотека Вам нужна чтоб приобрести опыт, Ваш генератор может себя прекрасно показывать, когда Вы его отдельно тестируете, но в режиме многозадачности могут быть проблемы

Daedal
18.06.2017, 20:26
Все что передал контроллер в модуль, Вы увидели на своей осциллограмме, если бы не было обмена полностью или частично, не было бы и пилы вовсе

В реальном проекте я значения пилы передаю на ModbusTCP сервер и далее в БД. Оттуда строю график и вижу, что пила чистая.
Я ведь выложил тестовый проект - в нём нет формирования прямоугольных импульсов.


Выполняя ПОУ в разных задачах, надо усвоить правило, что общую глобальную переменную в одном месте только читают, в другом только записывают

Это мне известно и мною выполняется.


Библиотека Вам нужна чтоб приобрести опыт, Ваш генератор может себя прекрасно показывать, когда Вы его отдельно тестируете, но в режиме многозадачности могут быть проблемы

Я бы не сказал, что у меня нет опыта.

Я предлагаю всем, у кого опыт есть, посмотреть проект, прикреплённый в первом посте и доработать его до нормальной работы.
Или выложить правильную настройку из любого другого, своего проекта.

ASo
18.06.2017, 20:34
Например, вот, если интересует механизм работы с бибкой

PROGRAM ExModbus
VAR
SendModbus2: MB_WR_SNG_REG;
SendModbus4: MB_RD_HOLD_REGS;
SendModbus5: MB_WR_REGS;
ES: BOOL:= TRUE;
TimeOut: TIME:= T#100ms;
Buffer: ARRAY[0..255] OF BYTE;
ErrorSend2: ARRAY[1..5] OF BYTE;
ErrorSend4: ARRAY[1..5] OF BYTE;
ErrorSend5: ARRAY[1..5] OF BYTE;
Stage: BYTE:= 1;
EndSendStored: BOOL;
EndSendError: BOOL:= FALSE;
HavePause:TON;
SpeedPtr: POINTER TO BYTE;

StartTRV1: BOOL;
SetupTRV1: WORD;
SuperHeat: WORD;
END_VAR

IF ComPortState = 2 THEN
IF NOT EndSendStored THEN
CASE Stage OF
1: SendModbus4(
Enable:= ES,
Mode:= MB_RTU,
DevAddr:= 1,
FirstAddr:= 2530,
Quantity:= 1,
ComHandle:= Settings.Port,
TimeOut:= Timeout,
Buffer:= Buffer);
IF SendModbus4.Complete THEN
EndSendStored:=TRUE;
ErrorSend4[1]:= SendModbus4.Exception;
IF ErrorSend4[1] = 0 THEN
EvpTemp1:= Buffer[0]*256 + Buffer[1];
TRV1Alarm:= FALSE;
ELSE
EvpTemp1:= 16#FFFF;
TRV1Alarm:= TRUE;
EndSendError:= TRUE;
Stage:= 1;
END_IF;
END_IF;
2: SendModbus4(
Enable:= ES,
Mode:= MB_RTU,
DevAddr:= 1,
FirstAddr:= 116,
Quantity:= 1,
ComHandle:= Settings.Port,
TimeOut:= Timeout,
Buffer:= Buffer);
IF SendModbus4.Complete THEN
EndSendStored:=TRUE;
ErrorSend4[2]:= SendModbus4.Exception;
IF ErrorSend4[2] = 0 THEN
StartTRV1:= BYTE_TO_BOOL(Buffer[1] AND 1);
TRV1Alarm:= FALSE;
ELSE
TRV1Alarm:= TRUE;
EndSendError:= TRUE;
Stage:= 1;
END_IF;
END_IF;
3: SendModbus4(
Enable:= ES,
Mode:= MB_RTU,
DevAddr:= 1,
FirstAddr:= 99,
Quantity:= 1,
ComHandle:= Settings.Port,
TimeOut:= Timeout,
Buffer:= Buffer);
IF SendModbus4.Complete THEN
EndSendStored:=TRUE;
ErrorSend4[3]:= SendModbus4.Exception;
IF ErrorSend4[3] = 0 THEN
SetupTRV1:= Buffer[0]*256 + Buffer[1];
TRV1Alarm:= FALSE;
ELSE
TRV1Alarm:= TRUE;
EndSendError:= TRUE;
Stage:= 1;
END_IF;
END_IF;
4: IF StartTRV1 <> StartTRV THEN
SendModbus2(
Enable:= ES,
Mode:= MB_RTU,
DevAddr:= 1,
RegAddr:= 116,
Value:= BOOL_TO_WORD(StartTRV),
ComHandle:= Settings.Port,
TimeOut:= TimeOut);
IF SendModbus2.Complete THEN
EndSendStored:=TRUE;
ErrorSend2[1]:= SendModbus2.Exception;
END_IF;
ELSE
EndSendStored:= TRUE;
END_IF;
5: IF SetupTRV1 <> SetupTRV THEN
SendModbus2(
Enable:= ES,
Mode:= MB_RTU,
DevAddr:= 1,
RegAddr:= 99,
Value:= SetupTRV,
ComHandle:= Settings.Port,
TimeOut:= TimeOut);
IF SendModbus2.Complete THEN
EndSendStored:=TRUE;
ErrorSend2[2]:= SendModbus2.Exception;
END_IF;
ELSE
EndSendStored:= TRUE;
END_IF;
6: SendModbus4(
Enable:= ES,
Mode:= MB_RTU,
DevAddr:= 1,
FirstAddr:= 2535,
Quantity:= 1,
ComHandle:= Settings.Port,
TimeOut:= Timeout,
Buffer:= Buffer);
IF SendModbus4.Complete THEN
EndSendStored:=TRUE;
ErrorSend4[4]:= SendModbus4.Exception;
IF ErrorSend4[4] = 0 THEN
SuperHeat:= Buffer[1];
TRV1Alarm:= FALSE;
ELSE
SuperHeat1:= 16#FFFF;
EndSendError:= TRUE;
Stage:= 1;
END_IF;
END_IF;
7: SendModbus4(
Enable:= ES,
Mode:= MB_RTU,
DevAddr:= 1,
FirstAddr:= 2527,
Quantity:= 1,
ComHandle:= Settings.Port,
TimeOut:= Timeout,
Buffer:= Buffer);
IF SendModbus4.Complete THEN
EndSendStored:=TRUE;
ErrorSend4[5]:= SendModbus4.Exception;
IF ErrorSend4[5] = 0 THEN
SuperHeat1:= SuperHeat*100 + Buffer[1];
TRV1Alarm:= FALSE;
ELSE
SuperHeat1:= 16#FFFF;
EndSendError:= TRUE;
Stage:= 8;
END_IF;
END_IF;
END_CASE;
ES:= FALSE;
ELSE
IF EndSendError THEN
EndSendError:= FALSE;
ES:= TRUE;
EndSendStored:= FALSE;
ELSE
HavePause(IN:= EndSendStored,PT:=T#40ms);
IF HavePause.Q THEN
ES:= TRUE;
EndSendStored:= FALSE;
IF Stage >= 7 THEN
Stage:= 1;
ELSE
Stage:= Stage+1;
END_IF;
END_IF;
END_IF;
END_IF;
HavePause(IN:= EndSendStored,PT:=T#40ms);
END_IF;

Daedal
18.06.2017, 21:22
Например, вот, если интересует механизм работы с бибкой

Спасибо!
Смотрю что и как надо было мне делать.

Daedal
18.06.2017, 22:54
Смотрю-смотрю. Ничего не понимаю. Картинка приложенная в п#1 соответствует выходному сигналу автора. А вопрос-то в чем ?

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

capzap
18.06.2017, 23:03
Это мне известно и мною выполняется..
Если бы выполняли, было бы также как через конфигуратор, у Вас в обоих поу производятся действия с глобальной переменной

Daedal
18.06.2017, 23:05
С какого это перепугу-то ?

Ps
причем тут вообще модбасы-фигасы, таймеры-шмайнеры ? В какие-то прерывания занесло..
Лезутъ куда - сами не знаютъ куда, а преобразованиями-то и совладать и не могутъ.

А что такого страшного в прерываниях?

В чём конкрентно у меня ошибка-то?

Тот же самый код формирования пилы(смешно даже говорить о каком-то там коде пилы), но переданный модбасом, настроенным через конфигуратор ПЛК, прекрасно выдаёт на выходе МУ110-6У пилу БЕЗ ступенек.

Daedal
18.06.2017, 23:15
Дык в преобразованиях, мил человек, сказал же уже.
Вот скажи мне, откель докель сигнал-то твой болтаецца ?

от 0 до 1000 болтается.

Нашёл!

Нельзя было делить на 256..
т.е. .да, получается, что переменная в прерывании инкрементируется, а в основном теле делится...

Ой, блииин..
Я это деление вообще не рассматривал.

Спасибо всем ещё раз!;)

Daedal
18.06.2017, 23:20
О-о. А теперь скажи мне - как ты литру в поллитру запихаешь ?

Вот тут не скажу, не потребляю. А вообще, сжижать надо, тогда залезет.:D

А если серьёзно, то я ещё поменял REAL на WORD и дело было в этом, а не в делении.

Daedal
18.06.2017, 23:30
А зря. Тогда б просто взял тару поболее. Тут ведь в чем вопрос-то. Ну не лезет тыща в байт. Хоть убей.

А в байт никто и не пихает. Пихаю в 2 байта. WORD же тоже не 1 байт, а 2. И он заходит прекрасно.

Daedal
18.06.2017, 23:31
Да ни хрена ты не нашел.
Видимо завтра опять зайдешь )))

ЗЫ
Опять какие-то прерывания.

В посте №41 я уже исправился на этот счёт..нашёл я, нашёл.

Daedal
18.06.2017, 23:40
Как это никто. Вот одел очки и вижу

tx_arr[0] := REAL_TO_BYTE(DACcode /256);
tx_arr[1] := REAL_TO_BYTE(DACcode);
Кто это тогда пихает ?


------------

Не поспеваю за молодыми и горячими..

Так. А почему тогда прекрасно работает вот это:


tx_arr[0] := WORD_TO_BYTE(DACcode /256);
tx_arr[1] := WORD_TO_BYTE(DACcode);

В этом случае у меня переменные WORD, а не REAL;
и даже если я делаю с этими WORD переменными


tx_arr[0] := REAL_TO_BYTE(DACcode /256);
tx_arr[1] := REAL_TO_BYTE(DACcode);

то тоже всё работает..

Daedal
18.06.2017, 23:47
Мдя.... сказка продолжается.
А вот эта откуда ?

DAC_code : REAL;
ведь с этим вот это

германская механизьма не даст работать

DAC_code тоже переделана в WORD, конечно же. Вы ведь смотрите на исходную версию кода, я же сейчас правлю и пишу.))

Daedal
18.06.2017, 23:50
Т.е. сейчас вопрос сводится к преобразованию типов..
Мне всегда казалось, что преобразование


tx_arr[0] := WORD_TO_BYTE(DACcode /256);

пихает не всё,а только старший байт из двух, т.к. деление на 256 - это фактически сдвиг на 8 бит вправо.
т.е. я не пихаю все в байт, а пихаю старший байт слова в байт, т.к. массив байтовый.

А


tx_arr[1] := WORD_TO_BYTE(DACcode);

просто отрезает младший байт и пихает в массив.

Но почему такое не проходит с переменными типа REAL я не понимаю.

Daedal
19.06.2017, 00:32
Дык ведь целочисленная математика..


И тута тоже - целочисленная.. Пока байтом не станет. А потом становится плавающей.
А потом по просьбе (REAL_TO_BYTE) сызнова байт. А так байт байтом и был - все замечательно и влезает в байт. Это ж очевидно ))

Т.е. Вы хотите сказать, что когда я делаю DACcode := DACcode + 1 при этом DACcode у меня REAL, то это делается c данными формата плавающей точки?
Если да, то почему тогда на выходе именно ступеньки, причём масштаб(шкала по Y) той части, которая без ступенек, верный? Там ведь должна быть вообще абракадабра,т.к. формат плавающей точки не похож на обычный.

Daedal
19.06.2017, 00:34
А какое - такое ? Разве формат хранения плавающих совпадает форматом хранения целочисленных ?

PS
А с реалом нужно было сделать всего-то :
.. := word_to_byte(real_to_word(..) / 256);
.. := word_to_byte(real_to_word(..) );

Хм...а почему нельзя сразу-то REAL_TO_BYTE ?

capzap
19.06.2017, 07:29
следующим постом я и хотел начать с этих присвоений массиву(это по поводу опытности), хотел добраться до КДС и сперва сам проверить, но ночью всё уже разрешилось само собой, только не пойму зачем две страницы тратить на преобразование _TO_ , хоть бы словечко про указатель