Exchange problems in QSerialPort with hardware flow control

Now I'm writing a data exchange program in QT Creator 5.12 through, let's say, a specific data network. The "modems" of this network operate over the RS-232 UART, and only in the hardware flow control mode.

The essence of the problem.

When issuing data to the port, there are no problems, I can set or reset the DTR and RTS signals QSerialPort:: dataTerminalReady(bool), and QSerialPort:: RequestToSend(bool) and check for CTS and DSR responses using QSerialPort::pinoutSignals(). And, of course, after that, the data is transmitted successfully.

Problems when receiving data from the device: I assume that the reason is the absence of the QSerialPort::setClearToSend(bool) and QSerialPort::setDataSetReady methods in the QSerialPort class(bool) since I can't respond to a request from the RTS data channel hardware with CTS, and I can't respond to DTR with DSR to accept data under hardware flow control, although I can certainly see these requests using QSerialPort::pinoutSignals(). Thus, just transferring data from the modem to my program does not start. We observed the signals using an oscilloscope.

Of course, theoretically, it is possible to make a special cable, where the signals RTS to CTS and DTR to DSR will be looped inside each connector, but the equipment with cables has long been at the receiver and it is not possible to rewrite the port controller code in the "modem" for unmanaged data flow transmission.

Maybe I'm missing something understand? Who knows - tell me a way out of this difficulty.

The actual code of the class for data exchange: Header file

#ifndef PORTUART_H
#define PORTUART_H

#include <QObject>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QTimer>

class PortUART : public QObject
{
    Q_OBJECT
public:
    explicit PortUART(QObject *parent = nullptr);
    ~PortUART();

signals:
    void signalReadedPortIO(QByteArray Str); //Resived data
    void signalErrComPort(QSerialPort::SerialPortError err); //Retranslate Error

public slots:
    bool setComData(QString name, QSerialPort::BaudRate baud, QSerialPort::DataBits databits, QSerialPort::Parity parbits, QSerialPort::StopBits stopbits, QSerialPort::FlowControl qflow_control);
    bool open(void);
    bool close(void);
    QString getName();
    qint64 WriteToPort(QByteArray message);

    bool isOpen(void);
    bool isWritable(void);
    bool isReadable(void);

    void read();
    void errorSerail(QSerialPort::SerialPortError err);
    void timerOverflow(void);
private:
    QTimer *pTimer;
    int timeInterval = 1000;
    QByteArray rcBuffer;

    QSerialPort *serial = nullptr;
    QString port_name;
    QSerialPort::BaudRate baud_rate;
    QSerialPort::DataBits data_bits;
    QSerialPort::Parity parity;
    QSerialPort::StopBits stop_bits;
    QSerialPort::FlowControl flow_control;
};

#endif // PORTUART_H

Implementation file

#include "portuart.h"
#include <QDebug>

PortUART::PortUART(QObject *parent) : QObject(parent)
{
    baud_rate = QSerialPort::Baud115200;
    data_bits = QSerialPort::Data8;
    parity = QSerialPort::NoParity;
    stop_bits = QSerialPort::OneStop;
    flow_control = QSerialPort::HardwareControl;
    serial = new QSerialPort(this);
    connect(serial, SIGNAL(readyRead()), this, SLOT(read()));
    connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(errorSerail(QSerialPort::SerialPortError)));
    pTimer = new QTimer;
    connect(pTimer, SIGNAL(timeout()), this, SLOT(timerOverflow()));
}

PortUART::~PortUART()
{
    if (serial != nullptr)
    {
        if(serial->isOpen())
            serial->close();
        delete serial;
    }
    if (pTimer != nullptr)
        delete pTimer;
}

