Не удается связаться, когда программное управление потоком отключено для последовательного порта

Мое приложение должно иметь возможность взаимодействовать с различными настройками последовательного порта, которые пользователь выбирает в пользовательском интерфейсе. Все опции работают, кроме программного управления потоком. Когда я включаю XON/XOFF, он работает с другим узлом. Но когда я отключаю XON/XOFF, одноранговый узел получает случайное количество 0x00 байт.

Есть ли что-то, что я упускаю?

Примечание. Точно такую ​​же настройку я выполняю и для однорангового узла. Поэтому, когда я включаю или отключаю опцию XON/XOFF, я делаю то же самое для узла.

int serialDevice = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_SYNC | O_NONBLOCK);

Serial_Params_t serialSettings;
serialSettings.baudRate = 12; // means 115200
serialSettings.dataBits = 8;
serialSettings.hardwareFlowControl = 0;
serialSettings.parity = 0;
serialSettings.parityOdd = 0;
serialSettings.stopBits = 1;
serialSettings.xonxoff = 0;

setSerialSettings(serialDevice, &serialSettings);
//---------------------------------------------------------------
int8_t setSerialSettings(int serialDevice, Serial_Params_t *settings)
{
    struct termios2 tty;
    memset(&tty, 0, sizeof tty);

    // get current serial settings
    if (ioctl(serialDevice, TCGETS2, &tty) == -1)
    {
        sendLog("Can't get serial attributes | setSerialSettings", LOG_TYPE_ERROR);
        return FALSE;
    }

    // baudrate
    int32_t baudrates[] = {B110, B150, B300, B600, B1200, B2400, B4800, B9600, B9600, B19200, B38400, B57600, B115200};
    cfsetospeed(&tty, (speed_t)baudrates[settings->baudRate]);
    cfsetispeed(&tty, (speed_t)baudrates[settings->baudRate]);

    // enable input parity check
    tty.c_cflag |= INPCK;

    // data bits: CS5, CS6, CS7, CS8
    tty.c_cflag &= ~CSIZE;
    switch (settings->dataBits)
    {
    case 5:
        tty.c_cflag |= CS5;
        break;
    case 6:
        tty.c_cflag |= CS6;
        break;
    case 7:
        tty.c_cflag |= CS7;
        break;
    case 8:
    default:
        tty.c_cflag |= CS8;
        break;
    }

    // stop bit
    switch (settings->stopBits)
    {
    case 1:
    default:
        tty.c_cflag &= ~CSTOPB;
        break;
    case 2:
        tty.c_cflag |= CSTOPB;
    }

    // parity
    if (settings->parity == 1)
        tty.c_cflag |= PARENB;
    else
        tty.c_cflag &= ~PARENB;

    // odd/even parity
    if (settings->parityOdd == 1)
        tty.c_cflag |= PARODD;
    else
        tty.c_cflag &= ~PARODD;

    // flow control
    // XON/XOFF
    if (settings->xonxoff == 1)
        tty.c_cflag |= (IXON | IXOFF | IXANY);
    else
        tty.c_cflag &= ~(IXON | IXOFF | IXANY);

    // enable RTS/CTS
    if (settings->hardwareFlowControl == 1)
        tty.c_cflag |= CRTSCTS;
    else
        tty.c_cflag &= ~CRTSCTS;

    tty.c_cc[VMIN] = 1;            // raise read flag even you read 1 byte
    tty.c_cc[VTIME] = 50;          // 1 seconds read timeout (deciseconds)
    tty.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines

    // non-canonical mode
    tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
    tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
    tty.c_oflag &= ~OPOST;

    // flush port & apply attributes
    tcflush(serialDevice, TCIFLUSH);
    if (tcsetattr(serialDevice, TCSANOW, &tty) != 0)
    {
        sendLog("Can't set serial attributes | setSerialSettings", LOG_TYPE_ERROR);
        return FALSE;
    }
    return TRUE;
}

