Как избежать тайм-аута в QSerialPort при обработке высокоскоростных данных

Я работаю над приложением Windows, которое получает данные от датчика на частоте 600 Гц. В двух из пяти случаев мой поток ввода-вывода успешно считывает 4 байта данных с датчика и передает их в поток графического интерфейса.

Проблема в трех из пяти раз, QSerialPort имеет необъяснимые тайм-ауты, где waitForReadyRead() QSerialPort возвращает false, а serial.errorString() имеет ошибку тайм-аута. В этом случае он никогда не будет читать данные. Если я читаю с последовательного порта, несмотря на ошибку тайм-аута, я буду читать 2000+ байтов данных в следующем waitForReadyRead, который будет доставлен кусками, что делает аспект приема данных в реальном времени моего приложения устаревшим.

Я попытался использовать сигнал readyRead() последовательного порта, но он демонстрирует то же поведение, т.е. если появляется ошибка тайм-аута, сигнал readyRead() никогда не срабатывает.

ОБНОВЛЕНИЕ: я могу воспроизвести проблему с примером терминала Qt ([QT_INSTALL_EXAMPLES]/serialport/ терминал), который использует неблокирующее чтение. Частота появления ошибки значительно меньше, но она определенно есть.

ОБНОВЛЕНИЕ: Используя Serial Port Monitor, я вижу, что когда он застревает, Пример Qt Terminal застревает на IOCTL_SERIAL_WAIT_ON_MASK, мой пример застревает на IRP_MJ_WRITE DOWN сразу после IOCT_SERIAL_WAIT_ON_MASK. Этого никогда не происходит с другими терминальными программами, что наводит меня на мысль, что проблема определенно в Qt.

Pastebin выхода монитора последовательного порта

void IOThread::run(){

QSerialPort serial;
serial.setPortName(portname)
serial.setBaudRage(QSerialPort::Baud115200);
serial.setStopBits(QSerialPort::OneStop)
serial.setParity(QSerialPort::NoParity);
serial.setDataBits(QSerialPort::Data8);
serial.setFlowControl(QSerialPort::NoFlowControl);

if(!serial.open(QIODevice::ReadWrite)
{
    qDebug() << "Error Opening Port";
    return;
}
else
{
    qDebug() << "Error Message: " << serial.errorString() // prints "Unknown Error"
}

while(true)
{
    if(serial.waitForReadyRead(1000))
    {
        qDebug() << "Normal read";
        reception_buffer = serial.readAll();
    }
    else
    {
        qDebug() << "Timeout";
        /* serial.readAll() here will read nothing but force next read to read huge chunk of data */ 
        continue;
    }
}

// Process data...
}

3 ответа

Решение

Спасибо ребятам из QSerialPort, эта ошибка в Qt 5.10.1 решается путем применения этого патча: https://codereview.qt-project.org/

"QSP может игнорировать все события чтения, когда данные поступают на устройство при открытии. В этом случае даже повторное открытие устройства не помогает. Причина в том, что QWinOverlappedIoNotifier включен после вызова startAsyncCommunication(), что, вероятно,, приводит к игнорированию всех событий EV_RXCHAR. Обходной путь - включить уведомитель раньше, чем какая-либо из операций ввода-вывода." - Денис Шиенков, специалист по обслуживанию QSerialPort

застревает на IOCTL_SERIAL_WAIT_ON_MASK

Скорее всего, проблема в вашем HW или драйвере. QSP использует асинхронное уведомление, основанное на WaitCommEvent. Если WaitCommEvent застревает - проблема в вашем устройстве или драйвере (скорее всего).

Попробуйте, если это имеет какое-либо значение:

while (true) {
    QByteArray reception_buffer;
    if (serial.waitForReadyRead(1000)) {
        reception_buffer = serial.readAll();
        while (serial.waitForReadyRead(10)) {
            reception_buffer += serial.readAll();
        }
        qDebug() << "reception_buffer ready";
    }
    else {
        qDebug() << "Timeout";
    }
}

Если вы хотите предотвратить таймаут от waitForReadyRead Вы можете установить:

if(serial.waitForReadyRead(-1))

bool QSerialPort:: waitForReadyRead (int msecs = 30000) будет превышать время ожидания через миллисекунды; время ожидания по умолчанию составляет 30000 миллисекунд. Если msecs равен -1, функция не будет иметь тайм-аут.

Другие вопросы по тегам