Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

Fast single-write using DAQmx

Hi,

My application is the following:

Loop
calculate A;
write A;
EndLoop;

Where A is a single i16 sample, "calculate" is a simple constant assignment, and "write" is DAQmxWriteBinaryI16(AutoStart = True). I'm using a PCI 6052E card, on a 3GHz PentiumIV, with HT.

To my understanding, the new DAQmx API is advertized to support up to 50 kHz refresh rates using the above construct. However, I only get less than 1kHz, which is the same as with the traditional DAQ API.

How does one obtain the claimed 50 kHz rate?


And a different qustion:

How can the processor affinity of the NIDAQ be changed? It appears the continuous waveform generation with the traditional DAQ API unnecessarily interrupts both logical processors every few msec, even when the buffer is longer than that. I would like to set the affinity to only one of the processors, to have my application run uninterrupted. The new DAQmx claims to be thread optimized. Does it then support affinity changes?

Thank you.
0 Kudos
Message 1 of 8
(4,330 Views)
Hi Lorin, there are a couple of ways to speed up single-point performance. A simple one is to just call DAQmxStartTask before you enter your loop (and DAQmxStopTask when you leave your loop). When the task stays in the started state, then single point writes will be much faster. Even though you're setting the autostart parameter to true, since you're using on-demand timing mode, the task doesn't stay in the started state unless you've called an explicit start task before the write. The only downside of doing an explicit start is that the AO subsystem of the device will stay reserved for the duration of your while loop. That won't be a problem for you unless you want to perform a second AO task on the same device in a separate thread.

The other way to speed up performance is to use hardware timing, using the HW-timed single point timing mode. This gives you all of the speed benefits in the above method, and the additional benefit that you will receive a warning from the DAQmxWrite if you are not keeping up with the rate specified by the hardware timing.

As for the CPU usage when doing continuous waveform generation: this is going to depend somewhat on your data transfer method. If you're using interrupt-driven wfm generation, then CPU usage will be higher than when using DMA. I don't remember if the device still needs to interrupt occasionally when using DMA. I can try to find out if there is a way to set the processor affinity for interrupts. Neither traditional DAQ nor DAQmx exposes a way through their API for you to configure this, but Windows may have a way that you can configure this yourself in your application.

Joe
0 Kudos
Message 2 of 8
(4,318 Views)
Here is a tool that will supposedly allow you to configure the CPU affinity for interrupts. I haven't tried it, so I don't know how well it works. It was developed for Win2K, but I have no reason to think it wouldn't work with XP, too. I'd be interested in knowing if it's useful with DAQ devices.

http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q252867&

--Joe

P.S. I'm also not sure if the utility above affects the affinity of DPCs sheduled from ISRs. Since DPCs often do the bulk of the work after a device interrupt, the answer to this question will determine how useful this utility is for your ultimate purpose.
0 Kudos
Message 3 of 8
(4,311 Views)
Joe,

Thanks for your replies. As you indicated, I tested the StartTask function, and the results are very good. A Write operation executes at ~800kHz, and a Read operation runs at ~125kHz. These rates exceed my initial expectations, and, indeed, NI's ("up to 50kHz"). I wonder how much of this is limited by the computer and how much by the DAQ card.

I tested the hardware timing with Read, with puzzling results. Since there is no example on how to use HWTiming, my understanding is that it starts a continuous ADC operation, and stores the samples internally (i.e., one sample). When the Read function is called, it returns the sample already read internally. I can see how this should be more efficient. However, I get a rate of only a few kHz. What am I doing wrong? Do you have a sample code?


I had tried already the interrupt filter you mentioned, and it wasn't clear if it worked or not. Moreover, the affinity of what should be changed, to affect the double buffered operation which runs in background? What I was doing, in fact, was to mimic the HWTiming of the new API. It worked fine except for interrupting the main thread. Now the new API makes that unnecessary.

Lorin.
0 Kudos
Message 4 of 8
(4,275 Views)
Hi Lorin,

You should be able to find text based examples of a Continuous Hardware timed acquisition at C:\Program Files\National Instruments\NI-DAQ\Examples. If you cannot find an example which shows what you need, please respond with more details (what you want to accomplish, what programming language you are using) and I will see if I can find one for you.

Regards,
Sean C.
0 Kudos
Message 5 of 8
(4,217 Views)
Hi Sean,