person Mustafa Chelik    schedule 14.03.2019    source источник
comment
Когда два устройства или программы пытаются установить связь, важны характеристики оба. Вы уверены, что удаленный одноранговый узел не полагается на включение программного управления потоком данных? Явно ли, или, возможно, просто прослушивается в случае, если программное управление потоком отключено?   -  person John Bollinger    schedule 14.03.2019
comment
@JohnBollinger да, я проверил все возможности, прежде чем публиковать этот вопрос. Я даже пользовался разными приложениями для пира. Результаты те же, к сожалению.   -  person Mustafa Chelik    schedule 14.03.2019
comment
Опубликуйте используемые включаемые файлы и определение Serial_Params_t. минимально воспроизводимый пример был бы еще лучше.   -  person chux - Reinstate Monica    schedule 14.03.2019
comment
Подозрительно, что вы используете ioctl для получения свойств последовательного порта, но используете tcsetattr() для их обновления. В частности, потому что struct termios2 - это линуксизм, а (Linux-) стандартный заголовок, в котором он определен, несовместим с GLIBC termios.h, поэтому вам нужно будет сделать что-то странное, чтобы даже ваш код скомпилировался. Более того, struct termios GLIBC несовместим с struct termios2 ядра (и с struct termios ядра тоже).   -  person John Bollinger    schedule 14.03.2019
comment
В вашем Serial_Params_t вы отключили аппаратное управление потоком (CRTSCTS) и программное управление потоком (XON/XOFF). Для последовательных устройств, с которыми я работаю, обычно используется аппаратное управление потоком. Но резервное копирование, что говорит вам ваше руководство по оборудованию? Вам также не хватает пары флагов, которые могут быть важны (строки состояния и постобработка). Вот как выглядит мой файл поддержки терминала для модемов: пример конфигурации терминала linux. Вам также следует рассмотреть возможность использования cfmakeraw для базовой конфигурации терминала.   -  person jww    schedule 14.03.2019
comment
Поток 0 может быть связан с tcsendbreak. Думаю, нам нужен MCVE и подробности об оборудовании. Также см. Почему tcsendbreak() необходимо для запуска последовательной связи? и комментарий, Несоблюдение правильной настройки режимов терминала - это один из способов нарушить переносимость.   -  person jww    schedule 14.03.2019


Ответы (2)


Я не могу конкретно объяснить, почему ваш одноранговый узел получает посторонние 0x00 байт, потому что вы вообще не представляете никакого кода, ответственного за отправку чего-либо, но код, который вы представляете для настройки свойств последовательного порта, несколько раз ошибочен. Проблемы включают:

  • Использование несоответствующих системных интерфейсов. Вы можете использовать соответствующие ioctl для получения и обновления атрибутов терминала, или вы можете использовать tcgetattr и tcsetattr, но вы не должны смешивать и сочетать. Структуры данных, с которыми работают ioctl Linux, не являются взаимозаменяемыми со структурами данных, с которыми работают функции termios GLIBC, хотя они достаточно похожи, особенно по типу, именам и порядку элементов флагов, что не может вызывать заметных ошибок. . Вы, должно быть, использовали некоторые трюки и / или игнорировали некоторые важные предупреждения компилятора, чтобы даже заставить ваш код скомпилироваться.

    Обратите внимание, что смешивание и сопоставление применяется не только к извлечению и установке общих настроек терминала, но и ко всем функциям termios.h для изменения структуры настроек терминала, например cfgetispeed и cfgetospeed. Вы можете использовать их со структурой настроек, полученной через tcgetattr, или, что менее полезно, с непосредственно объявленным экземпляром struct termios, определенным termios.h, но не с экземпляром, полученным через ioctl. С другой стороны, структура, подходящая для использования с любой функцией termios.h, не подходит для использования ни с одним из ioctl для управления свойствами терминала.

  • Установка битов флага в неверных переменных флага. Особое значение для вопроса имеет то, что вы устанавливаете биты IXON, IXOFF и IXANY c_cflag, но они применяются к члену c_iflag, а не c_cflag. Ведущий I в их именах является мнемоническим для этого, хотя не все макросы флагов следуют этому соглашению. Вы также устанавливаете INPCK не в том члене.

