Ian Я его ща пишу под один проект. Могу примерно показать как делать.
Ещё сам разбираюсь, но Валенку ОГРОМНОЕ пасибо, что когда-то меня в него ткнул носом!! Тема - охеренская!!
Краткая суть такая (кто-то это может вынести в FAQ?):
1. CodeSys v2 не умеет делать групповые запросы (это когда ему можно сказать: "Прочитай ВСЕ регистры с 0 по 68й разом").
Это плохо тем, что в протоколе Modbus (то, как там идёт общение с модулями IO и другими устройствами) много байт уходит на заголовок и коннец запроса. Образно: "я такой-то, ща начну передавать столько-то данных (два байта), как понял, приём" =)
Каждая позиция в дереве конфигурации ПЛК - это один такой запрос (Register .. module, Float .. module и так далее).
Поэтому решение, когда для каждого канала модуля мы пихаем в конфигурацию ПЛК отдельны ..module - херовое.
2. Сам протокол Modbus имеет команду "прочитать столько-то регистров подряд", и ОВЕНонвские модули её поддерживают.
А CodeSys v2 умеет посылать такие команды при помощи String Input (или Output) Module.
Вот Валенок всем и предлагает использовать эту фичу. Мне понравилось до чертей в животе =))
Суть такая: String ... module читает БАЙТЫ (не регистры!!!) столько, сколько укажешь.
В его настройках выставляем так:
Command = Read Holding Registers (0x03)
Register address = адрес начального регистра, с которого будем читать
Amounth byte = сколько БАЙТ надо прочитать.
Важно:
* Максимальная длина одного String ... module = 79 байтов (не более). Поэтому если нам надо прочитать больше - придётся разбивать это на два или три String...module, но это будет быстрее чем куча запросов по кусочкам.
* String ... module читает БАЙТЫ. Поэтому надо помнить, что один регистр Modbus - это ДВА байта. И пересчитывать всё по байтам. Ниже я покажу свои пересчёты.
3. После того, как мы создали такие фиговины в конфигурации ПЛК, нам нужен доступ к этому STRING.
Тут опять есть замутки с CodeSys v2: просто так обратиться к байтам этой STRING[79] у меня не получилось.
Народ предлагает делать так: узнать адрес, по которому находится этот STRING в конфигурации ПЛК (AT %xxxxx) и вручную в коде программы назначить ему переменную вида ARRAY [1..скокабайт] OF BYTE;
Мне это не нравится: я не люблю ручное назначение адресов.
Поэтому я назначил в конфигурации ПЛК нормальную переменную (ниже будет показано для модулей измерения электросети), а дальше через SysMemCpy копирую её в свой буфер из байтов.
Так вот дальше будет немного гимора: надо вручную из байтов склеить нужные значения регистров.
Я сделал такое для измерительного модуля, и ща на его примере покажу с пояснениями.
ИТАК, как это выглядит.
1. Я подсчитал реистры для модулей МВ110-224.8А и МЭ110-224.1М.
Прям на БУМАЖКЕ (если буду часто пользоваться - создам XLS-файлик).
Я расписал себе, тупенькому, всё таким образом, что понял где сколько байт мне надо:
MV110-8A.jpg ME110-1M.jpg
Для .8А получилось что надо читать 26 регистров (72 байта) + 12 регистров (24 байта)
А для .1М получилось 21 регистр (42 байта)
Вот я напихал это всё в конфигурацию ПЛК и назначил переменные (для .1М пока что):
RegRead.gif
Дальше (для модулей измерения электросети) сделал структуру (с полями всех параметров, которые модуль меряет), и написал функцию её парсинга.
Код:
(* Тип для описания всех параметров нагрузки одной из фаз сети *)
TYPE CSPhaseInfo :
STRUCT
Voltage : REAL; (* Напряжение, В *)
Amperage : REAL; (* Ток, А *)
PowerFull : REAL; (* Мощность: Полная, ВА *)
PowerActive : REAL; (* Мощность: Активная, Вт *)
PowerReactive : REAL; (* Мощность: Реактивная, Вар *)
PowerFactor : REAL; (* Коэффициент мощности нагрузки *)
Frequency : REAL; (* Измеренная частота сети *)
END_STRUCT
END_TYPE
Вот тут Валенок-то мне щас наподдаст... код там трешовый: я побайтно склеиваю нужные регистры в WORD, а потом делаю над ними операции, чтобы получить верное значение, которое модуль даёт.
ParseFunction.gif
Код:
(* Функция парсит все каналы одного модуля МЭ110-224.1М в структуру данных *)
FUNCTION CSParseME1Ch : CSPhaseInfo
VAR_INPUT
pData : DWORD; (* Указатель (ADR) на переменную канала ПЛК *)
pDataSize : DWORD; (* Максимальная длина данных от модуля *)
END_VAR
VAR
pBuffer : ARRAY [1..64] OF BYTE; (* Вутренний буфер для копирования данных *)
END_VAR
(* Копируем полученный буфер во внутреннюю память, так как CodeSys в конфигурации ПЛК
нее может присвоить STRING[79] в массив байтов штатно *)
SysMemCpy(ADR(pBuffer), pData, pDataSize);
CSParseME1Ch.Voltage := DWORD_TO_REAL(
SHL(BYTE_TO_DWORD(pBuffer[4]), 8 * 3) OR
SHL(BYTE_TO_DWORD(pBuffer[3]), 8 * 2) OR
SHL(BYTE_TO_DWORD(pBuffer[6]), 8) OR
BYTE_TO_DWORD(pBuffer[5])
) / EXPT(10, WORD_TO_REAL(
BYTE_TO_WORD(pBuffer[1]) OR
SHL(BYTE_TO_WORD(pBuffer[2]), 8)
)
);
В итоге модуль - даёт (по 2й и 3й фазам нагрузки нету):
TestPhases.gif
Как-то так! Опрос летает, рядом стоят модули DI/DO - они опрашиваются быстро, ничего не тормозит.
Потом накатаю функцию парсинга каналов для .8А, которая тоже всё будет пихать в структуру и распарсивать эти байты из буферов.