2014-11-13 2 views
2

Я делаю программу, которая считывает поток данных, поступающих из последовательного соединения Bluetooth RN42 со скоростью 250 выборок в секунду (115200 бод). При запуске кода я заметил, что некоторые данные были отброшены и не прочитаны, что приводит к сбою синхронизации.Serial Packet Loss - QTSerialPort

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 

    } 
} 

}

Есть ли проблема с моим кодом? Если нет, есть ли способ решить эту проблему? Это устройство ЭЭГ, поэтому каждая точка данных имеет решающее значение.

Заранее благодарен!

+0

ли Bluetooth, имеющий API с некоторой функцией для чтения из своего рода переполнение RX FIFO? – vermaete

ответ

0

Вы отключили все проверки ошибок и синхронизации механизм:

  • Паритет битную отключена.
  • Управление потоком отключено.

Если у вас есть контроль над микроконтроллером на устройствах, хорошим выбором является механизм chk для восстановления потерянных данных. Если ваши устройства являются черным ящиком, вы должны использовать механизм HW для повышения стабильности. Когда используется беспроводная связь, существует возможность потерять некоторые данные и рассматривается в проекте.

Примечание: после открытия не следует устанавливать серийный параметр.

0
  1. Вы можете попробовать проверить его с bytesAvailable/readAll() вместо (can)readLine().

  2. Есть вероятность того, что драйвер не успеет прочитать данные из FIFO, что приведет к потере данных (возможно, это проблема с драйвером).

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