There is no example I could find. I use Delphi (why don't you support it anymore?!), but an example in any programming language would be fine (no LabView).
I want to read/calculate/write in a loop as fast as possible. Using the regular R/W functions (not hardware timed) I get ~100 kHz. I was hoping to get more with hardware timed, but I don't quite know how to use it. There is very little in the help file.
I would appreciate your help with this.

Regards,
Lorin.
0 Kudos
Message 6 of 8
(4,137 Views)
Hi Lorin,

I am attaching a hardware timed, simultaneous analog input/output example program written in C#. This example makes use of the NI-DAQmx driver. PLease let me knowif you have any further questions.

Regards,
Sean C.
0 Kudos
Message 7 of 8
(4,125 Views)
Hi,

I'm trying to do continuous reading/writing, but with the output being a function of the input. As I understand, the above example constantly outputs the values returned by FunctionGenerator(...). I want to take the most recent inputs and use them to create new outputs.

I have C code that works, but the buffersize needs to be rather large to avoid running out of readable samples. (This just merges 4 inputs to 2 outputs by summing, but the idea is to implement a filter in software.)

TaskHandle OtaskHandle = 0;
TaskHandle ItaskHandle = 0;

main()
{
...
    /*********************************************/
    // DAQmx Configure Code
    /*********************************************/
    DAQmxErrChk (DAQmxCreateTask("",&ItaskHandle));
    DAQmxErrChk (DAQmxCreateAIVoltageChan(ItaskHandle,"Dev1/ai0:3","",DAQmx_Val_Cfg_Default,-10.0,10.0,DAQmx_Val_Volts,NULL));
    DAQmxErrChk (DAQmxCfgSampClkTiming(ItaskHandle,NULL, samplerate,DAQmx_Val_Rising,DAQmx_Val_ContSamps,blocksize));

    DAQmxErrChk (DAQmxRegisterDoneEvent(ItaskHandle,0,DoneCallback,NULL));


    /*********************************************/
    // DAQmx Configure Code
    /*********************************************/
    DAQmxErrChk (DAQmxCreateTask("",&OtaskHandle));
    DAQmxErrChk (DAQmxCreateAOVoltageChan(OtaskHandle,"Dev1/ao0:1","",
                                                                                -MAXOUT,MAXOUT,
                                                                                DAQmx_Val_Volts,NULL));
    DAQmxErrChk (DAQmxCfgSampClkTiming(OtaskHandle,NULL,samplerate,
                                                                         DAQmx_Val_Rising,
                                                                         DAQmx_Val_ContSamps,blocksize));
   
    DAQmxErrChk (DAQmxRegisterDoneEvent(OtaskHandle,0,DoneCallback,NULL));
   

    //
    DAQmxErrChk (DAQmxRegisterEveryNSamplesEvent(ItaskHandle,DAQmx_Val_Acquired_Into_Buffer,blocksize,0,EveryNCallback,NULL));
 
   
    /*********************************************/
    // DAQmx Write Code
    /*********************************************/
    DAQmxErrChk (DAQmxWriteAnalogF64(OtaskHandle,
                                                                     blocksize,0,10.0,
                                                                     DAQmx_Val_GroupByScanNumber,
                                                                     dataout,NULL,NULL));
   
    /*********************************************/
    // DAQmx Start Code
    /*********************************************/
    DAQmxErrChk (DAQmxStartTask(OtaskHandle));
   
    /*********************************************/
    // DAQmx Start Code
    /*********************************************/
    DAQmxErrChk (DAQmxStartTask(ItaskHandle));

    printf("# Acquiring samples continuously. Press Enter to interrupt\n");
    getchar();
...
}

int32 CVICALLBACK EveryNCallback(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData)
{
    int32       error=0;
    char        errBuff[2048]={'\0'};
    int32       read=0;
    float64     data[blocksize*NINCHANNELS];
    int i;

    /*********************************************/
    // DAQmx Read Code
    /*********************************************/
    DAQmxErrChk (DAQmxReadAnalogF64(taskHandle,blocksize,10.0,
                                                                   DAQmx_Val_GroupByScanNumber ,
                                                                    data,blocksize*4,
                                                                    &read,NULL));
    if( read>0 ) {
        for (i = 0; i < read; i++) {
            dataout[i * NOUTCHANNELS] =
                (data[i * NINCHANNELS + 2] + data[i * NINCHANNELS + 0]);
            dataout[i * NOUTCHANNELS + 1] =
                (data[i * NINCHANNELS + 3] + data[i + NINCHANNELS + 1]);
        }
    }

    DAQmxErrChk (DAQmxWriteAnalogF64(OtaskHandle,blocksize,0,10.0,
                                                                     DAQmx_Val_GroupByScanNumber,
                                                                     dataout,NULL,NULL));

Error:
    if( DAQmxFailed(error) ) {
        DAQmxGetExtendedErrorInfo(errBuff,2048);
        /*********************************************/
        // DAQmx Stop Code
        /*********************************************/
        DAQmxStopTask(taskHandle);
        DAQmxClearTask(taskHandle);
        printf("DAQmx Error: %s\n",errBuff);
    }
    return 0;
}

0 Kudos
Message 8 of 8
(3,755 Views)