QT 5.7串口读取速度非常慢。

3
我很新手,正在自学编程。我写了一个应用程序来轮询控制单元中的多个请求。基本上,我不断向控制单元发送各种读取命令,并读取响应。我的程序可以正常工作,我成功地发送了命令并接收到了回答。但是读取速度非常慢(我在代码中设置了100毫秒的超时以确保我获得完整的回复)。
我有一个针对同一控制单元的程序,由专业的C++编码人员编写,他的程序每30毫秒轮询一次,我总是在那个时间范围内收到完整的答案。我使用相同的设置57K波特率,8位1个停止位和无校验位。然而,我的QT代码需要几乎100毫秒才能收到答案。
在我的代码中,我先读取前2个字节(第一个字节是消息标识符,第二个字节是剩余消息长度),然后再循环读取,直到总消息长度等于消息长度字节+1(加1是为了包含第一个字节)。我已经陷入困境,不知道为什么我的QT代码如此缓慢,当我知道限制因素不是硬件时。请求始终是3个字节,回复的长度从3到61字节不等。请帮助我找出错误所在。如果我取消超时设置,我总是有短读取。到目前为止,我也尝试过read(all),但结果相同。
下面是我从代码中提取出来的读取响应部分。完整代码在https://github.com/MarkusIppy/PowerTune上。
//Error handling 
QTime startTime = QTime::currentTime(); 
int timeOut = 100; // timeout in milisec. 
QByteArray recvData = m_serialport->read(2);  // reading first two bytes of received message to determine lenght of ecpected message 
int msgLen = recvData[1]; //Total message Lenght excluding the first byte 
while ( recvData.size() <= (msgLen+1) ) 
{ 
    if ( startTime.msecsTo(QTime::currentTime()) > timeOut ) break; 
    recvData += m_serialport->read(msgLen+1-recvData.size()); 
} 

if (msgLen +1 == recvData.length())  //if the received data lenght equals the message lenght from lenght byte + identifier byte (correct message lenght received ) 
{ 
    qDebug() << "Received data OK"<<msgLen +1; 
    if(requestIndex <= 61){requestIndex++;} 
    else{requestIndex = 58;} 
    readData(recvData); 
} 
else   //if the lenght of the received message does not correspond with the expected lenght repeat the request 
{ 
    qDebug() << "Received data lenght NIO"; 
    readData(recvData); 
    qDebug() << "Request Message again"<< requestIndex; 
}
3个回答

2

我稍微修改了代码,现在它可以在实际的硬件上完美运行了。以下是我准备好的可读插槽:

