Драйвер RS_TO_ECAT для модулей SPRS
Назначение
RS_TO_ECAT — библиотека, предназначенная для упрощения работы с модулями последовательных интерфейсов SA-P5-IF485/422, SA-P5-IF232.
Блок полностью скрывает особенности обмена через EtherCAT и предоставляет разработчику простой интерфейс передачи и приёма данных произвольной длины.
Почему необходим данный драйвер
Передача данных между ПЛК и модулем ввода-вывода осуществляется через PDO EtherCAT.
Размер PDO является фиксированным и не может изменяться динамически во время работы.
Использование большого буфера передачи (например, 256 байт) привело бы к тому, что весь этот объём передавался бы в каждом цикле EtherCAT независимо от количества полезных данных, значительно увеличивая загрузку сети.
Поэтому обмен в модулях реализован небольшими блоками по 8 байт.
Функциональный блок автоматически:
- разбивает передаваемый массив на части по 8 байт;
- отправляет их последовательно;
- автоматически собирает принятые данные обратно в единый буфер;
- определяет окончание приёма по времени отсутствия данных.
Пользователю больше не требуется самостоятельно управлять передачей отдельных блоков.
Установка библиотеки
- Откройте
Tools → Library Repositoryи нажмитеInstall..., указав путь к собранному файлу библиотеки. - В проекте-приложении откройте
Application → Library Manager, нажмитеAdd libraryи найдитеRS_TO_ECAT_Driverв списке. - После добавления библиотеки блок становится доступен по полному имени
RS_TO_ECAT_Driver.RS_TO_ECAT.
Интерфейс блока
Входы (VAR_INPUT)
| Параметр | Тип | Назначение |
|---|---|---|
Execute |
BOOL |
Запуск передачи по переднему фронту. Сигнал можно подавать импульсом — блок сам отслеживает фронт и не требует удержания TRUE на всё время передачи. |
RecvGapTime |
TIME |
Время отсутствия новых данных на линии, после которого принятый пакет считается завершённым. По умолчанию T#40MS. |
SendLength |
USINT |
Фактическая длина данных в буфере SendBuf, которые необходимо отправить (в байтах). |
RS485_InData |
ARRAY[0..7] OF BYTE |
Подключается к входному PDO модуля (%IB). |
RS485_InLength |
USINT |
Подключается к входному PDO модуля (%IB), показывает количество байт, доступных для считывания в RS485_InData. |
FilterEcho |
BOOL |
Флаг фильтрации эха. При TRUE (по умолчанию) первые байты приёма, совпадающие по количеству с отправленным пакетом, отбрасываются. Полезно при работе с RS-485 и RS-232 . |
Выходы (VAR_OUTPUT)
| Параметр | Тип | Назначение |
|---|---|---|
RS485_OutData |
ARRAY[0..7] OF BYTE |
Подключается к выходному PDO модуля (%QB). |
RS485_OutLength |
USINT |
Подключается к выходному PDO модуля (%QB). |
RS485_ReadFlag |
USINT |
Подключается к выходному PDO модуля (%QB), подтверждает модулю приём очередной порции данных. |
Busy |
BOOL |
TRUE — идёт передача, начатая по Execute. |
Done |
BOOL |
TRUE — приём пакета завершён, данные доступны в RecvBuf. Остаётся TRUE до начала приёма следующего пакета. |
Error |
BOOL |
TRUE — произошла ошибка обмена. |
RecvBuf |
ARRAY[0..63] OF BYTE |
Буфер с принятыми данными. |
RecvLen |
USINT |
Длина принятого пакета (количество значащих байт в RecvBuf). |
Входы-выходы (VAR_IN_OUT)
| Параметр | Тип | Назначение |
|---|---|---|
SendBuf |
ARRAY[*] OF BYTE |
Буфер с данными для отправки. Передаётся как открытый массив, что позволяет использовать буферы любой длины. Важно: так как стандарт IEC 61131-3 не гарантирует корректное автоматическое определение размера открытого массива внутри FB, реальное количество байт для отправки необходимо явно передавать через параметр SendLength. |
Принцип работы
Передача
При появлении переднего фронта на Execute блок считывает длину пакета из параметра SendLength и начинает цикл из трёх состояний:
- Копирует очередную порцию (до 8 байт) из
SendBufвRS485_OutData, выставляетRS485_OutLength. - На следующем цикле сбрасывает
RS485_OutLengthв0— это обязательный шаг квитирования, без которого модуль не примет следующую порцию. - Проверяет, остались ли неотправленные байты. Если да — возвращается к шагу 1 с новой порцией. Если нет — завершает передачу, сбрасывает
BusyвFALSE.
Все три шага выполняются автоматически, без участия прикладной программы — пользователю достаточно один раз подать импульс на Execute.
Приём
Приём построен симметрично передаче и проходит через три состояния:
- Как только
RS485_InLengthстановится не равным0, блок копирует данные изRS485_InDataвRecvBufи устанавливаетRS485_ReadFlag := 1, подтверждая модулю получение порции. - На следующем цикле
RS485_ReadFlagсбрасывается в0. - Блок проверяет
RS485_InLength: если в нём снова появились данные — переходит к шагу 1 за новой порцией. Если данных нет — запускает таймер на времяRecvGapTime; если в течение этого времени новых данных не появляется, пакет считается завершённым: фиксируется длина вRecvLen, выставляетсяDone := TRUE.
Если за время ожидания (RecvGapTime) появляются новые данные, таймер сбрасывается, и приём продолжается — это защищает от обрыва пакета на естественных микропаузах при передаче по медленным линиям RS485.
Рекомендации по архитектуре и циклам задач
Важно
Для стабильной работы драйвера и корректного обмена данными через PDO крайне не рекомендуется запускать задачу (Task), в которой вызывается данный FB, с тем же циклом, что и шина EtherCAT, или быстрее неё.
Правило: цикл задачи опроса должен быть медленнее (длиннее) цикла шины EtherCAT минимум в 2–4 раза.
Пример использования
PROGRAM PLC_PRG
VAR
fbRS485 : RS_TO_ECAT_Driver.RS_TO_ECAT;
// Буфер запроса. Может быть любой длины (в примере 8 байт)
SendData8 : ARRAY[0..7] OF BYTE := [16#01, 16#02, 16#03, 16#04,
16#05, 16#06, 16#07, 16#08];
SendLength : USINT; // Переменная для хранения длины буфера
bStart : BOOL;
MyAnswer : ARRAY[0..63] OF BYTE; // куда забираем готовый ответ
MyAnswerLen : USINT;
bAnswerReady : BOOL; // флаг "новый ответ получен"
bDoneLast : BOOL;
i : USINT;
// Привязка к PDO модуля
RS485_OutData AT %QB10 : ARRAY[0..7] OF BYTE;
RS485_OutLength AT %QB18 : USINT;
RS485_ReadFlag AT %QB19 : USINT;
RS485_InData AT %IB9 : ARRAY[0..7] OF BYTE;
RS485_InLength AT %IB17 : USINT;
END_VAR
// 1. Вычисляем длину массива для отправки.
SendLength := USINT(SIZEOF(SendData8));
// 2. Вызов драйвера каждый цикл
fbRS485(
Execute := bStart,
SendBuf := SendData8,
SendLength := SendLength, // Явно передаем длину
RecvGapTime := T#40MS,
FilterEcho := TRUE,
RS485_InData := RS485_InData,
RS485_InLength := RS485_InLength,
RS485_OutData => RS485_OutData,
RS485_OutLength => RS485_OutLength,
RS485_ReadFlag => RS485_ReadFlag
);
// bStart - однократный импульс, сбрасываем сами после подачи
IF bStart THEN
bStart := FALSE;
END_IF;
// Забираем ответ в обычную переменную по переднему фронту Done
IF fbRS485.Done AND NOT bDoneLast THEN
MyAnswerLen := fbRS485.RecvLen;
FOR i := 0 TO MyAnswerLen - 1 DO
MyAnswer[i] := fbRS485.RecvBuf[i];
END_FOR;
bAnswerReady := TRUE;
END_IF;
bDoneLast := fbRS485.Done;