May 14, 2017

Мастер Modbus RTU на шине RS485

Ненадолго станем мастером Modbus RTU на шине RS485, будем читать регистры устройств, например, ряда электросчетчиков или насосов, связанных двумя проводами и контролируемых ПЛК.
Если вам нужно наоборот — побыть подчиненным, выставляя данные наружу, читайте — Сервер 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


И не забывайте про время обработки цикла ПЛК-задачи: его продолжительность должна быть достаточной для вычитывания буферов, иначе начнет происходить периодическое переполнение и вы будете терять актуальную информацию.

1 comment

  1. Жаль что не показали как иницировать сам блок ModbusRtuMasterV2_KL6x22B

    ReplyDelete

Note: Only a member of this blog may post a comment.