Просмотр полной версии : SysLibCom
ribamuka
11.07.2017, 01:44
Существует библиотека SysLibCom для CodeSys 3.5 ?
Если да, то где ее скачать?
Евгений Кислов
11.07.2017, 06:54
Существует библиотека SysLibCom для CodeSys 3.5 ?
Если да, то где ее скачать?
В состав CODESYS 3.5 входит несколько библиотек для работы с COM-портом - в частности, SysCom (аналог SysLibCom c приблизительно тем же набором функций/ФБ) и CAA SerialCom. Скачивать ничего не надо, просто добавьте их в Менеджере библиотек.
Пример работы с CAA SerialCom приведен в документе СПК. Реализация нестандартных протоколов:
http://ftp.owen.ru/index.html/CoDeSys3/11_Documentation/01_SPK/SPK_NonstandardProtocols_v.1.0.pdf
ribamuka
11.07.2017, 08:42
Спасибо за подсказку
ВладОвен
11.08.2022, 17:12
Здравствуйте.
А подскажите, как работать с дескриптором открываемого порта на языке ST? (библиотека CAA SerialCom).
При открытии порта мы должны получить дескриптор:
myComOpen(usiListLength := SIZEOF(arrParams)/SIZEOF(COM.PARAMETER), pParameterList := ADR(arrParams));
А при отправке в порт дескриптор использовать:
myComWrite(xExecute := TRUE, udiTimeOut := 1000, hCom := myComOpen.hCom, pBuffer := ADR(arrMassive), szSize := 5);
Правильно ли я его использую? В порт ничего не пишется.
Что я делаю не так?
UP: Почему этот hCom всегда равен 0? Пробовал разные порты.
Скрин:
62107
Евгений Кислов
11.08.2022, 17:51
Потому что вы не дожидаетесь открытия порта (myComOpen.xDone).
Изучите этот документ:
https://ftp.owen.ru/CoDeSys3/11_Documentation/03_3.5.11.5/CDSv3.5_UserProtocols_v.3.0.pdf
ВладОвен
12.08.2022, 09:35
Потому что вы не дожидаетесь открытия порта (myComOpen.xDone).
Хм... Странно.
Декларация:
PROGRAM PLC_PRG
VAR
xOpen: BOOL := FALSE;
myHandle : COM.CAA.HANDLE;
myError : ERROR;
xError: BOOL := FALSE;
xSucces: BOOL := FALSE;
myComOpen : COM.Open;
arrParams : ARRAY [1..7] OF COM.PARAMETER := [
(udiParameterId := COM.CAA_Parameter_Constants.udiPort, udiValue := 1),
(udiParameterId := COM.CAA_Parameter_Constants.udiBaudrate, udiValue := 115200),
(udiParameterId := COM.CAA_Parameter_Constants.udiParity, udiValue := 2), // no parity
(udiParameterId := COM.CAA_Parameter_Constants.udiStopBits, udiValue := 0), // 1 stop
(udiParameterId := COM.CAA_Parameter_Constants.udiTimeout, udiValue := 0),
(udiParameterId := COM.CAA_Parameter_Constants.udiByteSize, udiValue := 8),
(udiParameterId := COM.CAA_Parameter_Constants.udiBinary, udiValue := 1)
];
END_VAR
Реализация:
IF xOpen THEN
xOpen := FALSE;
myComOpen(usiListLength := SIZEOF(arrParams)/SIZEOF(COM.PARAMETER), pParameterList := ADR(arrParams));
END_IF
myHandle := myComOpen.hCom;
xError := myComOpen.xError;
myError := myComopen.eError;
xSucces := myComOpen.xDone;
Я нажимаю на клавишу на экране визуализации, которая устанавливает флаг xOpen.
А далее долго-долго наблюдаю за 4-я параметрами. И они не меняются...
Скрин:
62112
Но ничего так и не происходит...
Пробовал перебирать порты - результата нет.
Может есть проект на ST для образца?
Евгений Кислов
12.08.2022, 09:42
Может есть проект на ST для образца?
См. ссылку в моем прошлом посте.
ВладОвен
12.08.2022, 11:56
В общем тестировал библиотеку CAA SerialCom, перебирал порты, курил доки Овена.
Попытка открыть порт приводит к состоянию флагов:
62118
Что мы видим в этом окне:
xDone - никогда не устанавливается,
xError - никогда не устанавливается,
xBusy - всегда установлен.
Какой мы вывод делаем:
Открытие порта не заканчивается
Ошибок не возникает
Процесс идет бесконечно долго.
Евгений Кислов
12.08.2022, 12:01
Я могу порекомендовать только внимательнее "курить доки".
IF xOpen THEN
xOpen := FALSE;
myComOpen(usiListLength := SIZEOF(arrParams)/SIZEOF(COM.PARAMETER), pParameterList := ADR(arrParams));
END_IF
У вас xOpen взводится на один цикл и myComOpen тоже вызывается только один цикл.
За один цикл порт не успевает открыться, и блок зависает в xBusy.
Обычно делают так:
myComOpen(xExecute := xOpen, usiListLength := SIZEOF(arrParams)/SIZEOF(COM.PARAMETER), pParameterList := ADR(arrParams));
IF myComOpen.xDone THEN
myHandle := myComOpen.hCom;
END_IF
Или можно использовать USR_COM_CONTROL из документа - это обертка над COM.Open / COM.Close, которая будет существенно более проста в использовании для начинающих программистов, чем эти блоки.
ВладОвен
12.08.2022, 12:09
Спасибо за помощь.
Но:
Обычно делают так:
Это далеко не обычно!
Грёбаные циклы, которых не хватает за один проход! Кто-бы мог подумать!
Это не объясняется в документации к библиотеке.
ВладОвен
12.08.2022, 16:02
Ну да ладно. Отправка байтов налажена. Теперь есть проблемы с приемником. Пишем в программе:
myComRead(xExecute := TRUE, hCom := myComOpen.hCom, pBuffer := ADR(arrMassiveReceive), szBuffer := 10, udiTimeOut := 1000);
Сую в порт одиночные символы / не сую в порт одиночные символы.
xDone всегда отключен. xBusy всегда включен. Что можете посоветовать начинающему программисту?
Евгений Кислов
12.08.2022, 16:08
Что можете посоветовать начинающему программисту?
Как и раньше - внимательно изучить документ по ссылке из поста #5 (https://owen.ru/forum/showthread.php?t=27053&p=386678&viewfull=1#post386678).
ВладОвен
12.08.2022, 16:42
Как и раньше - внимательно изучить документ по ссылке из поста #5 (https://owen.ru/forum/showthread.php?t=27053&p=386678&viewfull=1#post386678).
Нет.
У меня вопрос!
В блоке Read есть параметр udiTimeOut. Он определяет время (толи в mS, толи в uS) через которое произойдет таймаут по приему.
Я записал туда число 10 000 000.
Что произойдет через этих 10 миллионов попугаев? Какой флаг должен установиться через это время? Что должно измениться?
Евгений Кислов
12.08.2022, 17:04
В блоке Read есть параметр udiTimeOut. Он определяет время (толи в mS, толи в uS) через которое произойдет таймаут по приему.
?
Это не так. Это таймаут доступа к буферу COM-порта.
Что произойдет через этих 10 миллионов попугаев? Какой флаг должен установиться через это время? Что должно измениться?
Если за это время не случится xDone или какая-то другая ошибка, то:
xError - TRUE
eError - COM.ERROR.TIME_OUT
ВладОвен
12.08.2022, 17:29
Правильно ли я понимаю, что функциональные блоки Write и Read реализованы по разному (что затрудняет их понимание и читаемость кода)?
1. Ф-блок Write не отдаёт xDone до тех пор, пока физически не отдаст последний байт линию COM-порта. Я пробовал на скорости 1200 отдать 255 байтов. Ф-блок Write блокирует программу более 2-х секунд. Реализовано хорошо: исключается наложение отдаваемых пакетов (потому-что блокируется программа).
2. Ф-блок Read отдаёт xDone сразу же, как только физически добрался до буфера. Придется сталкиваться с пустыми массивами, закольцованными массивами (придется проверять это). Параметр udiTimeOut выполняет непонятную своим смыслом функцию. Этот параметр должен был блокировать программу на указанное время, а после отдавать xDone, размер принятых байт (szSize) и принятый буфер (arrBuffer)! И ненужно было бы вводить свои кастомные таймера для таймаута, код был бы понятнее.
Ну а если нельзя блокировать программу, то зачем это сделано в ф-блоке Write? Он тоже мог бы тупо перекидывать в буфер и сразу же отдавать xDone? Пусть байты вылетают из порта своим ходом (хардовым способом).
В общем: Write блочит прогу, Read не блочит прогу.
PS: А еще обратите внимание, что параметр szSize отдает не количество принятых байт, а указывает на последний байт в кольцевом (!) буфере приема (arrBuffer). И если принятая посылка будет больше буфера, то вы получите недостоверное значение.
Евгений Кислов
12.08.2022, 19:42
Придется сталкиваться с пустыми массивами, закольцованными массивами (придется проверять это).
Можно, наверное, и так.
Или можно почитать документ, посмотреть пример из него - и обойтись без всех этих столкновений.
Впрочем, я понимаю, что вы не ищите легких путей.
Этот параметр должен был блокировать программу на указанное время, а после отдавать xDone, размер принятых байт (szSize) и принятый буфер (arrBuffer)!
Должен кому?
Библиотека CAA SerialCom включает в себя асинхронные ФБ, которые не блокируют задачу, в которой вызываются.
В этом их преимущество - сохранение стабильного времени цикла без лишних джиттеров.
Для любителей "блокировать программу" - есть библиотека SysCom.
Он тоже мог бы тупо перекидывать в буфер и сразу же отдавать xDone? Пусть байты вылетают из порта своим ходом (хардовым способом).
Тогда другой пользователь спрашивал бы: почему после отправки запроса я читаю приемный буфер и вижу в нем свои же отправленные байты?
В общем, так себе решение.
И если принятая посылка будет больше буфера, то вы получите недостоверное значение.
А если читать документацию и разумно подходить к написанию кода - то таких проблем не будет.
ВладОвен
12.08.2022, 20:59
Ок. Спасибо за разъяснения.
Получается, что эта библиотека асинхронная. По задумке не блокирует программу.
Но отправка пакета реализована с блокировкой из-за того, что-бы не ловить свои-же байты.
ВладОвен
17.08.2022, 13:56
Евгений, здравствуйте.
А можете объяснить почему не срабатывает такая конструкция при открытии порта?
WHILE NOT myComOpen.xDone DO
myComOpen(xExecute := TRUE, usiListLength := SIZEOF(arrParams)/SIZEOF(COM.PARAMETER), pParameterList := ADR(arrParams));
END_WHILE
Правильно ли я понимаю, что из-за того, что я блокирую программу?
Евгений, здравствуйте.
А можете объяснить почему не срабатывает такая конструкция при открытии порта?
WHILE NOT myComOpen.xDone DO
myComOpen(xExecute := TRUE, usiListLength := SIZEOF(arrParams)/SIZEOF(COM.PARAMETER), pParameterList := ADR(arrParams));
END_WHILE
Правильно ли я понимаю, что из-за того, что я блокирую программу?
потому что while выполняется в рамках одного цикла, а необходимо несколько циклов плк, чтоб получить обмен с аппаратным сом-портом
ВладОвен
26.08.2022, 18:05
Привет.
Помогите решить такую проблему:
Мне нужно получить посылку длинной 39 байт. Посылка должная прийти асинхронно. Т.е. чёрт знает когда.
Мне приходится постоянно циклически слушать порт:
CASE bStep OF
00: ...
01: // Принимаем пакет
myComRead(xExecute := TRUE, hcom := hCom, pBuffer := ADR(arrReceive), szBuffer := 255, udiTimeOut := 0);
IF myComRead.xDone THEN
bStep : = 2;
END_IF
02: // Проверяем длину принятого пакета
IF myComRead.szSize = 0 THEN // Нулевая длина: уходим в начало
myComRead(xExecute := FALSE);
bStep := 1;
ELSE
bStep := 4;
END_IF
04: ...
END_CASE
Пакет принимается, но иногда поломанный. Я записывал длину каждого принятого пакета в массив и увидел вот что:
arrBufferLen[] = 0, 0, 29, 10, 0, 0, 0, 0, 0, 0, 0
Т.е. принимаемые 39 байт разбились на отдельные пакеты длиной 29 и 10 байт.
Очевидно, что это происходит из-за рассинхронизации процессов.
Как этого избежать? При этом длина входного пакета всегда разная.
ВладОвен Мне Евгений Кислов помогал с подобным примером.
Судя по этому примеру (и реальной жизни) такой принцип работы - норма (а я тогда как глупый как раз и думал, что все мои байты придут за один раз, и из-за мой код не хотел работать).
В примере Евгения Кислова был такой алгоритм-принцип: сколько байт получили - столько и склеиваем в один общий массив байт, пока не наберём нужную длину посылки.
После этого посылка идёт на обработку (проверка корректности и прочего).
Если посылка верная - то что-то с ней делаем.
Если ошибочная (или в течение таймаута не пришло заданное число байт) - обнуляем буфер, счётчик принятых байт, и начинаем всё сначала.
Добавил: если там длина неизвестна - то так по байтам и клеить и парсить на лету (искать заголовок или какую-то инфу, которая покажет о том, что началась новая посылка данных). Скажем, так: приняли байт - запустили таймер TOF. Если были байты, пока он ещё считает - то значит это наша посылка. А если байтов не было - то посылка кончилась.
ВладОвен
29.08.2022, 11:21
Привет.
Решил эту задачу. Пока работает нормально.
Я просто каждый раз после полученного пакета N1 (длинна не равна 0) сразу же принимаю второй пакет и если его длинна N2 не равна 0 , то я их склеиваю:
CASE bStep OF
01: ...
02: // Ожидаем пакет (N1)
myComRead(xExecute := TRUE, hcom := hCom, pBuffer := ADR(arrBufferN1), szBuffer := 255, udiTimeOut := 0);
IF myComRead.xDone THEN
bStep := 3;
END_IF
03: // Проверяем длину принятого пакета (N1)
IF myComRead.szSize = 0 THEN // Нулевая длина
myComRead(xExecute := FALSE);
bStep := 2; // Повторно ожидаем пакет (N1)
END_IF
ELSE
udiSizeN1 := myComRead.szSize; // Длинна пакета (N1) была не нулевой
myComRead(xExecute := FALSE);
bStep := 04;
END_IF
04: // Принимаем пакет (N2)
myComRead(xExecute := TRUE, hcom := hCom, pBuffer := ADR(arrBufferN2), szBuffer := 255, udiTimeOut := 0);
IF myComRead.xDone THEN
bStep := 5;
END_IF
06: // Проверяем длину принятого пакета (N2)
IF myComRead.szSize <> 0 THEN // Длина не нулевая. Значит пакет был разбил на два отдельных. Склеиваем их.
udiSizeN2 := myComRead.szSize;
MEM.MemMove(ADR(arrBufferN2), ADR(arrBufferN1) + udiSizeN1, UDINT_TO_UINT(udiSizeN2));
END_IF
myComRead(xExecute := FALSE);
bStep := 5;
05: ...
END_CASE
ВладОвен Приятно посмотреть на код: везде комментарии и, если со стороны читать, то всё понятно. Я такое люблю.
Ты не занимаешься уходом от магических чисел (это программистский термин (https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D 0%BE%D0%B5_%D1%87%D0%B8%D1%81%D0%BB%D0%BE_(%D0%BF% D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1 %80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)))? Не хочу навязывать, но я все шаги именую через константы, то есть:
VAR_CONSTANT
wStep_Wait : WORD := 0;
wStep_Recieve : WORD := 1;
wStep_Process : WORD := 2;
...
END_VAR
Так код и Case выглядят лучше, чем цифирки: с цифирками легко запутаться, когда добавляешь или удаляешь шаги.
А ещё хотел спросить: у тебя в коде есть защита от ухода за границы буфера?
Вот в этом месте
ADR(arrBufferN1) + udiSizeN1
у тебя есть защита от того, чтобы этот адрес не ушёл за границы длины arrBufferN1?
Про такие вещи надо думать, иначе потом они могут вызывать сбой программы.
ВладОвен Приятно посмотреть на код: везде комментарии и, если со стороны читать, то всё понятно. Я такое люблю.
Ты не занимаешься уходом от магических чисел (это программистский термин (https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D 0%BE%D0%B5_%D1%87%D0%B8%D1%81%D0%BB%D0%BE_(%D0%BF% D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1 %80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)))? Не хочу навязывать, но я все шаги именую через константы, то есть:
VAR_CONSTANT
wStep_Wait : WORD := 0;
wStep_Recieve : WORD := 1;
wStep_Process : WORD := 2;
...
END_VAR
Так код и Case выглядят лучше, чем цифирки: с цифирками легко запутаться, когда добавляешь или удаляешь шаги.
Ещё удобно использовать перечисления (создать пользовательский тип данных) например:
TYPE enStateMotor : (
gc_iMotorStopped := 0 ,
gc_iMotorStopping := 1 ,
gc_iMotorBlocked := 2 ,
gc_iMotorErrStart := 3 ,
gc_iMotorWork := 4 ,
gc_iMotorStarting := 5 ,
gc_iMotorNoFeedback := 6
);
END_TYPE
При этом с типом enStateMotor можно работать как с INT
1exan Ага, я про перечисления знаю, но не хочу их использовать, так как перечисления внутри FB не объявишь, а у меня чаще всего автоматы состояний находятся и работают в FB. Мне хочется, чтобы FB был максимально самодостаточен и при копировании его по другим проектам не надо было ещё и DUT тащить.
Поэтому внутри FB мне удобнее константы сделать.
Если я не прав и внутри FB можно перечисление объявить - прошу меня поправить.
Евгений Кислов
30.08.2022, 08:14
Если я не прав и внутри FB можно перечисление объявить - прошу меня поправить.
Есть такая недокументированная (на данный момент) фича:
https://owen.ru/forum/showthread.php?t=28167&p=369251&viewfull=1#post369251
1exan Ага, я про перечисления знаю, но не хочу их использовать, так как перечисления внутри FB не объявишь, а у меня чаще всего автоматы состояний находятся и работают в FB. Мне хочется, чтобы FB был максимально самодостаточен и при копировании его по другим проектам не надо было ещё и DUT тащить.
Поэтому внутри FB мне удобнее константы сделать.
Если я не прав и внутри FB можно перечисление объявить - прошу меня поправить.
Можно, это же получается такой-же тип данных как BOOL, WORD - можно использовать и внутри блоков.
Но тут надо не забывать если переносишь блок в другую программу, то и перечисление, используемое внутри тоже надо переносить. (если через импорт-экспорт надо выделить и блок и свой тип данных при экспорте).
UPD:
Можно - это я конечно имел в виду использование, а не объявление перечисления. Про фичу не знал.
Перечисления удобны, если переменная состояния FB используется и снаружи FB.
1exan Вот, сначала пишем, потом читаем =) Я говорил о FB, так как снаружи это наоборот - не должно быть доступно.
Евгений Кислов О! СПАСИБО!
Хм... как бы уже придумать то, как этот FAQ распечатать со всех страниц сразу в PDF. Там сведений куча, а не все упомнишь!
Сделал пока себе скриншот ответа!
ВладОвен
30.08.2022, 10:30
ВладОвен, ... Ты не занимаешься уходом от магических чисел (это программистский термин (https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D0%B3%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D 0%BE%D0%B5_%D1%87%D0%B8%D1%81%D0%BB%D0%BE_(%D0%BF% D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1 %80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)))? Не хочу навязывать, но я все шаги именую через константы ...
Да. Этот способ тоже хорош. Но в этом проекте я решил пока от этого отказаться.
Потому что последовательность очень длинная - на 100 шагов. Я просто боюсь, что потом запутаюсь в последовательности действий.
Всё таки: числа дают последовательность перечисления, а слова - нет.
Т.е. мы всегда знаем, что после шага 56 идет шаг 57, а потом и шаг 58.
И наоборот, если со словами: после startZeroOperation идет waitCountOffset? Или waitCoeffView? Или, может быть, waitJeater? А, вспомнил! Потом идет goStableFlow! (но это не точно...).
Но с числами есть недостаток: если понадобится вставить доп.промежуточный шаг в будущем, то придется сдвигать все числа вручную. И это неудобно.
Наверное, оптимально делать всё же перечисление, но такого формата:
10_startZeroOperation,
20_waitCountOffset,
30_waitCoeffView,
40_waitJeater,
50_goStableFlow.
В этом случае и понятна последовательность шагов (за счет чисел), и смысл шага (за счет слов), и вставить доп.промежуточный шаг можно за счет предварительного пропуска чисел (например, 35_smokeDocsOwen).
Да. Этот способ тоже хорош. Но в этом проекте я решил пока от этого отказаться.
Потому, что последовательность очень длинная - на 100 шагов. Я просто боюсь, что потом запутаюсь в последовательности действий.
Всё таки: числа дают последовательность перечисления, а слова - нет.
Т.е. мы всегда знаем, что после шага 56 идет шаг 57, а потом и шаг 58.
И наоборот, если со словами: после startZeroOperation идет waitCountOffset? Или waitCoeffView? Или, может быть, waitJeater? А, вспомнил! Потом идет goStableFlow! (но это не точно...).
Но с числами есть недостаток: если понадобится вставить доп.промежуточный шаг в будущем, то придется сдвигать все числа вручную. И это неудобно.
Наверное, оптимально делать всё же перечисление, но такого формата:
10_startZeroOperation,
20_waitCountOffset,
30_waitCoeffView,
40_waitJeater,
50_goStableFlow.
В этом случае и понятна последовательность шагов (за счет чисел), и смысл шага (за счет слов), и вставить доп.промежуточный шаг можно за счет предварительного пропуска чисел (например, 35_waitSmokeDocsOwen).
Я конечно не знаю как у вас организована программа, но я в таких случаях делаю так:
Если мне надо добавить шаг, я просто вставляю его в нужное место перечисления и перенумеровываю значения заново (в екселе - делается за 1 сек если разделители - табуляция):
Например:
Было
TYPE enStateMotor : (
gc_iMotorStopped := 0 ,
gc_iMotorStopping := 1 ,
gc_iMotorBlocked := 2 ,
gc_iMotorErrStart := 3 ,
gc_iMotorWork := 4 ,
gc_iMotorStarting := 5 ,
gc_iMotorNoFeedback := 6
);
END_TYPE
Стало:
TYPE enStateMotor : (
gc_iMotorStopped := 0 ,
gc_iMotorStopping := 1 ,
gc_iMotorBlocked := 2 ,
gc_iMotor_NEW_STATE := 3 ,
gc_iMotorErrStart := 4 ,
gc_iMotorWork := 5 ,
gc_iMotorStarting := 6 ,
gc_iMotorNoFeedback := 7
);
END_TYPE
При этом оператор
CASE State OF
gc_iMotorStopped..gc_iMotorWork, gc_iMotorNoFeedback:
...
и конструкции типа
State > gc_iMotorStopped AND State < gc_iMotorNoFeedback
ну и т.д. не перестают работать
ВладОвен, я имел ввиду что и в CASE шаги тоже называются по имени. Ща, сделаю скриншот, покажу как у меня в FB окучено.
62361 62362
А если ты хочешь прикалываться, то так тебе надо не под каждый пакет свой шаг мутить, а, ИМХО, в этом конечном автомате ловить сам пакет, а потом отсылать его на какую-нить функцию типа "ProcessData()", которая и будет соображать, что там за данные пришли и что с ними сделать.
В случае чего можно даже очередь пакетов сделать =)
ВладОвен
03.10.2022, 09:52
Привет.
Скажите, а как будут работать два устройства, которые висят на одном порту, но одно из них работает на ModBus-Slave, а другое - по нестандартному протоколу (библиотека SysLibCom)?
Будут ли конфликты?
Евгений Кислов
03.10.2022, 09:56
Привет.
Скажите, а как будут работать два устройства, которые висят на одном порту, но одно из них работает на ModBus-Slave, а другое - по нестандартному протоколу (библиотека SysLibCom)?
Будут ли конфликты?
Добрый день.
Если опрос полностью реализован в вашем коде - то конфликтов не будет (если, конечно, вы сами их не создадите).
ВладОвен
03.10.2022, 16:16
Если опрос полностью реализован в вашем коде - то конфликтов не будет.
Вы имеете ввиду, что и ModBus и SysLibCom я реализую программно в коде?
Обмен через SysLibCom я реализовал в программе, а ModBus реализуется внутренним автоматом CodeSys.
Евгений Кислов
03.10.2022, 16:21
Вы имеете ввиду, что и ModBus и SysLibCom я реализую программно в коде?
Обмен через SysLibCom я реализовал в программе, а ModBus реализуется внутренним автоматом CodeSys.
Не думаю, что это получится - стандартный компонент Modbus (который вы называете "внутренним автоматом") займет COM-порт, и вы уже не сможете открыть его с помощью SysComOpen.
ВладОвен
03.10.2022, 16:38
Так и есть: порт не может быть открыт.
ВладОвен
05.10.2022, 11:48
Привет.
Можно ли организовать обмен информацией с двумя устройствами на одной шине RS485, НО меняя частоту обмена на лету: 115200 и 9600 ?
Так получается, потому что у меня есть один датчик, и у него частота только 9600, а все другие устройства работают на максимальной - 115200.
а) Для стандартного компонента ModBus.
б) Для нестандартного протокола.
Евгений Кислов
05.10.2022, 11:52
Привет.
Можно ли организовать обмен информацией с двумя устройствами на одной шине RS485, НО меняя частоту обмена на лету: 115200 и 9600 ?
Так получается, потому что у меня есть один датчик, и у него частота только 9600, а все другие устройства работают на максимальной - 115200.
а) Для стандартного компонента ModBus.
б) Для нестандартного протокола.
Добрый день.
Уточните - у вас на одной шине устройство с Modbus и устройство с нестандартным протоколом?
ВладОвен
05.10.2022, 12:36
Добрый день.
Уточните - у вас на одной шине устройство с Modbus и устройство с нестандартным протоколом?
Нет.
Имеется ввиду: устройства с протоколом ModBus на одной шине, но один тип устройств (8шт.) работает только на скорости 9600, а другой тип устройств (16шт.) - на скорости 115200.
Но, давайте еще рассмотрим аналогичный вопрос: устройства с нестандартным протоколом на одной шине, но один тип устройств (8шт.) работает только на скорости 9600, а другой тип устройств (16шт.) - на скорости 115200.
Евгений Кислов
05.10.2022, 12:39
Нет.
Имеется ввиду: устройства с протоколом ModBus на одной шине, но один тип устройств (8шт.) работает только на скорости 9600, а другой тип устройств (16шт.) - на скорости 115200.
Но, давайте еще рассмотрим аналогичный вопрос: устройства с нестандартным протоколом на одной шине, но один тип устройств (8шт.) работает только на скорости 9600, а другой тип устройств (16шт.) - на скорости 115200.
a) Можно
б) Можно
ВладОвен
05.10.2022, 14:14
a) Можно
б) Можно
Супер!
Так а как скорости-то переключать на ходу?
Евгений Кислов
05.10.2022, 14:18
Супер!
Так а как скорости-то переключать на ходу?
a) Переводите все каналы опроса в режим Передний фронт или Приложение.
Когда нужно переключить скорости - останавливаете опрос каналов.
Изменяете настройки COM-порта согласно рисунку 4.6.6 и информации рядом с ним: https://ftp.owen.ru/CoDeSys3/11_Documentation/03_3.5.11.5/CDSv3.5_Modbus_v3.0.pdf
б) В этом случае обмен настраивается через библиотечные ФБ. Соответственно, в нужный момент времени останавливаете опрос, закрываете COM-порт, открываете его с новыми настройками и продолжаете опрос.
ВладОвен
05.10.2022, 16:22
Спасибо. За разъяснения.
ВладОвен
23.12.2022, 17:51
Привет. Попробовал построить обмен через Com-порт но на языке CFC.
1. Как вам идея реализации на этом языке?
2. Правильно ли использовал элемент move для перехода на второй шаг автомата и для копирования указателя hCom?
См. рисунок:
64783
Почему я об этом спросил? Просто я заметил, что элемент с EN-ENO имеет определенное "западло". Оказывается, что при отсутствующем сигнале EN элемент не обнуляется, а тупо фиксирует свои выходы в последнем состоянии (т.е. работает по спаду на входе EN).
А зачем так делать? Чем ST не подходит?
ВладОвен
23.12.2022, 18:08
Ну для понимания возможностей этого языка. Надо же попробовать.
В конце концов в обучающих видео от Овена часто используют этот язык. (Видели? Там блондинка сидит и пикает сканером штрих-кода :))
Ну для понимания возможностей этого языка. Надо же попробовать.
В конце концов в обучающих видео от Овена часто используют этот язык. (Видели? Там блондинка сидит и пикает сканером штрих-кода :))
Да ну... на ST удобнее. Мне Евгений Кислов помогал с примером, а я сам - бывший СИшный программист. Вот на ST втянулся.
Блондинка? Неа... Мне всегда нравился фетиш- и готик стайл и брюнетки =)
ВладОвен
02.03.2023, 15:53
Привет.
Подскажите, а можно ли принять пакет из порта не в массив, а в строку?
Т.е.
Не так:
myComRead(xExecute:=TRUE, hcom:=myhCom, pBuffer:=ADR(arrReceive1), szBuffer:=255, udiTimeOut:=0);
А так:
myComRead(xExecute:=TRUE, hcom:=myhCom, pBuffer:=ADR(sReceive1), szBuffer:=SIZEOF(81), udiTimeOut:=0);
У меня так не получается.
В массив принимает данные, а в строку - не принимает.
Спасибо.
Евгений Кислов
02.03.2023, 15:55
Добрый день.
Да, можно.
szBuffer:=SIZEOF(81)
^Здесь, очевидно, ошибка.
Нужно так:
szBuffer:=SIZEOF(sReceive1)
ВладОвен
02.03.2023, 16:26
Хм. Исправил, но приема нет.
Если принимать в буфер, то принимает норм.
См.рисунок.
66266
ВладОвен
02.03.2023, 16:33
Походу разобрался.
Нужно делать так:
sReceive2 := sReceive1;
myComRead(xExecute := FALSE);
Потому как при eXecute:=FALSE эта строка обнуляется и мы ее не видим.
ВладОвен
02.03.2023, 21:04
А подскажите, как добавить в конец строки спец.символ?
Пробую так:
sSend := concat(sSend, CHR_TO_STRING(16#0D))
При этом нужно подключить библиотеку BASIC(OSCAT)
А потом возникает ошибка 'Неоднозначное пространство имен 'STANDARD' задано библиотекой 'Standard, 3.5.17.0 (System)'
Как это побороть?
Евгений Кислов
02.03.2023, 21:26
sSend := CONCAT(sSend, '$0D');
Powered by vBulletin® Version 4.2.3 Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot