我正在制作一个程序,以每秒 42 个样本(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
}
}
}
我的代码有问题吗?如果没有,有没有办法解决这个问题?这是一个脑电图设备,因此每个数据点都至关重要。
提前感谢!
您已禁用所有错误检查和同步机制:
- 奇偶校验位已禁用。
- 流控制已禁用。
如果您可以控制设备上的微控制器,一个不错的选择是实施 chk 机制来恢复丢失的数据。如果您的设备是黑匣子,则必须使用硬件机制来提高稳定性。当使用无线通信时,存在丢失一些数据的可能性,并在项目中考虑过。
注意:您应该在打开之后而不是之前设置串行参数。
-
您可以尝试使用
bytesAvailable/readAll()
而不是(can)readLine()
进行检查。
驱动程序 可能没有及时从FIFO读取数据,从而导致数据丢失(可能是驱动程序问题)。
我遇到了同样的问题。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