Серийная проблема на Beaglebone Black с запущенной Linux

Вопрос или проблема

Для проекта мы используем Modbus, который работает через интерфейс RS485. Около 95% сообщений корректно отправляются клиентом, после чего сервер корректно отвечает. Однако, примерно раз в несколько сотен сообщений сервер не отвечает, что приводит к тайм-ауту.

После более глубокого анализа причин, почему сервер не отвечает, оказалось, что сервер получает либо неправильный ID modbus, на который не следует отвечать, либо неправильный байт CRC. Сигнал RS485 также был показан на осциллографе, который показал, что сервер правильно интерпретирует данные, следовательно, клиент отправляет неправильные данные.

Чтобы проверить, действительно ли клиент отправляет ожидаемые данные, был написан тестовый код:

#include <iostream>
#include <thread>
#include <modbus/modbus-rtu.h>
#include <dlfcn.h>
#include "macrologger.h"

modbus_t *context = nullptr;

// Определяем тип указателя функции для оригинальной функции записи
typedef ssize_t (*write_t)(int, const void *, size_t);

// Обертка для функции записи
ssize_t write(int fd, const void *buf, size_t count) {
    // Логируем данные, которые записываются
    printf("Перехваченный вызов записи: ");
    for (size_t i = 0; i < count; i++) {
        printf("{%02X}", ((unsigned char *)buf)[i]);
    }
    printf("\n");

    // Используем dlsym для получения оригинальной функции записи
    write_t real_write = (write_t) dlsym(RTLD_NEXT, "write");

    // Проверяем на ошибки в dlsym
    if (!real_write) {
        fprintf(stderr, "Ошибка: %s\n", dlerror());
        return -1;  // Возвращаем код ошибки, если dlsym не удается
    }

    // Вызываем оригинальную функцию записи
    return real_write(fd, buf, count);
}

uint32_t modbus_errors = 0;
uint8_t id = 0;

void setModbusSlaveId() {
    // Устанавливаем Modbus slave ID
    if (modbus_set_slave(context, id++) < 0) {
        LOG_ERROR("Ошибка установки modbus slave: %s", modbus_strerror(errno));
    }
}

void sendTest() {
    uint16_t state;

    for (uint32_t index = 0; index < 100; index++) {
        printf("Отправка сообщения с slave ID: %d\n", modbus_get_slave(context));
        setModbusSlaveId();
        int reply = modbus_read_registers(context, 0x12, 1, &state);

        if (reply == -1) {
            modbus_errors++;
            while (1);
        }
    }
}

