Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

Synchronous/Simultaneous Analog Input and Output - DAQmx on PCI6052E

Good morning,

I'm in the process of converting some traditional NI-DAQ to DAQmx and am running into a bit of trouble while attempting to sync up my analog output and input.  Our experimental protocol calls for the presentation of a sound stimulus to the subject, then recording (1 and 2) the neural output from two units in response to the stimulus, and (3) the output stimulus after it has been passed through our filters and amplifiers.  Our setup right now has the stimulus delivered via PCI6052E to Dev1/ai1 to the filter/amplifier system; the output of the amplifier goes to the speaker and into Dev1/ai7.  Dev1/ai0 and Dev1/ai1 are the input from the neural recordings.

Our Traditional NI-DAQ code called for the output to be triggered from the input as below:

iStatus = Select_Signal (iDevice, ND_IN_START_TRIGGER, ND_AUTOMATIC, ND_DONT_CARE);
iStatus = Select_Signal (iDevice, ND_OUT_START_TRIGGER, ND_IN_START_TRIGGER, ND_LOW_TO_HIGH);

I currently have the channels configured as follows:

DAQmxCreateTask("",&inputTaskHandle);
DAQmxCreateAIVoltageChan(inputTaskHandle,"Dev1/ai0","",DAQmx_Val_NRSE,-10.0,10.0,DAQmx_Val_Volts,NULL);
DAQmxCreateAIVoltageChan(inputTaskHandle,"Dev1/ai1","",DAQmx_Val_NRSE,-10.0,10.0,DAQmx_Val_Volts,NULL);
DAQmxCreateAIVoltageChan(inputTaskHandle,"Dev1/ai7","",DAQmx_Val_NRSE,-10.0,10.0,DAQmx_Val_Volts,NULL);
DAQmxCfgSampClkTiming (inputTaskHandle, "", 32000.0, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,ulFileLength); // (ulFileLength is the length of the stimulus to be played)

DAQmxCreateTask("",&outputTaskHandle);
DAQmxCreateAOVoltageChan(outputTaskHandle,"Dev1/ao1","",-10.0,10.0,DAQmx_Val_Volts,NULL);
DAQmxCfgSampClkTiming (outputTaskHandle, "ai/SampleClock", 32000.0, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,ulFileLength);                 DAQmxSetAnlgEdgeStartTrigSrc(outputTaskHandle,"ai/StartTrigger");

// Record voltage input to datin for plotting on a strip chart
iStatus = DAQmxReadAnalogF64(inputTaskHandle,ulFileLength,10.0,DAQmx_Val_GroupByScanNumber,datin,3*ulFileLength,&written,NULL);
// Record binary values for data analysis later
iStatus = DAQmxReadBinaryI16(inputTaskHandle,ulFileLength,10.0,DAQmx_Val_GroupByScanNumber,datbin,3*ulFileLength,&written,NULL);
iStatus = DAQmxWriteAnalogF64(outputTaskHandle,ulFileLength,0,10.0,DAQmx_Val_GroupByChannel,data,&written,NULL);
                   
// fwrite and stripchart code goes here

DAQmxStartTask(outputTaskHandle);
DAQmxStartTask(inputTaskHandle);
                   
while( !done )
{
    DAQmxIsTaskDone(outputTaskHandle,&done);
               
    if( !done )
    Delay(0.1);
}

if( outputTaskHandle!=0 )
{
    DAQmxStopTask(outputTaskHandle);
    DAQmxClearTask(outputTaskHandle);
}   
               
if( inputTaskHandle != 0 )
{
    DAQmxStopTask(inputTaskHandle);
    DAQmxClearTask(inputTaskHandle);
}

When I run the code like this, the program plays the song initially but then hangs on the while(!done) part; commenting that portion out obviously causes the output to never be produced.  I've also tried manipulating the example recommended here: http://forums.ni.com/ni/board/message?board.id=250&message.id=15846&query.id=12460#M15846, but in this case, the stimulus never started and the system remains hanging.

