[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