串行数据包丢失 - QTSerialPort
Posted
技术标签:
【中文标题】串行数据包丢失 - QTSerialPort【英文标题】:Serial Packet Loss - QTSerialPort 【发布时间】:2014-11-13 19:39:16 【问题描述】:我正在编写一个程序,它以每秒 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
我的代码有问题吗?如果没有,有没有办法解决这个问题?这是一个脑电图设备,因此每个数据点都至关重要。
提前致谢!
【问题讨论】:
蓝牙是否有 API 可以读取某种 RX FIFO 溢出? 【参考方案1】:您已禁用所有错误检查和同步机制:
奇偶校验位已禁用。 流控制已禁用。如果您可以控制设备上的微控制器,一个不错的选择是实施 chk 机制来恢复丢失的数据。如果您的设备是黑匣子,您必须使用硬件机制来提高稳定性。当使用无线通信时,存在丢失某些数据的可能性,并在项目中进行了考虑。
注意:串口参数设置在打开后而不是之前。
【讨论】:
【参考方案2】:您可以尝试使用bytesAvailable/readAll()
而不是(can)readLine()
进行检查。
有可能驱动程序没有及时从 FIFO 中读取数据,从而导致数据丢失(可能是驱动程序问题)。
【讨论】:
【参考方案3】:我遇到了同样的问题。 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
【讨论】:
以上是关于串行数据包丢失 - QTSerialPort的主要内容,如果未能解决你的问题,请参考以下文章