串行丢包 - QTSerialPort

时间:2014-11-13 19:39:16

标签: c++ qt serial-port qt5 qtserialport

我正在制作一个程序,以每秒250个样本(115200波特)读取来自串行RN42蓝牙连接的数据流。在运行代码时,我注意到有些数据没有被删除和读取,因此不同步。

SerialMonitor::SerialMonitor(QObject *parent) :
    QObject(parent)
{

    // Initialization here

     DAQ = new QSerialPort(this);
     DAQ->setPortName("/dev/tty.BIOEXG-SPP");
     DAQ->setBaudRate(QSerialPort::Baud115200);
     DAQ->setDataBits(QSerialPort::Data8);
     DAQ->setParity(QSerialPort::NoParity);
     DAQ->setStopBits(QSerialPort::OneStop);
     DAQ->setFlowControl(QSerialPort::NoFlowControl);

     if (DAQ->open(QIODevice::ReadOnly)) printf("Success!\n");
     else printf("FAILED...\n");

     connect(DAQ, SIGNAL(readyRead()), this, SLOT(WriteToText()));
}

void SerialMonitor::WriteToText()
{
    while (DAQ->canReadLine()) {
        QString IncomingData = DAQ->readLine();

        // More processing here

    }
}

}

我的代码有问题吗?如果没有,有没有办法解决这个问题?这是一个EEG设备,因此每个数据点都至关重要。

提前致谢!

3 个答案:

答案 0 :(得分:0)

您已禁用所有错误检查和同步机制:

  • 奇偶校验位已禁用。
  • 禁用流量控制。

如果您可以控制设备上的微控制器,那么选择实施chk机制来恢复丢失数据是一个不错的选择。如果您的设备是黑盒子,则必须使用HW机制来提高稳定性。当使用无线通信时,存在丢失一些数据的可能性并且在项目中被考虑。

注意:您应该在开场之后设置串行参数。

答案 1 :(得分:0)

  1. 您可以尝试使用bytesAvailable/readAll()代替(can)readLine()进行检查。

  2. 驱动程序有可能无法及时从FIFO读取数据,从而导致数据丢失(可能是驱动程序问题)。

答案 2 :(得分:0)

我遇到了同样的问题。 QSerialPort有时会丢失一些字节块。 我修改了一个我在网上找到的旧代码,这不会丢失任何东西。

#include "QVSerialPort.hpp"
#include <QtDebug>

//////////////////////////////////////////
// Set header file for documentation    /
////////////////////////////////////////

//////////////////////////////////////////////////////
// Windows Version of the serial port driver Code
/////////////////////////////////////////////////////

#ifdef Q_OS_WIN32

#include <windows.h>

QVSerialPort::QVSerialPort(QObject *parent) :
    QThread(parent)
{
    // make everything in this thread, run in this thread. (Including signals/slots)
    QObject::moveToThread(this);
    // make our data buffer
    dataBuffer = new QByteArray();
    hSerial = INVALID_HANDLE_VALUE;
    running = true;
    deviceName=NULL;
    bufferSem = new QSemaphore(1); // control access to buffer
}

QVSerialPort::~QVSerialPort() {
    running = false;
    CloseHandle(hSerial);
}

//write data to serial port
int QVSerialPort::writeBuffer(QByteArray *buffer) {
        int dwBytesRead = 0;
    if (hSerial != INVALID_HANDLE_VALUE) {
        // have a valid file discriptor
        WriteFile(hSerial, buffer->constData(), buffer->size(), (DWORD *)&dwBytesRead, NULL);
        return dwBytesRead;
    } else {
        return -1;
    }
}

// setup what device we should use
void QVSerialPort::usePort(QString *device_Name, int _buad, int _byteSize, int _stopBits, int _parity) {
    deviceName = new QString(device_Name->toLatin1());
    // serial port settings
    Buad = _buad;
    ByteSize = _byteSize;
    StopBits = _stopBits;
    Parity = _parity;
}

// data fetcher, get next byte from buffer
uint8_t QVSerialPort::getNextByte() {
    // mutex needed to make thread safe
    bufferSem->acquire(1); // lock access to resource, or wait untill lock is avaliable
    uint8_t byte = (uint8_t)dataBuffer->at(0); // get the top most byte
    dataBuffer->remove(0, 1); // remove top most byte
    bufferSem->release(1);
    return byte; // return top most byte
}

// return number of bytes in receive buffer
uint32_t QVSerialPort::bytesAvailable() {
    // this is thread safe, read only operation
    bufferSem->acquire(1); // lock access to resource, or wait untill lock is avaliable
    uint32_t res = (uint32_t)dataBuffer->size();
    bufferSem->release(1);
    return res;
}

