PDA

View Full Version : QSerialPort - speed and performance



Wer_Bn
29th April 2015, 18:41
Hello guys.
First time here, should not be the last, since I should work with Qt for a few times

I developed an app to obtain data from a serial port.
The current baudrate is 115200, and I wanted to improve it even more.

The thing is, that even at this rate it is really slow.
The buffer keeps storing data and increasing all the time, by using the function QSerialPort::readAll

I don't know if I am handling the data correctly, but here's a hint of my code:

This is the slot for the readyRead() signal from QSerialPort


void ADCStream::readData()
{
ADCData += ADCPort->readAll();

int dataBegin;
int dataEnd;

dataBegin = ADCData.indexOf('A',0);
dataEnd = ADCData.indexOf('Z',dataBegin);
if((dataBegin != -1) && (dataEnd != -1))
{
QByteArray tempData = ADCData.mid(dataBegin+1,2);
quint16 number=0;

number = (tempData.at(0) & 0x00FF)<<8;
number |= tempData.at(1) & 0x00FF;

emit dataReady(number);
ADCData.remove(0,dataEnd+1);//get next data
}
}


this is the constructor of the main dialog


ui->setupUi(this);
myStream = new ADCStream();//customized class, with a QSerialPort instance
streamThread = new QThread(this);
myStream->moveToThread(streamThread);

streamThread->setPriority();
streamThread->start();
myStream->open();

log = new QFile("Log",this);
if(!log->open(QIODevice::Append))
{
QMessageBox::warning(this,"Log File","Couldn't open file");
}

connect(myStream,SIGNAL(dataReady(quint16)),this,S LOT(showData(quint16)),Qt::QueuedConnection);
connect(myStream->ADCPort,SIGNAL(readyRead()),myStream,SLOT(readData ()));


The slot showData simply displays the data in a QTextBrowser with this:
ui->textLogger->insertPlainText(QString(" %1\n").arg(whatever));

Can you tell me how can I improve this data handling from the serialPort?
I'm reading from a sensor, and when I change the sensor, it takes a REALLY long time to update in the GUI

Thanks in advance ;)

Wer_Bn
30th April 2015, 11:05
I found (kind of) a solution.

If I put a timer generating an update and sendig a signal every milisecond(! the minimum of QTimer) I can actually read from the serial port every milisecond. Which is what I wanted.
Still, If I want to improve the sampling rate I cannot do that...

Any ideas?

anda_skoa
30th April 2015, 11:26
Since you are using a thread, make sure each object belongs to the thread you think it belongs.
e.g. especially make sure that myStream->ADCPort is a child of myStream and is thus owned by the thread.

In general my recommendation would be to make it work without a thread first.

Cheers,
_

ChrisW67
30th April 2015, 11:35
You don't want to sample faster: you already read all data when it becomes available. You do want to process all the available received data each time more data is received.

Your code currently processes a single 16 bit value each time more data is received, leaving any other data for later. For example, if you receive data in 16 byte blocks (a not uncommon UART buffer size), you will process four bytes (AxxZ) and leave 12 bytes in the buffer. The next 16 byte block arrives and you process the second value from the first block and carry forward 24 bytes, and so on. Processing one sample per packet effectively reduces your sample processing rate.

The readyRead() handler should iterate over the entire buffer processing samples until less than a full sample remains.

You should not need threads to keep up with a serial port and remain responsive.

BTW: Your code also has a problem if either byte following the 'A' is 0x5A ('Z').

Wer_Bn
30th April 2015, 15:14
Thank you very much for the idea. And the warning !
I just though that readyRead would constantly generate an interrupt, as long as it had data waiting to be read.

If something goes wrong, I'll shout ;)

Wer_Bn
30th April 2015, 18:12
What about this?

Is it nice? Do you see any flaws?
In terms of performance seems fine. And the buffer is not constantly increasing.
I use no threads or timers now :D



void ADCStream::readData()
{
int dataBegin;
int dataEnd;

ADCData += ADCPort->readAll();

while(ADCData.size()>=4)
{
dataBegin = ADCData.indexOf('A',0);
dataEnd = dataBegin + 3;

if((dataBegin != -1) && (dataEnd != -1) && (ADCData.size() >= dataEnd+1) && (ADCData.at(dataEnd) == 'Z'))
{
QByteArray tempData = ADCData.mid(dataBegin+1,2);
quint16 number=0;

samplesReceived++;

number = (tempData.at(0) & 0x00FF)<<8;
number |= tempData.at(1) & 0x00FF;

hardMedia += (double)((double)number*((double)1/SAMPLES_END));

if(samplesReceived == SAMPLES_END)
{
emit dataReady((quint16)hardMedia);
samplesReceived = 0;
hardMedia = 0;
}
ADCData.remove(0,dataEnd+1);//get next data
}
}
}