Thanks so much for any help that can be provided.

- Gilberto Grana


0 Kudos
Message 1 of 8
(6,328 Views)
Ok, I got the synchronization working just fine, but now a new problem arises when I try to do buffered reads/writes.

Here's the essential bit of code thus-far:

// Start with creating input and output tasks and channels

DAQmxCreateTask("",&inputTaskHandle);
DAQmxCreateAIVoltageChan(inputTaskHandle,"Dev1/ai0","",DAQmx_Val_NRSE,-10.0,10.0,DAQmx_Val_Volts,NULL);
DAQmxCreateAIVoltageChan(inputTaskHandle,"Dev1/ai1","",DAQmx_Val_NRSE,-10.0,10.0,DAQmx_Val_Volts,NULL);
DAQmxCreateAIVoltageChan(inputTaskHandle,"Dev1/ai7","",DAQmx_Val_NRSE,-10.0,10.0,DAQmx_Val_Volts,NULL);
DAQmxCfgSampClkTiming (inputTaskHandle, "", 32000.0, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,ulAOHalfCount);
GetTerminalNameWithDevPrefix(inputTaskHandle,"ai/StartTrigger",trigName);
              
DAQmxCreateTask("",&outputTaskHandle);
DAQmxCreateAOVoltageChan(outputTaskHandle,"Dev1/ao1","",-10.0,10.0,DAQmx_Val_Volts,NULL);
DAQmxCfgSampClkTiming (outputTaskHandle, "", 32000.0, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps,ulAOHalfCount);                                    DAQmxSetDigEdgeStartTrigSrc(outputTaskHandle,trigName); //Will this work?

// I then want to pad the input and output with zeros as stored in buffers of 500 ms each (16000 samples @ 32000 S/s)
              
while ((ulWriteFlag < (ulNumPostBufs + ulNumPreBufs)) && (iStatus == 0))
{
   
    if (ulWriteFlag < (ulNumPostBufs + ulNumPreBufs))
    {
        if (ulWriteFlag < ulNumPreBufs) //write two zero padded prebuffers
        {
            ulWriteFlag+=1;
            iStatus = Clear1D (pdAOHalfBuffer, ulAOHalfCount);
        }
                       
        else if (ulFileRemaining == 0)   //start writing zero padded postbuffers
        {
            ulCurWrite = 0;
            ulWriteFlag+=1;
            iStatus = Clear1D (pdAOHalfBuffer, ulAOHalfCount);
        }
                       
        else if (ulFileRemaining  < ulAOHalfCount) //then we dont have a full buffer of data to write, so we pad with zeros
        {
            ulCurWrite = ulFileRemaining; // ulFileRemaining is the amount of output data left to produce
            iStatus = fread (pdAOHalfBuffer, sizeof(double), ulCurWrite, IFID); // IFID is a pointer to the data
            for (i=ulCurWrite;i<ulAOHalfCount;i++) pdAOHalfBuffer[i]=0.0; // padding pdAOHalfBuffer with zeros
        }       
       
        else // write one halfbuffer
        {
            ulCurWrite = ulAOHalfCount;                               
            iStatus = fread (pdAOHalfBuffer, sizeof(double), ulCurWrite, IFID);
        }
                       
        ulFileRemaining -= ulCurWrite;   
        iStatus = DAQmxWriteAnalogF64(outputTaskHandle,ulAOHalfCount,0,0,DAQmx_Val_GroupByChannel,pdAOHalfBuffer,&written,NULL); // Here I ask the task to write what ever is in the buffer to the output channel
                        

    }   
                   
    DAQmxStartTask(outputTaskHandle);
    DAQmxStartTask(inputTaskHandle);
                               
    if (iWriteFlag)
    {
        //write the three channels to disk
        iStatus = fwrite (piAIHalfBuffer, sizeof(short), ulAIHalfCount, OFID);
    }
                                   
    iStatus = PlotStripChart (panelHandle, PANEL_SPK_STRIPCHART, piAIHalfBuffer, ulAIHalfCount/iAINumChans, 2, 2, VAL_SHORT_INTEGER);
    iStatus = PlotStripChart (panelHandle, PANEL_SPK_STRIPCHART2, piAIHalfBuffer, ulAIHalfCount/iAINumChans, 0, 2, VAL_SHORT_INTEGER);
    iStatus = PlotStripChart (panelHandle, PANEL_SPK_STRIPCHART3, piAIHalfBuffer, ulAIHalfCount/iAINumChans, 1, 2, VAL_SHORT_INTEGER);
                  
    while( !done )
    {
        DAQmxIsTaskDone(outputTaskHandle,&done);
       
        if( !done )
            Delay(0.1);
    }
                   
    if ( outputTaskHandle!=0 )
    {
        DAQmxStopTask(outputTaskHandle);
    }
                   
    if ( inputTaskHandle!=0 )
    {
        DAQmxStopTask(inputTaskHandle);
    }
                   
}   //while loop
               