// our main code thread
void QVSerialPort::run() {
//    bufferSem->release(1);      // not in a locked state

    // thread procedure
    if (_SERIALTHREAD_DEBUG) {
        qDebug() << "QVSerialPort: QVSerialPort Started..";
        qDebug() << "QVSerialPort: Openning serial port " << deviceName->toLatin1();
    }

    // open selected device
    hSerial = CreateFile( (WCHAR *) deviceName->constData() , GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if ( hSerial == INVALID_HANDLE_VALUE ) {
        qDebug() << "QVSerialPort: Failed to open serial port " << deviceName->toLatin1();
        emit openPortFailed();
        return;  // exit thread
    }

    // Yay we are able to open device as read/write
    qDebug() << "QVSerialPort: Opened serial port " << deviceName->toLatin1() << " Sucessfully!";

    // now save current device/terminal settings
    dcbSerialParams.DCBlength=sizeof(dcbSerialParams);

    if (!GetCommState(hSerial, &dcbSerialParams)) {
                qDebug() << "QVSerialPort: Failed to get com port paramters";
                emit openPortFailed();
                return;
        }

    if (_SERIALTHREAD_DEBUG) {
        qDebug() << "QVSerialPort: Serial port setup and ready for use";
        qDebug() << "QVSerialPort: Starting QVSerialPort main loop";
    }
    dcbSerialParams.BaudRate=Buad;
    dcbSerialParams.ByteSize=ByteSize;
    dcbSerialParams.Parity=Parity;
    dcbSerialParams.StopBits=StopBits;

    if(!SetCommState(hSerial, &dcbSerialParams)) {
                qDebug() << "QVSerialPort: Failed to set new com port paramters";
                emit openPortFailed();
                return;
    }
    COMMTIMEOUTS timeouts;

    timeouts.ReadIntervalTimeout = 0;
    timeouts.ReadTotalTimeoutMultiplier = 0;
    timeouts.ReadTotalTimeoutConstant = 0;
    timeouts.WriteTotalTimeoutMultiplier = 1;
    timeouts.WriteTotalTimeoutConstant = 1;
    if (!SetCommTimeouts(hSerial, &timeouts)){
        qDebug()<<" error setcommtimeout";
    }
    // signal we are opened and running
    emit openPortSuccess();

    static uint8_t byte123[1023]; // temp storage byte
    int dwBytesRead;
    int state=0;    // state machine state

    // start polling loop
    while(running) {
        int ret = ReadFile(hSerial, (void *)byte123, 128, (DWORD *)&dwBytesRead, NULL); // reading 1 byte at a time..  only 2400 baud.
        // print what we received
        if (ret != 0 && dwBytesRead > 0){
            if (_SERIALTHREAD_DEBUG) {
                qDebug() << "QVSerialPort: Received byte with value: " << byte123[0];
            }
            if (dataBuffer->size() > 1023*1024) {
                if ( state == 0 ) {
                    qDebug() << "Local buffer overflow, dropping input serial port data";
                    state = 1;  // over-flowed state
                    emit bufferOverflow();
                }
            } else {
                if ( state == 1 ) {
                    qDebug() << "Local buffer no-longer overflowing, back to normal";
                    state = 0;;
                }
                // stick byte read from device into buffer
                // Mutex needed to make thread safe from buffer read operation
                bufferSem->acquire(1);
                for (int i=0;i<dwBytesRead;i++)
                    dataBuffer->append(byte123[i]);
                bufferSem->release(1);
                emit hasData(); // signal our user that there is data to receive
            }
        }
    }
    CloseHandle(hSerial);
}

#else

//////////////////////////////////////////////////////////
// Linux/Mac/BSD Version of the serial port driver Code
////////////////////////////////////////////////////////

// POSIX C stuff for accessing the serial port
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

// Constructor
QVSerialPort::QVSerialPort(QObject *parent) :
    QThread(parent)
{
    // make everything in this thread, run in this thread. (Including signals/slots)
    QObject::moveToThread(this);
    // make our data buffer
    dataBuffer = new QByteArray();
    running = true;
    deviceName=NULL;
    bufferMutex = new QMutex(); // control access to buffer
    bufferMutex->unlock();  // not in a locked state
}

QVSerialPort::~QVSerialPort() {
                running = false;
                close(sfd);
}

//write data to serial port
int QVSerialPort::writeBuffer(QByteArray *buffer) {
    if (sfd != 0) {
        // have a valid file discriptor
        return write(sfd, buffer->constData(), buffer->size());
    } else {
        return -1;
    }
}

// setup what device we should use
void QVSerialPort::usePort(QString *device_Name) {
    deviceName = new QString(device_Name->toLatin1());
    if (_SERIALTHREAD_DEBUG) {
        qDebug() << "QVSerialPort: Using device: " << deviceName->toLatin1();
    }
}

// data fetcher, get next byte from buffer
uint8_t QVSerialPort::getNextByte() {
    // mutex needed to make thread safe
    bufferMutex->lock(); // lock access to resource, or wait untill lock is avaliable
    uint8_t byte = (uint8_t)dataBuffer->at(0); // get the top most byte
    dataBuffer->remove(0, 1); // remove top most byte
    bufferMutex->unlock();
    return byte; // return top most byte
}

// return number of bytes in receive buffer
uint32_t QVSerialPort::bytesAvailable() {
    // this is thread safe, read only operation
    return (uint32_t)dataBuffer->size();
}

// our main code thread
void QVSerialPort::run() {
    // thread procedure
    if (_SERIALTHREAD_DEBUG) {
        qDebug() << "QVSerialPort: QVSerialPort Started..";
        qDebug() << "QVSerialPort: Openning serial port " << deviceName->toLatin1();
    }

    // open selected device
    sfd = open( deviceName->toLatin1(), O_RDWR | O_NOCTTY );
    if ( sfd < 0 ) {
        qDebug() << "QVSerialPort: Failed to open serial port " << deviceName->toLatin1();
        emit openPortFailed();
        return;  // exit thread
    }

    // Yay we are able to open device as read/write
    qDebug() << "QVSerialPort: Opened serial port " << deviceName->toLatin1() << " Sucessfully!";
    // now save current device/terminal settings
    tcgetattr(sfd,&oldtio);
    // setup new terminal settings
    bzero(&newtio, sizeof(newtio));
    newtio.c_cflag = Buad | ByteSize | StopBits | Parity | CREAD | CLOCAL; // enable rx, ignore flowcontrol
    newtio.c_iflag = IGNPAR;
    newtio.c_oflag = 0;
    newtio.c_lflag = 0;
    newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
    newtio.c_cc[VMIN]     = 1;   /* blocking read until atleast 1 charactors received */
    // flush device buffer
    tcflush(sfd, TCIFLUSH);
    // set new terminal settings to the device
    tcsetattr(sfd,TCSANOW,&newtio);
    // ok serial port setup and ready for use

    if (_SERIALTHREAD_DEBUG) {
        qDebug() << "QVSerialPort: Serial port setup and ready for use";
        qDebug() << "QVSerialPort: Starting QVSerialPort main loop";
    }

    // signal we are opened and running
    emit openPortSuccess();

    uint8_t byte; // temp storage byte
    int state=0;    // state machine state

    // start polling loop
    while(running) {
        read(sfd, (void *)&byte, 1); // reading 1 byte at a time..  only 2400 baud.
        // print what we received
        if (_SERIALTHREAD_DEBUG) {
            qDebug() << "QVSerialPort: Received byte with value: " << byte;
        }
        if (dataBuffer->size() > 1023) {
            if ( state == 0 ) {
                qDebug() << "Local buffer overflow, dropping input serial port data";
                state = 1;  // over-flowed state
                emit bufferOverflow();
            }
        } else {
            if ( state == 1 ) {
                qDebug() << "Local buffer no-longer overflowing, back to normal";
                state = 0;;
            }
            // stick byte read from device into buffer
            // Mutex needed to make thread safe from buffer read operation
            bufferMutex->lock();
            dataBuffer->append(byte);
            bufferMutex->unlock();
            emit hasData(); // signal our user that there is data to receive
        }
    }
    close(sfd);
}

#endif // OS Selection

.hpp文件:

#ifndef QVSerialPort_HPP
#define QVSerialPort_HPP

// library linking info
#include "QVSerialPort_Global.hpp"

// Common Stuff
#include <QThread>
#include <QMutex>
#include <QSemaphore>

// enables verbose qDebug messages
#define _SERIALTHREAD_DEBUG 0

#ifdef Q_OS_WIN32

///////////////////////////////////////////////////////
//  IF BUILDING ON WINDOWS, IMPLEMENT WINDOWS VERSION
//  THE SERIAL PORT CLASS.
///////////////////////////////////////////////////////

#include <windows.h>
#include <stdint.h>

// default defined baud rates
// custom ones could be set.  These are just clock dividers from some base serial clock.
#ifdef Q_OS_WIN32
// Use windows definitions
#define Baud300        CBR_300
#define Baud600        CBR_600
#define Baud1200       CBR_1200
#define Baud2400       CBR_2400
#define Baud4800       CBR_4800
#define Baud9600       CBR_9600
#define Baud19200      CBR_19200
#define Baud38400      CBR_38400
#define Baud57600      CBR_57600
#define Baud115200     CBR_115200
#else
// Use Posix definitions
#define Baud300        B300
#define Baud600        B600
#define Baud1200       B1200
#define Baud2400       B2400
#define Baud4800       B4800
#define Baud9600       B9600
#define Baud19200      B19200
#define Baud38400      B38400
#define Baud57600      B57600
#define Baud115200     B115200
#endif

// bytes sizes
#ifdef Q_OS_WIN32
// windows byte defines
#define CS8            8
#define CS7            7
#define CS6            6
#define CS5            5
#else
// posix is already CS8 CS7 CS6 CS5 defined
#endif

// parity
#ifdef Q_OS_WIN32
#define ParityEven      EVENPARITY
#define ParityOdd       ODDPARITY
#define ParityNone      NOPARITY
#else
#define ParityEven      PARENB
#define ParityOdd       PARENB | PARODD
#define ParityNone      0
#endif

// stop bit
#ifdef Q_OS_WIN32
#define SB1             0
#define SB2             CSTOPB
#else
#define SB1             ONESTOPBIT
#define SB2             TWOSTOPBIT
#endif

class QVSerialPort : public QThread
{
    Q_OBJECT
public:
    explicit QVSerialPort(QObject *parent = 0);
    ~QVSerialPort();
    void usePort(QString *device_Name, int _buad, int _byteSize, int _stopBits, int _parity);
    void closePort();

    // data fetcher, get next byte from buffer
    uint8_t getNextByte();

    // return number of bytes in receive buffer
    uint32_t bytesAvailable();

    // write buffer
    int writeBuffer(QByteArray *buffer);

protected:
    // thread process, called with a start() defined in the base class type
    // This is our hardware receive buffer polling thread
    // when data is received, the hasData() signal is emitted.
    virtual void run();

signals:
    // asynchronous signal to notify there is receive data to process
    void hasData();
    // signal that we couldn't open the serial port
    void openPortFailed();
    // signal that we openned the port correct and are running
    void openPortSuccess();
    // RX buffer overflow signal
    void bufferOverflow();

public slots:
    // we don't need no sinking slots

private:
    // serial port settings
    int Buad;
    int ByteSize;
    int StopBits;
    int Parity;

    HANDLE hSerial;
    bool running;
    QByteArray *dataBuffer;
    QSemaphore *bufferSem;
    QString *deviceName;
    // windows uses a struct called DCB to hold serial port configuration information
    DCB dcbSerialParams;
};

#else // not Q_OS_WIN32

////////////////////////////////////////////////////////////////
//  IF USING A POSIX OS, ONE THAT UNDSTANDS THE NOTION        /
//  OF A TERMINAL DEVICE (Linux,BSD,Mac OSX, Solaris, etc)   /
/////////////////////////////////////////////////////////////

// Assuming posix compliant OS  (Tested on Linux, might work on Mac / BSD etc )

#include <inttypes.h>
#include <termios.h>


class QVSerialPort : public QThread
{
    Q_OBJECT
public:
    explicit QVSerialPort(QObject *parent = 0);
    ~QVSerialPort();
    void usePort(QString *device_Name);
    void closePort();

    // data fetcher, get next byte from buffer
    uint8_t getNextByte();

    // return number of bytes in receive buffer
    uint32_t bytesAvailable();

    // write buffer
    int writeBuffer(QByteArray *buffer);

protected:
    // thread process, called with a start() defined in the base class type
    virtual void run();

signals:
    // asynchronous signal to notify there is receive data to process
    void hasData();
    // signal that we couldn't open the serial port
    void openPortFailed();
    // signal that we openned the port correct and are running
    void openPortSuccess();
    // RX buffer overflow signal
    void bufferOverflow();

public slots:
    // we don't need no sinking slots

private:
    // serial port settings
    int Buad;
    int ByteSize;
    int StopBits;
    int Parity;

    int sfd;
    bool running;
    QByteArray *dataBuffer;
    QMutex *bufferMutex;
    QString *deviceName;
    // termio structs
    struct termios oldtio, newtio;
};

#endif // OS Selection

#endif // QVSerialPort_HPP