Digital I/O

cancel
Showing results for 
Search instead for 
Did you mean: 

Generation Buffer Control



@FrustratedwithDAQmxHelp wrote:

Paul,

I appreciate your reply. I have looked at all of the examples that you refer to. Without those I would certainly have gotten nowhere. However, they are limited. I can honestly tell you with what you have given me on the double buffering, I will not succeed. There are far too many options and subtile nuances involved and resolving each takes far longer than I have time. It would be much better for either better documentation on the each function to be provided or for you to give me more details (like an actual example). In the mean time do you have any idea why my exiting code keeps output the state of the first buffer call.

 

Thanks Larry



This snippet of code might be helpful...

// continuous.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <NIDAQmx.h>
#include <stdio.h>
#include <string>
#include <stdlib.h>

#pragma comment(lib, "NIDAQmx.lib")

std::string getDAQmxErrorString()
{
    int32 size = DAQmxGetExtendedErrorInfo(NULL, 0);
    if (size > 0) {
        std::string buffer(size, 0);
        char *data = &buffer[0];
        DAQmxGetExtendedErrorInfo(data, size);
        std::string::iterator e = buffer.end();
        --e;
        buffer.erase(e);
        return buffer;
    } else {
        return std::string();
    }
}

void getDaqError(int32 error_code)
{
    std::string error = getDAQmxErrorString();
    printf("error %d\n%s\n", error_code, error.c_str());
    exit(1);
}

int main(int argc, char* argv[])
{
    TaskHandle task_handle;

    int32 error_code = DAQmxCreateTask("", &task_handle);

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    error_code = DAQmxCreateDOChan(
        task_handle,
        "Dev4/line0:15",
        "",
        DAQmx_Val_ChanForAllLines);

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    uInt32 total_generation = 100*1024*1024;

    error_code = DAQmxCfgPipelinedSampClkTiming(
        task_handle,
        "", // clock source
        50e6,
        DAQmx_Val_Rising,
        DAQmx_Val_FiniteSamps,
        total_generation);

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    error_code = DAQmxSetExportedSignalAttribute(
        task_handle,
        DAQmx_Exported_SampClk_OutputTerm,
        "pfi4");

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    error_code = DAQmxSetExportedSignalAttribute(
        task_handle,
        DAQmx_Exported_SampClk_Pulse_Polarity,
        DAQmx_Val_ActiveHigh);

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    error_code = DAQmxSetExportedSignalAttribute(
        task_handle,
        DAQmx_Exported_DataActiveEvent_OutputTerm,
        "pfi0");

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    error_code = DAQmxSetExportedSignalAttribute(
        task_handle,
        DAQmx_Exported_DataActiveEvent_Lvl_ActiveLvl,
        DAQmx_Val_ActiveLow);

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    error_code = DAQmxSetWriteRegenMode(
        task_handle,
        DAQmx_Val_DoNotAllowRegen);

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    // amount of data to write before starting.  Eight megasamples
    // works on one of my machines.
    uInt32 amount_written = 1024*1024*8;
    uInt16 *buffer = new uInt16[total_generation];
    memset(buffer, 0, sizeof(uInt16)*total_generation);

    int32 samples_written = 0;

    error_code = DAQmxWriteDigitalU16(
            task_handle,
            amount_written,
            false,
            10.0, // timeout
            DAQmx_Val_GroupByChannel,
            buffer,
            &samples_written,
            0);

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    error_code = DAQmxStartTask(task_handle);

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    error_code = DAQmxWriteDigitalU16(
            task_handle,
                        total_generation - amount_written,
            false,
            10.0, // timeout
            DAQmx_Val_GroupByChannel,
            &buffer[amount_written],
            &samples_written,
            0);

    if (error_code < 0) { getDaqError(error_code); return 1;  }

    error_code = DAQmxWaitUntilTaskDone(task_handle, 100.0);
    if (error_code < 0) { getDaqError(error_code); return 1;  }

    return 0;
}





