Если вам нужно наоборот — побыть подчиненным, выставляя данные наружу, читайте — Сервер Modbus TCP, про адресацию — Взгляд со стороны клиента ModbusTCP.Для удобства и отсутствия пайки возьмем EL6021, который оснащен клемником, а не D-sub разъемом, хотя это вряд ли имеет значения — все модули организованы одинаково, включая системные, расположенные на "теле" контроллера, такие как -N031. Для RS232 и RS422 все аналогично, кроме линии связи и электрической части.
Для модулей под разъем (D-sub, 9-pin) характерно:
- RS232, разъем на модуле — "папа" (plug), на кабеле — "мама" (socket).
- RS485 / RS422, разъем на модуле — "мама" (socket), на кабеле — "папа" (plug).
Бекхофф пропагандирует первенство программной части, поэтому начнем от программы. Подробно про линковку переменных и прочие пошаговые прохождения квеста можно узнать из русскоязычной методички — Modbus_Step-by-step.pdf
Лицензии и библиотеки
Для TwinCAT 2 библиотека поставляется отдельно и за деньги: TS6255 | TwinCAT PLC Modbus RTU. Результатом будет библиотека TwinCAT — ModbusRTU.lib.
Библиотека для TwinCAT 3 TF6255 | TC3 Modbus RTU — "Already included in the basis setup", — то есть она уже включена в стандартную поставку, но потребует дальнейшего лицензирования при выводе проекта в "поле". Резюмирую: пока пишете проект или "щупаете" возможности библиотеки — можно не покупать и пользоваться возобновляемой семидневной лицензией. После готовности проекта, заказываете лицензию и активируете ее на постоянной основе.
Буферы данных и PDO
Первое, что необходимо сделать, это связать программу технологического процесса и "железо". Для этого объявляется ФБ типа:
VAR mb : ModbusRtuMasterV2_KL6x22B; END_VAR
Тип ФБ выбирается, исходя из размера буфера данных модуля связи:
- ModbusRtuMasterV2_KL6x22B — с буфером в 22 байта.
- ModbusRtuMasterV2_KL6x5B — с буфером в 5 байт.
- ModbusRtuMasterV2_PcCOM — с буфером в 64 байта, рассчитано на системный порт контроллера.
- ModbusRtuMasterV2 — для использования только внутри библиотеки, но можно попробовать задействовать как универсальный ФБ для буферов произвольных размеров (буферы адресуются через указатели pComIn и pComOut).
Размер буфера данных можно изменять в разделе PDO, но не нужно — это востребовано только при портировании старых проектов. Поэтому часть наборов помечено как Legacy — это наследие старых систем, а часть как Standart — это нормальные, современные возможности по трансляции данных в реальном времени.
Для примера, возьмем EL6022 (EL6021). Справочная система говорит, что этот модуль содержит:
Data buffer 864 bytes receive buffer, 128 bytes transmit buffer
Bit width in the process image 22 x 8 bit input, 22 x 8 bit output, 16 bit control, 16 bit status
Это означает, что на борту модуля есть аппаратный буфер на прием и отправку данных (Data buffer 864 bytes...), часть аппаратного буфера отражается (или мапится) в PDO-образы TwinCAT (process image 22 x 8 bit input...).
В случае с так называемым системным портом, например, модули -N031 или порт панели P205|CP6606-..., с буферами немного сложнее: необходимо вручную добавить последовательный порт Miscellaneous → Serial Communication Port в устройства ввода-вывода I/O Devices. После этого, по умолчанию, будет включен режим совместимости с коплерами BK8xx0 Mode. Нам же необходим режим совместимости с KL6xx1 Mode (Emulation). KL-EL — без разницы: разная шина — одинаковый принцип работы.
В режиме совместимости с KL6xx1 мы работаем с системным портом, как с модулем расширения EL6022, где Data Bytes — это размер PDO области, а Buffer Size — как бы аппаратный буфер. но не обязательно по настоящему аппаратный — он может эмулироваться операционной системой. Размер в Data Bytes будет влиять на тип структуры данных для линковки с ПЛК-программой. Для стандартных 64-байт подойдет ModbusRtuMasterV2_PcCOM. Как такового, требования не существует, но пусть аппаратный буфер будет как минимум в два раза больше буфера PDO. Помните, что все это не точно и требует плотного тестирования на вашей конкретной системе.
Модули EL60xx умеют становиться виртуальными COM-портами операционной системы: соседняя вкладка EL60xx → Virtual Com Port. После установки драйвера и активирования конфигурации, Windows обнаружит новый последовательный порт, к которому можно будет подключить произвольное внешнее устройство, не имеющее никакого отношения к TwinCAT (сканер штрих кодов, например).
После линковки Input/Output (COM TxPDO-Map Inputs и COM RxPDO-Map Outputs) частей со структурами типа:
TYPE MB_KL6inData22B STRUCT Status: WORD; D: ARRAY [0..21] OF BYTE; END_STRUCT END_TYPE
...которые различаются только размером буфера, дальнейшая работа целиком ведется через экшены функционального блока. В TwinCAT 3 библиотека по прежнему работает через экшены: модных классов и методов, к сожалению, не завезли.
Экшены
ReadCoils — функция модбас #1, считать дискретный выход подчиненного (Read Coils), результат будет упакован (8 бит в байте).
ReadInputStatus — #2, прочитать дискретный вход подчиненного (Read Input Status), результат будет упакован (8 бит в байте).
ReadRegs — #3, прочитать регистр хранения (Read Holding Registers).
ReadInputRegs — #4, прочитать регистр ввода (Read Input Registers).
WriteSingleCoil — #5, записать (задать) состояние дискретного выхода подчиненного (Write Single Coil), данные должны быть упакованы (8 бит в байте).
WriteSingleRegister — #6, записать в регистр хранения (Write SingleRegister).
WriteMultipleCoils — #15, записать в несколько дискретных выходов подчиненного (Write Multiple Coils), данные должны быть упакованы (8 бит в байте).
WriteRegs — #16, записать в несколько регистров подчиненного (Preset Multiple Registers).
Diagnostics — #8, диагностика (Diagnostics).
Диагностика
Поле Error в ModbusRtuMasterV2_KL6x22B сигнализирует об ошибке, а код ошибки содержится в поле ErrorId. Заниматься диагностикой удобно где-нибудь за пределами приема-передачи данных, заодно подсчитывая статистику пакетов.
VAR LastError : MODBUS_ERRORS; CountError : DINT; CountSuccess : DINT; Error : BOOL; trigBusy : F_TRIG; END_VAR // обмен данными начинается // [...] // обмен данными заканчивается END_CASE trigBusy(CLK := mb.BUSY ); IF trigBusy.Q THEN IF mb.Error THEN Error := TRUE; LastError := mb.ErrorId; // Запоминаем код последней ошибки CountError := CountError + 1; // Подсчитываем количество ошибок ELSE Error := FALSE; CountSuccess := CountSuccess + 1; // Количество удачных пакетов END_IF END_IF
Список всех ошибок есть в статье Modbus RTU Error Codes. Все названия констант "говорящие" и вполне понятные.
Практика
Для обмена данными понадобятся как минимум два буфера, которые могут быть как массивами, так и структурами. Можно читать пачки регистров в структуры, но не забывайте — Modbus оперирует регистрами размером в "слово" (WORD, uint, sint, int).
VAR ReadData : ARRAY [1..10] OF WORD; // массив из данных десяти регистров WriteData : ARRAY [1..10] OF WORD; // массив данных для десяти регистров END_VAR VAR CONSTANT WORDS_TO_READ : // [...] сколько слов (WORD) прочитать WORDS_TO_WRITE : // [...] сколько слов (WORD) отправить END_VAR
Сбрасываем блоки перед применением, и после применения тоже сбрасываем:
CASE state OF INIT_STATE: mb.ReadRegs(Execute := FALSE); mb.WriteRegs(Execute := FALSE);
Читаем:
READ_STATE: mb.ReadRegs( UnitID := 1, // адрес подчиненного, начинается с 1 Quantity := WORDS_TO_READ, // сколько слов (WORD) прочитать MBAddr := 0, // адрес модбас, может отсчитываться как от нуля, // так и от единицы, читайте документацию на подчиненного cbLength := WORDS_TO_READ * 2, // сколько прочитать, но уже в байтах pMemoryAddr := ADR(ReadData), // локальный буфер куда будут записаны прочитанные регистры Execute := TRUE, Timeout := T#5s // таймаут на выполнение операции ); IF NOT mb.BUSY THEN mb.ReadRegs(Execute := FALSE); IF mb.Error THEN state := INIT_STATE; // обрабатываем ошибки ELSE state := // [...] // продолжаем работу END_IF END_IF
Пишем:
WRITE_STATE: mb.WriteRegs( UnitID := 1, Quantity := WORDS_TO_WRITE, // сколько слов (WORD) записать MBAddr := 0, cbLength := WORDS_TO_WRITE * 2, // сколько записать, но уже в байтах pMemoryAddr := ADR(WriteData), // локальный буфер данные которого // будут записаны в регистры подчиненного Execute := TRUE, Timeout := T#5s // таймаут на выполнение операции ); IF NOT mb.BUSY THEN mb.WriteRegs(Execute := FALSE); IF mb.Error THEN state := INIT_STATE; // обрабатываем ошибки ELSE state := // [...] // продолжаем работу END_IF END_IF
И не забывайте про время обработки цикла ПЛК-задачи: его продолжительность должна быть достаточной для вычитывания буферов, иначе начнет происходить периодическое переполнение и вы будете терять актуальную информацию.
Жаль что не показали как иницировать сам блок ModbusRtuMasterV2_KL6x22B
ReplyDelete