// DAQmx Stop Code
if( outputTaskHandle!=0 )
{
    DAQmxClearTask(outputTaskHandle);
}   
               
if( inputTaskHandle != 0 )
{
    DAQmxClearTask(inputTaskHandle);
}

Now, with the task as written here, samples are acquired and written, but data is lost between iterations of the loop (tested by inputting a sinewave and noticing dropped samples).  When I set the modes to continuous - and use DAQmxCfgInputBuffer and DAQmxCfgOutputBuffer to set the size to 16000 samples - the program hangs on the DAQmxIsTaskDone step.  Any suggestions?  Thanks.

- Gilberto
0 Kudos
Message 2 of 8
(6,314 Views)

Hi Gilberto,

 

Thank you for posting to the NI forums.  Are you receiving an error during the while loop – if so, what is the error number?

 

There is a great NI Developer Zone article with examples programs written in C that perform very similar functionality to what you need.  Download the 3022.zip file, and check out the ANSI C files.

 

DAQmx - Simultaneously Started AI/AO - LabVIEW - CVI - ANSI C - VB.NET

 

From your code, I can see a couple of changes that should probably be made to your program. 

 

  1. Change your sample mode to continuous samples instead of finite samples.  This way you’ll be able to read samples in your while loop continuously.
  2. Add the analog read function to your while loop so you read samples during each iteration.

     

I hope this helps – please post back if you have further questions.

 

Ed W.

Applications Engineer

National Instruments

0 Kudos
Message 3 of 8
(6,309 Views)
Ed,

Thanks for your response; unfortunately, making the changes you recommend do not really help; if I do both, then the first while loop activates, placing one zero-padded buffer into the output.  If I put the read statement into the second while-loop - the one containing the "DAQmxIsTaskDone" command - then the board continues to read from the input indefinitely, while no stimulus is produced from the output since it's stuck with what's in its first buffer.  "done" is continuously 0 as a result.

The example you gave doesn't really capture what I want to do; in the example, the input/output continuously produce a waveform, regardless of how many samples I tell it to read.  What I want to do is play out two zero-padded buffers, the stimulus, then two more zero-padded buffers and that's it, all the while reading in whatever is going into the input channels at the same time.  The set of code I provided in my second message achieves this, but I assume that because it's in finite acquisition mode, I am getting dropped samples between buffers, whereas if I set it to continuous, the output repeats one buffer while the input goes on indefinitely.

Thanks again for your help.

- Gilberto
0 Kudos
Message 4 of 8
(6,302 Views)
I'm bringing this up again in the hopes that I can get my issues resolved, as I am really starting to get frustrated and stressed out about this situation.  I've read through many examples that claim to perform synchronous data acquistion, but they all seem to focus on coupling analog and digital input, which I don't want.  There are also many examples that deal with analog input and output, but these require me to tell the system to acquire data indefinitely; this, also, is not what I'm looking for.

