Проблемы обмена в QSerialPort при аппаратном управлении потоком
Сейчас пишу программу обмена данными в QT Creator 5.12 через, скажем так, специфичную сеть передачи данных. "Модемы" этой сети работают по UART RS-232, причём только в режиме аппаратного управления потоком.
Суть проблемы.
При выдаче данных в порт проблем нет я могу установить или сбросить сигналы DTR и RTS QSerialPort::dataTerminalReady(bool), and QSerialPort::requestToSend(bool) и проконтролировать наличие ответов CTS и DSR с помощью QSerialPort::pinoutSignals(). И, разумеется после этого, передаються данные успешно.
Проблемы при приёме данных из устройства: предполагаю, что причина в отсутствии в классе QSerialPort методов QSerialPort::setClearToSend(bool) и QSerialPort::setDataSetReady(bool) поскольку на запрос от аппаратуры канала данных RTS я не могу ответить CTS и на DTR ответить DSR, чтобы принять данные при аппаратном управлении потоком, хотя увидеть эти запросы я, конечно, могу с помощью QSerialPort::pinoutSignals(). Таким образом просто передача данных из модема в мою программу не начинается. Наблюдали сигналы с помощью осциллографа.
Конечно, теоретически, возможно сделать специальный кабель, где будут закольцованы сигналы RTS на CTS и DTR на DSR внутри каждого разъёма, но аппаратура с кабелями уже давно у получателя и переписать код контроллера порта в "модеме" на неуправляемую передачу потока данных не представляется возможным.
Может я чего-то не понимаю? Кто знает - подскажите выход из этого затруднения.
Собственно код класса для обмена данными: Заголовочный файл
#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
Файл реализации
#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);
}
Ответы (1 шт):
По рекомендации из поддержки QT я сделал изменения в файле C:\Qt\Qt5.12.7\5.12.7\Src\qtserialport\src\serialport\qserialport_win.cpp и перекомпилировал эту библиотеку в режиме "релиз".
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;
}
}
После этого, как вы наверное догадались, включилась дополнительно реакция DTR->DSR и успешно заработала RTS->CTS.