person John Bollinger    schedule 14.03.2019
comment
Большое спасибо. Я изменил как get, так и set на ioctl и изменил флаги, как вы упомянули. Я могу общаться с отключенным XON/XOFF. Сэр, оказывается, я не могу общаться с некоторыми скоростями передачи данных. Я могу общаться через 9600 и 115200, но не могу через 14400. Вы знаете, почему это происходит? Я обновил код. Спасибо :) - person Mustafa Chelik; 14.03.2019
comment
@MustafaChelik, если у вас есть дополнительный вопрос об исправленном коде, задайте его как отдельный вопрос. В частности, не изменяйте код, представленный в вопросе, если у вас есть ответы, относящиеся к нему (откат изменений). - person John Bollinger; 14.03.2019
comment
Однако мой общий совет будет заключаться в том, чтобы использовать (только) интерфейсы termios.h, избавляясь от всех следов ioctl и заголовков, связанных конкретно с ними. Ничто из того, что вы представили, не демонстрирует необходимость ioctl, а интерфейсы termios.h чище, удобнее и лучше документированы. - person John Bollinger; 14.03.2019
comment
Я не могу использовать termios.h, потому что мне нужно struct termios2. Я разместил новый вопрос, как вы просили. Буду рад, если проверишь. Мой новый вопрос здесь: stackoverflow.com/questions/55170488 / - person Mustafa Chelik; 14.03.2019
comment
@MustafaChelik, как я уже сказал, я не вижу причин в том, что вы здесь представили, зачем вам нужен интерфейс ioctl. Включая его struct termios2. В частности, в Linux вам не нужно это для поддержки скоростей передачи, которые вы пытаетесь приспособить. - person John Bollinger; 14.03.2019
comment
Если вы хотите использовать пользовательскую скорость передачи данных (в моем приложении должна быть эта опция), вы должны использовать termios2, потому что он имеет c_ispeed участника. Вы знаете какой-нибудь другой способ? Я был бы рад услышать это, сэр. - person Mustafa Chelik; 14.03.2019
comment
Также распространенная скорость 14400 бод не определена заранее в списке скоростей передачи в termios. Таким образом, чтобы использовать скорость 14400 бод, вы должны использовать пользовательские настройки скорости передачи данных, о которых я упоминал ранее. - person Mustafa Chelik; 14.03.2019
comment
Очень хорошо, @MustafaChelik. Но скорость 14400 строк не входит в число поддерживаемых кодом, который вы представили, так что опять же, ничто в том, что вы представили, не указывает на необходимость интерфейса ioctl. Это еще одна причина для того, чтобы продолжение было отдельным вопросом (и спасибо, что сделали его одним). - person John Bollinger; 14.03.2019
comment
Причина в том, что в терминах не определено B14400. Итак, если вы внимательно посмотрите, в списке есть два B9600, чтобы не сдвинуть мой список (у меня есть такой же список в Интернете). Спасибо за ваше время. - person Mustafa Chelik; 14.03.2019

Но когда я отключаю XON/XOFF, одноранговый узел получает случайное количество 0x00 байт.

Это может означать, что отправитель отправляет данные быстрее, чем получатель может с ними справиться.

Либо

  • Уменьшите скорость отправки данных (это не столько бод, сколько не отправляйте сразу столько данных).

  • Используйте аппаратное управление потоком (и провода/кабели, которые его поддерживают).

  • Улучшить обработку входящих данных получателей.


Примечание. Другие возможности объясняют нули.

person chux - Reinstate Monica    schedule 14.03.2019
comment
Я мог бы представить себе потерю данных (выпадение), если бы отправитель отправлял слишком быстро, чтобы получатель мог их обработать, но почему последовательности из 0x00 байтов? - person John Bollinger; 14.03.2019
comment
@John - я считаю, что tcsendbreak отправляет поток 0, и это может иметь какое-то отношение к ICANON. Чего я не знаю, так это почему используется tcsendbreak. Я не думаю, что у нас есть хороший рабочий пример от OP, и мы почти ничего не знаем о его оборудовании. - person jww; 14.03.2019
comment
@JohnBollinger Переполнение буфера может сигнализировать о количестве нулевых байтов, но @jww более вероятный сценарий. - person chux - Reinstate Monica; 14.03.2019
comment
Я полагаю, что это возможно, @jww, но получатели не должны интерпретировать разрывы как последовательности правильно оформленных 0x00 байтов. Возможно, все клиенты, которые пробовал OP, игнорируют ошибки кадрирования. - person John Bollinger; 14.03.2019