bool PortUART::setComData(QString name, QSerialPort::BaudRate baud, QSerialPort::DataBits databits, QSerialPort::Parity parbits, QSerialPort::StopBits stopbits, QSerialPort::FlowControl qflow_control)
{
    port_name = name;
    baud_rate = baud;
    data_bits = databits;
    parity = parbits;
    stop_bits = stopbits;
    flow_control = qflow_control;
    timeInterval = static_cast<int>((1000.0 / static_cast<int>(baud_rate)) * 500);
    timeInterval < 50 ? timeInterval = 50 : timeInterval += 0; // пауза не меньше 50 мс
    timeInterval > 250 ? timeInterval = 1000 : timeInterval += 0; // пауза не больше 1000 мс
    return true;
}

bool PortUART::open(void)
{
    try
    {
        //setup COM port
        serial->setPortName(port_name);
        serial->setBaudRate(baud_rate);
        serial->setDataBits(data_bits);
        serial->setParity(parity);
        serial->setStopBits(stop_bits);
        serial->setFlowControl(flow_control);

        serial->open(QIODevice::ReadWrite);

        if(serial->isWritable())
        {
            qDebug() << "Yes, i can write to port!";
        }

        serial->clear();
        if (flow_control == QSerialPort::HardwareControl)
        {
            serial->setDataTerminalReady(true);
            serial->setRequestToSend(true);
            serial->pinoutSignals( );
        }
    }
    catch(...)
    {
        return false;
    }
    return true;
}

bool PortUART::close(void)
{
    if (serial->isOpen())
    {
        serial->close();
        return true;
    }
    else
    {
        return false;
    }
}

void PortUART::read()
{
    if (pTimer->isActive())
        pTimer->stop();
    else
        rcBuffer.clear();
    QByteArray bytes = serial->readAll();
    qDebug() << QString(bytes.toHex().toUpper());
    //qDebug() << QString(bytes);
    rcBuffer.append(bytes);
    //emit signalReadedPortIO(bytes);
    pTimer->start(timeInterval);
}

void PortUART::timerOverflow(void)
{
    if (rcBuffer.size() > 0)
    {
        emit signalReadedPortIO(rcBuffer);
    }
    pTimer->stop();
}

void PortUART::errorSerail(QSerialPort::SerialPortError err)
{
    if (err != 0)
    {
        qDebug() << "Error: " << err;
        emit signalErrComPort(err);
    }
}

QString PortUART::getName(void)
{
    return port_name;
}

bool PortUART::isOpen()
{
    return serial->isOpen();
}

bool PortUART::isWritable()
{
    return serial->isOpen();
}

bool PortUART::isReadable()
{
    return serial->isOpen();
}

qint64 PortUART::WriteToPort(QByteArray message)
{
    if (serial->isWritable() == false)
    {
        return 0;
    }
    return serial->write(message);
}
Author: Виктор, 2020-03-24

1 answers

On the recommendation from QT support, I made changes to the file C:\Qt\Qt5.12.7\5.12.7\Src\qtserialport\src\serialport\qserialport_win.cpp and recompiled this library in "release" mode.

static inline void qt_set_flowcontrol(DCB *dcb, QSerialPort::FlowControl flowcontrol)
{
    dcb->fInX = FALSE;
    dcb->fOutX = FALSE;
    dcb->fOutxCtsFlow = FALSE;
    if (dcb->fRtsControl == RTS_CONTROL_HANDSHAKE) //Думаю, что эта строка лишняя, но я не стал её менять
        dcb->fRtsControl = RTS_CONTROL_DISABLE;
    switch (flowcontrol) {
    case QSerialPort::NoFlowControl:
        break;
    case QSerialPort::SoftwareControl:
        dcb->fInX = TRUE;
        dcb->fOutX = TRUE;
        break;
    case QSerialPort::HardwareControl:
        dcb->fOutxCtsFlow = TRUE;
        dcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
        dcb->fOutxDsrFlow = TRUE; // Это я добавил
        dcb->fDtrControl = DTR_CONTROL_HANDSHAKE; // Это я добавил
        break;
    default:
        break;
    }
}

After that, as you probably guessed, the DTR->DSR reaction was activated additionally and RTS->CTS was successfully activated.

 3
Author: Виктор, 2020-03-27 09:03:58