Another example I came across suggests the use of DAQmxRegisterEveryNSamplesEvent (C:\Program Files\National Instruments\NI-DAQ\Examples\DAQmx ANSI C\Synchronization\Multi-Function\ContAI-Read Dig Chan\ContAI-Read Dig Chan.c).  I have it implemented in my code as follows:

iStatus = Clear1D(pdAOHalfBuffer, ulAOHalfCount);
DAQmxErrChk (DAQmxWriteAnalogF64(outputTaskHandle, ulAOHalfCount, 0, 0, DAQmx_Val_GroupByChannel, pdAOHalfBuffer, &written, NULL));
               
DAQmxErrChk (DAQmxRegisterEveryNSamplesEvent(outputTaskHandle,DAQmx_Val_Transferred_From_Buffer,ulAOHalfCount,0,EveryNCallbackOut,NULL));
DAQmxErrChk (DAQmxRegisterEveryNSamplesEvent(inputTaskHandle,DAQmx_Val_Acquired_Into_Buffer,ulAOHalfCount,0,EveryNCallbackIn,NULL));
               
while ((ulWriteFlag < (ulNumPostBufs + ulNumPreBufs)) && (iStatus == 0))
{
    DAQmxErrChk (DAQmxStartTask(outputTaskHandle));
    DAQmxErrChk (DAQmxStartTask(inputTaskHandle));
                  
    if( outputTaskHandle )
    {
        DAQmxStopTask(outputTaskHandle);
    }
    if( inputTaskHandle )
    {
        DAQmxStopTask(inputTaskHandle);
    }
}

The tasks referenced above create channels and sychronise the output to the input; those have been troubleshot before and work well.  The callback functions are as follows:

int32 CVICALLBACK EveryNCallbackOut(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData)
{
    int    i, isOn;
    int32       error=0, written;
    char        errBuff[2048]={'\0'};

    if (ulWriteFlag < ulNumPostBufs + ulNumPreBufs)
    {
                   
        if (ulWriteFlag < ulNumPreBufs)
        {
            ulCurWrite = 0;
            ulWriteFlag+=1;
            iStatus = Clear1D (pdAOHalfBuffer, ulAOHalfCount);
        }
       
        else if (ulFileRemaining == 0)
        {
            ulCurWrite = 0;
            ulWriteFlag+=1;
            iStatus = Clear1D (pdAOHalfBuffer, ulAOHalfCount);
        }
                       
        else if (ulFileRemaining  < ulAOHalfCount)
        {
            ulCurWrite = ulFileRemaining;
            iStatus = fread (pdAOHalfBuffer, sizeof(double), ulCurWrite, IFID);
            for (i=ulCurWrite;i<ulAOHalfCount;i++) pdAOHalfBuffer[i]=0.0;
        }       
       
        else
        {
            ulCurWrite = ulAOHalfCount;                               
            iStatus = fread (pdAOHalfBuffer, sizeof(double), ulCurWrite, IFID);
        }
                       
        ulFileRemaining -= ulCurWrite;   
       
    }   
                               
    DAQmxErrChk (DAQmxWriteAnalogF64(outputTaskHandle,ulAOHalfCount,0,0,DAQmx_Val_GroupByChannel,pdAOHalfBuffer,&written,NULL));
   
Error:
    if( DAQmxFailed(error) ) {
        DAQmxGetExtendedErrorInfo(errBuff,2048);
        if( outputTaskHandle ) {
            DAQmxStopTask(outputTaskHandle);
            DAQmxClearTask(outputTaskHandle);
            outputTaskHandle = 0;
        }
        if( inputTaskHandle ) {
            DAQmxStopTask(inputTaskHandle);
            DAQmxClearTask(inputTaskHandle);
            inputTaskHandle = 0;
        }
        printf("DAQmx Error: %s\n",errBuff);
    }
    return 0;
}

