[Qt-interest] Howto implement Hardware interface interaction using QThread
Matthias Pospiech
matthias.pospiech at gmx.de
Mon Mar 22 12:40:01 CET 2010
I have a Analog to Digital Converter PCI Card from National Instruments
which can collect data with a rate up to 1 MHz.
I only want to use 1000 values / second = 1 KHz to test the device.
The programming interface is pure C and the device driver is using its
own thread. The problem I have is that currently the gui can block the
device interface and vise versa.
Since my device interface only waits and is nothing doing inbetween I do
not see how to implement it correct using a QThread. Here wait does mean
wait for a callback from the device driver
which is somehow a Signal to Slot principle. However since I do not know
how to push it to a different thread, the locking mechanism runs in the
gui thread which makes it impossible to lock for reading due to the cpu
usage in the gui (for plotting) and thus the application freezes.
Here run (could also be called start) looks like this (Note: nothing is
inhereted from QThread):
void Task::run()
{
CheckDAQmxError(DAQmxRegisterEveryNSamplesEvent(
d->taskHandle,
ValueIntoBuffer, // The type of event you
want to receive.
d->numberSamplesPerChannel, // The number of samples
after which each event should occur.
0, // The callback function is called in a DAQmx
thread. This is the default value.
EveryNCallback, // The function that you
want DAQmx to call when the event occurs.
(void *)this));
CheckDAQmxError(DAQmxStartTask (d->taskHandle));
}
where 'DAQmxStartTask' starts the thread in the device driver and
'EveryNCallback' is the function that is called from the driver.
That is implemented as:
extern "C" {
int32 CVICALLBACK EveryNCallback(TaskHandle taskHandle,
int32 everyNsamplesEventType,
uInt32 nSamples,
void *callbackData)
{
if(( taskHandle!=0 ) && (callbackData)) {
// activate read Data
static_cast<Task*>(callbackData)->readData();
}
return 0;
}
}
and calls:
void Task::readData()
{
if ((d->taskCreated) && (d->readSettingsValid)) {
readAnalogData(d->numberSamplesPerChannel, d->timeout,
d->fillMode, d->arraySizeInSampels);
}
}
where readAnalogData itself calls 'writeDataRecieved'
void Task::writeDataRecieved(double * dataArray, size_t datasize)
{
// lock for writing
if (d->lock.tryLockForWrite(d->hardwareEventTimer/2.0))
{
qDebug() << QTime::currentTime().toString("ss.zzz") << "Lock
Write Data to Class";
// save size of old readDataArray
size_t oldSize = d->readDataArray.size();
// resize with new totalsize
d->readDataArray.resize(d->readDataArray.size() + datasize);
// copy from ... to ...
std::copy ( dataArray, dataArray + datasize,
d->readDataArray.begin() + oldSize);
d->lock.unlock();
emit readDataFromDeviceFinished();
} else {
d->lock.unlock();
qDebug() << "DAQ: Write Data to Class FAILED (lock
impossible)";
}
}
if I now want to read this data from the class I am doing
void Task::readDataRecieved(vector<double> & readArray, size_t
beginSize, size_t endSize)
{
// lock for reading
if (d->lock.tryLockForRead(d->hardwareEventTimer))
{
qDebug() << QTime::currentTime().toString("ss.zzz") << "Lock
Read Data from Class";
readArray.resize(endSize - beginSize);
std::copy ( d->readDataArray.begin() + beginSize,
d->readDataArray.begin() + endSize, readArray.begin());
} else {
d->lock.unlock();
qDebug() << "DAQ: Read Data from Class FAILED (lock impossible)";
}
}
The locking happens as:
--->
"DAQ timing: Callback triggered every 1.0 seconds"
"08.109 DAQ: Data Callback:event after 1000 values"
"08.109" Lock Write Data to Class
"08.109" Lock Read Data from Class
"09.109 DAQ: Data Callback:event after 1000 values"
DAQ: Write Data to Class FAILED (lock impossible)
"10.093 DAQ: Data Callback:event after 1000 values"
"10.093" Lock Write Data to Class
"10.093" Lock Read Data from Class
"11.093 DAQ: Data Callback:event after 1000 values"
DAQ: Write Data to Class FAILED (lock impossible)
"12.109 DAQ: Data Callback:event after 1000 values"
"12.109" Lock Write Data to Class
"13.109 DAQ: Data Callback:event after 1000 values"
"13.109" Lock Write Data to Class
"14.109 DAQ: Data Callback:event after 1000 values"
"14.109" Lock Write Data to Class
"14.296" Lock Read Data from Class
"15.109 DAQ: Data Callback:event after 1000 values"
<---
As one can see the read lock fails very often, and if read succeds
then write is likely to fail. Note that the time of locking is 1.0 second
I assume that the whole locking must be implemented in a QThread running
with exec.This would prevent the gui from blocking the whole read/write
mechanism.
However I have absolutely no idea how to implement that. Any special
hint or general implementation idea is welcome.
Matthias
More information about the Qt-interest-old
mailing list