void Serial::readyToRead()
{
    qDebug() << "ready read";
    if(ecu == 0)
    {
        m_readData.append(m_serialport->readAll());
        Bytesexpected = m_readData[1]+1;
        qDebug() << "readdata current" <<m_readData.toHex();
        if (Bytesexpected == m_readData.size())
        {
            m_timer.stop();
            if(requestIndex <= 62){requestIndex++;}
            else{requestIndex = 59;}
            readData(m_readData);
            Serial::clear();
            m_readData.clear();
            Serial::sendRequest(requestIndex);
        }
        if (Bytesexpected != m_readData.size())
        {
            qDebug() << "starting timer";
            m_timer.start(5000);
        }
    }

2
对不起,我没有足够的时间来查看您的项目,从您提供的代码中,我不能百分之百地确定问题的原因。但是我的最佳猜测是,在这种情况下,问题可能是您明确等待数据接收,而事件处理有些延迟或根本没有进行。
无论如何,这里有一些建议:
  1. 使用 QTimer 代替 QTime 来设置超时。
  2. 了解 Qt5 的信号和槽 并使用它们来异步读取串口数据。
我使用QSerialPort通过将其bytesWritten(qint64 bytes)readyRead()信号连接到我的程序的槽,比如on_bytesWritten(qint64 bytes)on_readyRead()。然后我向目标设备发送请求,在on_readyRead()槽中处理结果。每次发送命令时,我都会启动一个QTimer,将其timeout()信号连接到应用程序的on_timeout()槽。这样我可以监控设备是否及时响应,以及在数据到达时立即获取数据。您还可以使用QSerialPorterrorOccurred(QSerialPort::SerialPortError error)信号来检查传输是否存在问题。

感谢您的回答。 - Markus
嗨@scopchanov,我无法再编辑我的评论了。 - Markus
嗨@scopchanov,我无法再编辑我的评论了。我已经开始编辑我的代码,我已经使用了信号和槽,并尝试实现您的建议。我查看了QT的异步读取器示例,并发现这正是您描述的方式。我还在我的代码中发现了另一个问题,我不知道一条消息可能会分成多个块发送并触发readyread槽超过一次。我现在已经为此做好了准备,并附加消息,直到它具有预期长度,包括5000毫秒的超时。我只能在下周末测试实际硬件端。 - Markus
1
@Markus Ippy,是的,消息不是一次性发送的,你需要自己构建它。这也是我在我的程序中所做的。void MySerial::on_readyRead() { if (!m_port->isOpen()) { return; } dataReady(m_port->readAll()); } 从这里开始:void MySerial::dataReady(const QByteArray &pdu) { m_buffer.append(pdu); if (pdu.contains('\n')) { processResponse(m_buffer); } } - scopchanov

1

这是我目前为止的代码(只发布我的cpp文件的重要部分)。这个代码现在几乎完美地与我的消息模拟器配合工作。它现在按预期进行轮询,但超时总是在5秒后触发(我需要将其更改为仅在没有消息到来时触发)。我将只能在下周末测试它的实际硬件端。

    void Serial::initSerialPort()
    {
    if (m_serialport)
        delete m_serialport;
    m_serialport = new SerialPort(this);
    connect(this->m_serialport,SIGNAL(readyRead()),this,SLOT(readyToRead()));
    connect(m_serialport, static_cast<void (QSerialPort::*)    (QSerialPort::SerialPortError)>(&QSerialPort::error),
            this, &Serial::handleError);
    connect(&m_timer, &QTimer::timeout, this, &Serial::handleTimeout);
     m_timer.start(5000);


    }

    void Serial::readyToRead() 
    {
    if(ecu == 0)
    {

        m_readData.append(m_serialport->readAll());
        Bytesexpected = m_readData[1]+1;
        if (Bytesexpected == m_readData.size())
        {
            if(requestIndex <= 62){requestIndex++;}
            else{requestIndex = 59;}
            readData(m_readData); // message for processing
            Serial::clear();
            m_readData.clear();
        }
    //Timeout
        if (!m_timer.isActive())
            m_timer.start(5000);

}
}

     void Serial::handleTimeout()
    {
    qDebug() << "Timeout";
    //request Data Again
    QString fileName = "Errors.txt";
    QFile mFile(fileName);
    if(!mFile.open(QFile::Append | QFile::Text)){
        qDebug() << "Could not open file for writing";
    }
    QTextStream out(&mFile);
    out << "Timeout Request Index " << int(requestIndex)<< " lenght received  "<< int(m_readData.length())<< " Bytes "<< " Expected Bytes "<< int(Bytesexpected)<< " bytes " <<" Message "<< QByteArray(m_readData.toHex()) <<endl;
    mFile.close();
    Serial::clear();
    m_readData.clear();
    Serial::sendRequest(requestIndex);
    }

    void Serial::handleError(QSerialPort::SerialPortError serialPortError)
    {
    if (serialPortError == QSerialPort::ReadError) {
        QString fileName = "Errors.txt";
        QFile mFile(fileName);
        if(!mFile.open(QFile::Append | QFile::Text)){
            qDebug() << "Could not open file for writing";
        }
        QTextStream out(&mFile);
        out << "Serial Error " << (m_serialport->errorString()) <<endl;
        mFile.close();
        qDebug() <<"Serialport Error" <<(m_serialport->errorString());
    }
    }

当您重新启动计时器或者在您确认一切正常后,您需要断开信号连接,例如:disconnect(&m_timer, &QTimer::timeout, this, &Serial::handleTimeout); - Spyros Mourelatos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接