05-25-2012 01:47 PM
I'm wrting a large data-acquisition and processing program using LabWindows/CVI 2010, Windows XP.
In the data acquistion section, I have a DAQmx task (set up using MAX) that will acquire a number of points, typically 1000 @ 50 kHz, on receipt of a trigger.
After the task is loaded with
DAQmxLoadTask ("task_name", &DAQTask_Handle);
A handler is installed with
DAQmxErrChk(DAQmxRegisterEveryNSamplesEvent (DAQTask_Handle,
DAQmx_Val_Acquired_Into_Buffer,
PointsToAcq,
0, //non-synchronous event callback, called from DAQmx thread
CB_Level1_DataRdy, NULL));
The trigger frequency is supposed to be 30 Hz, so in order for things to happen quickly enough I've already determined that the callback event needs to be non-synchronous. This is what is turning this into a multi-threading exercise.
A threadsafe queue is also set up with
status = CmtNewTSQ (DataQueueListSize(), sizeof (float64), OPT_TSQ_DYNAMIC_SIZE, &DataQueueHandle);
A callback (from the receiving module, in another dll) is installed with
status = CmtInstallTSQCallback (DataQueueHandle, EVENT_TSQ_ITEMS_IN_QUEUE, PointsToAcq, callback_func, NULL, CmtGetCurrentThreadID(), NULL); (where callback_func is the callback routine passed in through a subroutine call. )
The DAQ callback, then looks something like this:
int32 CVICALLBACK CB_Level1_DataRdy (TaskHandle taskHandle,
int32 everyNsamplesEventType,
uInt32 nSamples,
void *callbackData) {
//set priority
status = CmtGetCurrentThreadPriority (&Priority);
status = CmtSetCurrentThreadPriority (THREAD_PRIORITY_TIME_CRITICAL);
// setup stuff here
.......
// read data from DAQmx
DAQmxStatus = DAQmxReadAnalogF64 (taskHandle, DAQmx_Val_Auto, 0,
DAQmx_Val_GroupByChannel,
DataBuffer, //float64 array large enough to receive the samples
BufferSize, &samplesRead, 0);
//do some processing here
status = CmtWriteTSQData (DataQueueHandle, DataBuffer, samplesRead, 0, NULL);
//restore thread priority
CmtSetCurrentThreadPriority (Priority);
}
So far, so good.Note that the callback above is called from the DAQmx thread; the queue is used to buffer the data with the display, which is accomplished in the main process thread. It was necessary to set the DAQmx thread priority to THREAD_PRIORITY_TIME_CRITICAL to ensure that the handler doesn't get interrupted by later events.
On the other end, the queue callback is defined as
void CVICALLBACK Receiver_DataAcqHandler (CmtTSQHandle queueHandle, unsigned int event, int value, void *callbackData) {
num_data_read = CmtReadTSQData (queueHandle,
ReceiverVoltsBuffer,
samplesRead,
TSQ_INFINITE_TIMEOUT, 0);
//get rid of old handle
if (g_iPlotHandleReceiver) {
status = DeleteGraphPlot (MainWindowHandle, PANEL_GRAPH, g_iPlotHandleReceiver, VAL_DELAYED_DRAW);
g_iPlotHandleReceiver = 0;
}
//plot data
g_iPlotHandleReceiver = PlotWaveform (MainWindowHandle, PANEL_GRAPH, ReceiverVoltsBuffer,
num_data_read, VAL_DOUBLE, 1.0, 0.0, 0.0, 1.0,
VAL_THIN_LINE, VAL_NO_POINT, VAL_SOLID, 1, VAL_GREEN);
}
This version works nicely. I instrumented the program with some writes to the DAQ card's digital IO lines: the call to the DAQ callback taks about 250 us, being called immediately after the 1000 points have been acquired. No problem. The queue call takes about 5 milliseconds; what's interesting, though, is that you can watch the time-slicer working: the delay from the end of teh DAQ callback to the start of the queue callback varies from 0 to 30 ms, in a regular fashion. It starts out at 30, walks up to 0, then resets to 30 ms.
Question 1: is there a way to control this, so that the queue callback is called immediately? I've tried changing the thread priority, and it did nothing.
If you notice, using this scheme I have to double-buffer the data: DAQ->DataBuffer->queue->ReceiverVoltsBuffer->graph control.
I tried to shorted things up a bit by using the facility to get a pointer into the thread-safe queue. In this case, the queue callback looked like this:
void CVICALLBACK Receiver_DataAcqHandler (CmtTSQHandle queueHandle, unsigned int event, int value, void *callbackData) {
float64 *F64_data_p;
status = CmtGetTSQReadPtr (queueHandle, &F64_data_p, &num_data_read);
//get rid of old handle
if (g_iPlotHandleReceiver) {
status = DeleteGraphPlot (MainWindowHandle, PANEL_GRAPH, g_iPlotHandleReceiver, VAL_DELAYED_DRAW);
g_iPlotHandleReceiver = 0;
}
//plot data
g_iPlotHandleReceiver = PlotWaveform (MainWindowHandle, PANEL_GRAPH, F64_data_p,
num_data_read, VAL_DOUBLE, 1.0, 0.0, 0.0, 1.0,
VAL_THIN_LINE, VAL_NO_POINT, VAL_SOLID, 1, VAL_GREEN);
//return pointer
status = CmtReleaseTSQReadPtr (queueHandle, num_data_read);
}
This doesn't work well at all. In addition to the time-slicer delays, the actual routine can take up to 30 ms. I can only conclude that having the queue checked out for the duration of the PlotWaveform call messes things up somehow.
Question 2: can somebody help me under stand this timing issue? Something obscure is going on here.
05-29-2012 06:55 PM
Hi pblase,
Can you clarify what the actual issue is here? Are you getting error messages or losing data? Since you are using a queue, the timing of the data collection and processing should be decoupled.
07-17-2014 05:52 AM
Was this issue ever resolved? I am having a similiar problem using a PCI 6534 and TSQ's
07-18-2014 10:47 AM
Hi bill.kim,
As the last poster commented, this issue is a little hard to understand. Could you clarify what your exact problem is? Is there an error message or lost data?
I would also strongly recommend starting a new thread for your issue, since this thread is relatively old and won't get as much attention.
Regards,