0 Kudos
Message 11 of 17
(1,620 Views)
Jeff,

Thanks for the reply and example code. This is helpful. I have a couple of questions about your code.

First it appears you only call DAQmxWriteDigitalU16 twice. Once to output amount_written of words from buffer, starting with buffer[0] and the second time to output  total_generation - amount_written words from buffer, starting with word buffer[amount_written]. Is that correct?

If so, this is not totally anologies to my situation. First you have defined an array for the entire data set in buffer. I do not have enough memory to define an entire array of what I need to output. From what I am understanding, I need to define two buffers considerably smaller than the total data set, load these buffers and then call
DAQmxWriteDigitalU16 twice. Then I need monitor the first DAQmxWriteDigitalU16 call to see when it is done using the first buffer, so I can grab it, reload it and then call DAQmxWriteDigitalU16 again. Then I need to wait for the second DAQmxWriteDigitalU16 to finish using the second buffer so I can also grab it, reload it and then call DAQmxWriteDigitalU16 for a fourth time. This process would continue until all the data is generated.

1. Is this understanding correct?


2. Is there some function that I can call that tells me when a given
DAQmxWriteDigitalU16 is done using the passed buffer?

3. If I am off base can you correct my understanding?

Thanks for your help, Larry
0 Kudos
Message 12 of 17
(1,579 Views)


@FrustratedwithDAQmxHelp wrote:
Jeff,

Thanks for the reply and example code. This is helpful. I have a couple of questions about your code.

First it appears you only call DAQmxWriteDigitalU16 twice. Once to output amount_written of words from buffer, starting with buffer[0] and the second time to output  total_generation - amount_written words from buffer, starting with word buffer[amount_written]. Is that correct?

Yes.  The first write automatically sets the DAQmx Buffer size and gives DAQmx the first part of the data.  The second DAQmx write after starting the DAQmx task gives the remaining data.



If so, this is not totally anologies to my situation. First you have defined an array for the entire data set in buffer. I do not have enough memory to define an entire array of what I need to output. From what I am understanding, I need to define two buffers considerably smaller than the total data set, load these buffers and then call
DAQmxWriteDigitalU16 twice. Then I need monitor the first DAQmxWriteDigitalU16 call to see when it is done using the first buffer, so I can grab it, reload it and then call DAQmxWriteDigitalU16 again. Then I need to wait for the second DAQmxWriteDigitalU16 to finish using the second buffer so I can also grab it, reload it and then call DAQmxWriteDigitalU16 for a fourth time. This process would continue until all the data is generated.

1. Is this understanding correct?


2. Is there some function that I can call that tells me when a given
DAQmxWriteDigitalU16 is done using the passed buffer?

3. If I am off base can you correct my understanding?

Thanks for your help, Larry


The DAQmxWriteDigitalU16 is a synchronous call, when the function returns, you can modify the buffer that you passed in.  If the hardware is running and it needs to wait for space in its buffer, it will wait for space.

If you need to load the output data from disk or if you need to compute the output data in a loop, you can simply just write.  In pseudocode, it would be something like:

1. configure the DAQmx Task (create the task, create a channel, configure for pipelined sample clock, configure your exported signals, etc)

2.  Load the first segment of data, write it out w/ a DAQmxWrite function call

3. Call DAQmxStart

4. Load the next segment of data, write it out w/ a DAQmxWrite function Call

5. goto 4 until your done

Does this clarify things for you?

Jeff

Message Edited by jcarbonell on 10-19-2007 07:31 AM

0 Kudos
Message 13 of 17
(1,566 Views)
Thanks Jeff,
 
Sorry for being so slow on this. I guess I am so use to thinking on a low level, that when you say DAQmxWriteDigitalU16 will handle the additional calls, I find that hard to believe. If I am understanding you correctly, you are saying DAQmxWriteDigitalU16 will queu all of my additonal calls until the buffers become available.
 