int main() {
    LOG_INFO("Запуск libmodbus_test");

    context = modbus_new_rtu("/dev/rs485-2", 115200, 'N', 8, 1);
    if (context == nullptr) {
        LOG_ERROR("Не удалось создать контекст libmodbus");
        return 0;
    }

    // Устанавливаем тайм-аут ответа
    modbus_set_response_timeout(context, 2, 0);

    if (modbus_rtu_set_rts(context, MODBUS_RTU_RTS_UP) < 0) {
        LOG_ERROR("Не удалось установить режим модбас RTU RTS");
        return 0;
    }

    if (modbus_rtu_set_rts_delay(context, 200) < 0) {
        LOG_ERROR("Не удалось установить задержку модбас RTU RTS");
        return 0;
    }

    LOG_INFO("modbus rts: %i, текущая задержка rts: %i us", 
             modbus_rtu_get_rts(context), modbus_rtu_get_rts_delay(context));

    if (modbus_set_debug(context, false) < 0) {
        LOG_ERROR("Ошибка установки режима отладки modbus");
        return 0;  // Не критично, продолжаем
    }

    if (modbus_connect(context) < 0) {
        LOG_ERROR("Ошибка подключения modbus");
        return 0;
    }

    while (1) {
        sendTest();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

Этот код инициализирует библиотеку libmodbus и открывает соединение modbus на RS485-2 с скоростью 115200 бод. Он отправляет пакеты по 100 сообщений каждые 100 мс. Slave ID увеличивается с каждым сообщением, чтобы показать, где возникает ошибка, после чего программа останавливается (while (1)). Сервер настроен игнорировать slave ID, таким образом он отвечает на каждое полученное сообщение.

Ожидаемая команда modbus: {id}{03}{00}{12}{01}{CRCH}{CRCL}

После этого теста было отмечено 2 ошибки после остановки программы.

Во-первых, кажется, что иногда сначала отправляется slave ID предыдущего сообщения, после чего следует правильное сообщение. Однако это усекание отрезает байт CRC, вызывая ошибку CRC.

Неверный_ID логи
Неверный_ID волна

Вторая ошибка, которая возникает, является противоположностью первой, когда байт ID не передается и сообщение кажется начинающимся с функции, передающей случайный CRC байт.

Без_ID логи
Без_ID волна

В обоих случаях libmodubs, кажется, пытается передать правильные буферы. Это проверяется путем перехвата вызова записи, который использует libmodbus для записи в UART периферийное устройство в Linux. Это показывает правильные байты, которые должны были быть отправлены, но сигнал на осциллографе кажется показывает, что буфер отправленный, неправильно записан на UART устройство. Кто-нибудь знает, как я могу решить эту проблему, или как я могу продвигать отладку этой проблемы дальше?

Характеристики системы:

Клиент: Beaglebone black industrial под управлением Linux

Сервер: STM32F103

Ответ или решение

На основе предоставленного описания, можно выделить несколько потенциальных причин возникновения проблемы с передачей данных через интерфейс RS485 на платформе Beaglebone Black под управлением Linux. Для её решения необходимо учесть различные аспекты работы аппаратной и программной частей системы.

Теоретическая часть

Ошибки в передаче данных

Возникновение ошибок в передаче данных через интерфейс RS485, таких как передача неверного байта идентификатора или некорректное значение CRC, может быть обусловлено несколькими факторами:

  1. Электромагнитные помехи и качество сигналов: RS485 интерфейс подвержен электромагнитным помехам, особенно при высоких скоростях передачи данных, таких как 115200 бод. Это может приводить к искажению сигнала.

  2. Временные задержки и гонки: При быстрой передачи данных могут возникать проблемы из-за несовпадения временных задержек между началом передачи последовательности байтов и готовностью принимающего устройства к этой передаче.

  3. Аппаратные неисправности: Возможны неисправности кабелей или разъемов, которые могут приводить к сбоям в передаче данных.

  4. Ошибки в управляющем ПО: Неправильная конфигурация или настройка программных таймингов и режимов передачи данных в библиотеке libmodbus, а также ошибки в логике управления передачей.

Программная часть

В коде, предоставленном для диагностики проблемы, используется библиотека libmodbus, которая обрабатывает передачу данных по протоколу Modbus RTU. Используется перехват функции write для записи в UART, что позволяет своевременно логировать отправленные данные. Однако, несмотря на то, что библиотеки пытаются отправлять корректную последовательность байтов, определенные проблемы остаются неразрешенными в аппаратной части передачи данных.

Практическая часть

Диагностика и отладка

Рассмотрим шаги, которые могут помочь диагностировать и устранить проблему:

  1. Проверка аппаратной линии связи:

    • Проверьте качество и длину кабеля RS485 и убедитесь в отсутствии повреждений. Используйте экранированные кабели и минимизируйте длину линии.
    • Убедитесь, что на линии правильно настроены терминаторы, так как их отсутствие или неправильная установка может вызвать отражения сигнала.
  2. Анализ временных задержек:

    • Проверьте настройку RTS (Request to Send) сигналов. Убедитесь, что задержка rts корректно установлена (в вашем коде это 200 мкс).
    • Испытайте изменение значений задержек и времени ответа, чтобы выявить возможные улучшения.
  3. Аппаратная и программная совместимость:

    • Оцените разницу в скоростях обработки данных между сервером и клиентом. Убедитесь, что STM32F103 успевает обрабатывать входящую информацию с заданной скоростью.
    • Проверьте корректность настройки битов передачи (8-N-1) на обоих устройствах.
  4. Мониторинг и логирование:

    • В дополнение к программному логированию используйте аппаратный анализатор для захвата и анализа сигналов на шине RS485.
    • Сохранение осциллограмм для повторного изучения может помочь понять, что именно происходит при ошибке.

Применение решений

  1. Улучшение аппаратной защиты:

    • Обеспечьте экранирование и правильное заземление используемых устройств, чтобы минимизировать электромагнитные помехи.
  2. Оптимизация кода:

    • Рассмотрите возможность внедрения программных исправлений в библиотеку libmodbus, чтобы улучшить обработку временных интервалов.
    • Изучите возможность использования более устойчивого протокола коррекции ошибок.
  3. Дополнительные тесты:

    • Проведите серию тестов с разными скоростями передачи данных и задержками RTS для определения оптимальных параметров.

С применением вышеописанных процедур можно существенно увеличить вероятность устранения проблемы или, по крайней мере, значительно сократить частоту ее возникновения. Совокупность аппаратных и программных мер позволит обеспечить более устойчивую и надежную передачу данных по интерфейсу RS485.

Оцените материал
Добавить комментарий

Капча загружается...