int32 CVICALLBACK EveryNCallbackIn(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData)
{
    int isOn;
    int32       error=0, read;
    char        errBuff[2048]={'\0'};
  
    DAQmxErrChk (DAQmxReadBinaryI16(inputTaskHandle,ulAOHalfCount,10.0,DAQmx_Val_GroupByScanNumber,piAIHalfBuffer,ulAIHalfCount,&read,NULL));
   
    if (iWriteFlag)
    {
        iStatus = fwrite (piAIHalfBuffer, sizeof(short), ulAIHalfCount, OFID);
    }
                           
Error:
    if( DAQmxFailed(error) ) {
        DAQmxGetExtendedErrorInfo(errBuff,2048);
        if( outputTaskHandle ) {
            DAQmxStopTask(outputTaskHandle);
            DAQmxClearTask(outputTaskHandle);
            outputTaskHandle = 0;
        }
        if( inputTaskHandle ) {
            DAQmxStopTask(inputTaskHandle);
            DAQmxClearTask(inputTaskHandle);
            inputTaskHandle = 0;
        }
        printf("DAQmx Error: %s\n",errBuff);
    }
    return 0;
}


The gist of the code is as follows:  Load the output buffer and write to the channel, then register the events and start a while-loop where the tasks are started and stopped.  The purpose of the loop is to allocate the appropriate data into the buffer to be played out from the output; the format should be two zero-padded buffers, followed by the stimulus broken up into chunks the size of the buffer - any amount of stimulus "left over" should be padded with zeros.  Finally, two more zero-padded buffers are produced at the output.  All the while, whatever is coming into the input channels is read into the buffer and then written to a data file.

So far, this setup works even worse than what I had before; whereas previously I could get synchronous I/O albeit without continuous read/write - that is, samples would be dropped between buffers - now I cannot get any activity at all; it seems as if the program does not access the events at all and almost skips over starting the tasks.

- David
0 Kudos
Message 5 of 8
(6,284 Views)

Hi,

 

I’m sorry to hear that you’re getting frustrated.  Losing samples between iterations of your while loop is expected behavior for finite acquisition.  There is no way around losing samples in finite mode if you are using a loop to capture them.  If you do continuous acquisition, you shouldn’t lose any samples. 

 

If you do need finite mode, try to capture all of your samples in a single buffer rather than looping to acquire them. 

 

To synchronize the inputs and outputs, route the AI Start Trigger (“Dev1/ai/StartTrigger”) to the trigger source to configure a digital start trigger for your analog output.

 

int32 DAQmxCfgDigEdgeStartTrig (TaskHandle taskHandle, const char triggerSource[], int32 triggerEdge);

 

Lastly, it is important to perform proper error handling when writing your code.  Unless I’m missing something, it appears that you’re storing the error code in a variable, but never actually checking that variable to see if an error occurred. 

 

Ed W.

Applications Engineer

National Instruments

0 Kudos
Message 6 of 8
(6,254 Views)
Ed,

Thank you for your response, and I apologize for my outburst in that thread.

Tackling your comments out of order:

- I have recently changed the code a bit to implement a DAQmx error check as illustrated in a number of examples in the NI folder, so no longer is anything of that sort simply stored as a variable.
- My code now uses continuous sampling, and I have found that under the right conditions, it CAN work, but I am still running across some errors that I have not been able to circumvent, as I have not been able to find any examples that really address the situation.

I posted a separate thread dealing with this matter here: http://forums.ni.com/ni/board/message?board.id=250&message.id=28526.  It deals with trying to perform DAQmxWriteAnalogF64 calls within a while loop without encountering an "attempt to write a sample beyond final sample generated" error, and all the while making sure that the buffer is "ready" to be read; in other words, I am attempting to replicate the functionality of DAQ_DB_HalfReady and WFM_DB_HalfReady from the Traditional NI-DAQ function list.

Thanks,

- Gilberto
0 Kudos
Message 7 of 8
(6,247 Views)

Hi,

 

What is the error number associated with that message – is it -200288?  If so, there is actually a Knowledge Base entry that addresses this issue.  I’ve placed a link to the article below.

 

Why Am I Getting Error -200288?

 

Ed W.

Applications Engineer
National Instruments

0 Kudos
Message 8 of 8
(6,223 Views)