How will it avoid lossing data as I am only clocking out so fast and I am sure I will generated it faster than it clock out? There is not enough memory in the machine for everything I am going to stream at it. Is it going to try to write it to disk? Would it not be better for me to wait until some of the buffers free up?
 
Thanks Larry
0 Kudos
Message 14 of 17
(1,572 Views)
Hi Larry,

Your first statement is correct in that DAQmxWriteDigitalU16 will queue the additional calls until the buffer becomes available.

In regards to your second question, I understand your concerns with memory usage with writing such a large array of data.  This is why you will want to generate the data in chunks which may require setting up some type of delay between calling the DAQmxWriteDigitalU16 function calls.  I believe the DAQmxRegisterEveryNSamplesEvent might help you accomplish this task.  This event counts the samples that are sent from PC memory to the DAQ onboard memory.  This will allow you to time exactly when you start loading your next array chunk from disk or start generating it to memory.  This will help you avoid running out of memory.  For example, lets say that you have 1GB of data you need to output.  First, you can load 200MB with the first DAQmxWriteDigitalU16 to the output buffer.  Now you can wait for 50,000,000 samples to be written (2 Bytes per sample so 100MB) and then you can start writing your next 100MB chunk of data with another DAQmxWriteDigitalU16.  You can continue to do this until you have written the entire 1GB of data.  This also guarantees that there will be at least 100MB of data in the buffer at all times.  This would essentially follow the same pseudocode mentioned by Jeff except adding a step between 3 and 4 to wait for this event.

This is demonstrated in a different type of application in the example found here.  You might like to take a look at this example and see how they use the event for analog output.

I hope this helps,
Paul C.
0 Kudos
Message 15 of 17
(1,538 Views)
Thanks Paul,
 
This is starting to make sense to me. However, the DAQmxRegisterEveryNSamplesEvent seems a bit unnecessarily complicated for my application. Can I not just stay in a while loop calling DAQmxGetWriteSpaceAvail until I see sufficient buffer space to begin writing to the buffer again? Or does this function create too much overhead?
 
Thanks again, Larry
0 Kudos
Message 16 of 17
(1,534 Views)


@FrustratedwithDAQmxHelp wrote:
Thanks Jeff,
 
Sorry for being so slow on this. I guess I am so use to thinking on a low level, that when you say DAQmxWriteDigitalU16 will handle the additional calls, I find that hard to believe. If I am understanding you correctly, you are saying DAQmxWriteDigitalU16 will queu all of my additonal calls until the buffers become available.
 
How will it avoid lossing data as I am only clocking out so fast and I am sure I will generated it faster than it clock out? There is not enough memory in the machine for everything I am going to stream at it. Is it going to try to write it to disk? Would it not be better for me to wait until some of the buffers free up?
 
Thanks Larry


Hi Larry,

What DAQmx does is it allocates a buffer for you. The hardware accesses only this buffer.  Usually, the buffer size is the size of the DAQmx Write that you perform before starting the task.  With this first write, DAQmx will memcpy your buffer into its buffer.  After you've started the task, DAQmx Write will wait for space in the buffer before performing the memcpy.  The device generates an underflow error (1) if the device is generating data faster than what the bus can give, or (2) if regeneration is disallowed and the software hasn't notified the hardware that it can transfer more data and the internal FIFOs have drained.

Also, if the amount of data in the DAQmx Write call is bigger than the buffer, the DAQmx write call will break up the writes into smaller writes.

It's probably more efficient if you just do your writes in a loop.  You may want to put the write loop in a separate thread if your application provides some kind of a GUI:

void writeLoop()
{
   bool started = false;
   configureDAQmxTask();
   while (haveMoreData()) {
       Write(Data);
       if (!started) { started = true; startDAQmxTask() }
   }
}

Hope this helps,

Jeff
0 Kudos
Message 17 of 17
(1,518 Views)