I'm trying to get my M-series PCI-6221 board to do something quite simple using NI-DAQmx 9.5, without success. I want to acquire samples continuously from 4 channels in the 1-10kHz range at the same time as outputting a digital waveform at the same rate. This is clearly possible, as there is example C code (ContAI-ReadDigChan.c) showing how it can be done in DAQmx. Actually, I am using Delphi but making C calls to the DLL and want to do the equivalent "ContAI-WriteDigChan".
What is "unusual" is that I need the digital output to be switched as soon after the last channel in the scan group of AI channels has been acquired, rather than at the same time as the start of the first digitization (Dev1/ai/SampleClock). This is to set the conditions (LED light) for as much as the inter-sampling interval as possible before the next set of samples is acquired.
There doesn't appear to be a signal corresponding to all samples complete, e.g. Convert Clock pulses once for each channel acquired.
So I have tried to use one of the counters (Dev1/ctr0) to generate a suitable delay between Sample Clock's rising edge (start of AI scan) to provide a trigger for digital output at the correct time.
When I call DAQmxCreateCOPulseChanTime, I get the error -200077 "NIDAQ: Requested property is not a supported value for this property." for which the possible cause is:
"The operation you are trying to perform or the values you selected are not supported on the device you are using. You might be designating options for the data acquisition that are not available on your device, such as in triggering or clock configurations. Likewise, you might be trying to configure invalid ports or self-calibration parameters."
Am I correct to assume this is because timed delay isn't possible on the PCI-6221?
Is there another approach I can try to obtain the desired timing?
Thanks in advance,
There should be a way to configure a counter to do what you need, but I'm a LabVIEW guy and don't know the C syntax for the DAQmx driver call.
There's another way to approach this that also oughta work. There is a DAQmx property that can be set to control the "output behavior" of the AI sample clock. The default is to simply make a brief pulse at the beginning of sampling. The other option is to make the pulse last throughout the duration of all channels being sampled. In LabVIEW, I would use a DAQmx Export Signal property node, select the property "SampClk.OutputBehavior", and feed it the value "Level" (instead of the default of "Pulse").
By doing this, you could use the *falling* edge of the AI Sample Clock signal to output your next DO sample. Here's an article for reference.
That's an interesting idea, Kevin - thanks a lot for that!
I considered using the falling edge of the AI Sample Clock pulse, although by a different method. What put me off pursuing that line was the timing diagram here:
which shows the Sample Clock falling edge occurring *before* the final sample in the scan, which seems kind of arbitrary and, if true, wouldn't have helped my problem. However, if it is possible to control the duration of the (exported) Sample Clock pulse, that would be great. The article you directed me to doesn't actually refer to the duration when DAQmx_Val_Lvl is selected, so I need to find out if it stays at least until the final scan sample has been initiated.
It's an avenue to explore, at least.
A question to NI/low-level programmers... Can one safely assume that anything which can be done in LabVIEW can also be achieved in C code, or is some functionality accessible only in LabVIEW?
The C API and LabVIEW API pretty much have a 1:1 mapping of functions with the exception of some polymorphic instances in the LabVIEW API that utilize LabVIEW-specific datatypes. There isn't any hardware configuration that I'm aware of that is only supported in LabVIEW but not C.
With regards to your error (-200077 when calling DAQmxCreateCOPulseChanTime), I'd check what you set for initialDelay, highTime, and lowTime. The minimum value for any of these parameters is 2 ticks of the timebase (25 ns if you are specifying in terms of seconds given the max timebase is 80 MHz). The maximum count is 2^32-1 ticks (which is about 53.687 seconds if using the 80 MHz timebase). Did you get any additional information out of the error message regarding which parameter was out of range?
If this were me I'd probably go with the counter if you don't need it for something else as it gives you the flexibility to adjust the exact timing of the digital output relative to the AI in 12.5 ns increments. The falling edge of the AI Sample Clock in "level" configuration *could* be perfect for your application, but if it's not there wouldn't be a way to adjust the timing.
A snippet of code might be helpful if you need any help troubleshooting specific errors. Either of the methods suggested here should be possible using either the LabVIEW or C APIs.
Thanks for your suggestions.
The default values for the counter timings are: initialDelay = 20us, highTime = 10us, and lowTime = 100us (not used when there is only one pulse output). Durations in the tens of microseconds range should be fine? Unfortunately there is no further info given about what parameter is out of range.
Here is a (not too large I hope) snippet of my code.
// ################ Analog task ################################## ... // ################ Counter task (used to delay digital output) ############ CheckError( DAQmxCreateTask(PChar(''), CtrTask) ); // physical channel names don't start with forward slash counter := DeviceName[RequiredDeviceNumber] + '/ctr0'; // fully qualified terminal name starts with forward slash trigsource := '/' + DeviceName[RequiredDeviceNumber] + '/ai/SampleClock'; // convert requested integer microseconds into float64 seconds tdigdelay := DigDelay / 1000000; thiticks := HiTicks / 1000000; tloticks := LoTicks / 1000000; CheckError( DAQmxCreateCOPulseChanTime(CtrTask, PChar(counter), nil, // just use physical channel name DAQmx_Val_Seconds, DAQmx_Val_Low, tdigdelay, tloticks, // note lo before hi thiticks) ); CheckError( DAQmxSetPauseTrigType(CtrTask, DAQmx_Val_DigLvl) ); CheckError( DAQmxSetDigLvlPauseTrigSrc(CtrTask, PChar(trigsource)) ); CheckError( DAQmxSetDigLvlPauseTrigWhen(CtrTask, DAQmx_Val_High) ); CheckError( DAQmxCfgImplicitTiming(CtrTask, DAQmx_Val_FiniteSamps, 1) ); if CheckError( DAQmxTaskControl(CtrTask, DAQmx_Val_Task_Commit) ) then begin EnableFPUExceptions; Exit; end; // ################ Digital task ################################## CheckError( DAQmxCreateTask('', DigTask) ); channels := DeviceName[RequiredDeviceNumber] + '/port0'; CheckError( DAQmxCreateDOChan(DigTask, PChar(channels), nil, DAQmx_Val_ChanForAllLines) ); CheckError( DAQmxCfgSampClkTiming(DigTask, PChar(counter), // trigsource srate, DAQmx_Val_Rising, DAQmx_Val_ContSamps, Floor(srate)) ); CheckError( DAQmxCfgOutputBuffer( DigTask, ndigpts )); CheckError( DAQmxWriteDigitalU32( DigTask, ndigpts, false, 0, DAQmx_Val_GroupByScanNumber , pbuf, numbyteswritten, nil) ); if CheckError( DAQmxTaskControl(DigTask, DAQmx_Val_Task_Commit) ) then begin EnableFPUExceptions; Exit; end; ... // start digital task before analog task so that digital waiting to trigger // on completion of first analog sampling if CheckError( DAQmxStartTask(CtrTask) ) then Exit; if CheckError( DAQmxStartTask(DigTask) ) then Exit; if CheckError( DAQmxStartTask(ADCTask) ) then Exit;
Are you sure the error is coming from DAQmxCreateCOPulseChanTime? If you step through the code can you report exactly what each of the inputs are when it runs, and verify that the error is indeed coming from this function? It looks fine to me but then again I don't have any experience with Delphi, perhaps the strings aren't what I'm thinking they are?
I did notice a couple of other spots that would result in errors:
1. You are calling PChar(counter) in two spots that require different inputs ("Dev1/ctr0" when creating the CO channel vs. "/Dev1/Ctr0InternalOutput" when specifying the trigger source for the DO task).
2. You shouldn't be configuring a pause trigger (in fact, it's not supported for a finite counter output task on your device, but this should result in error -200144 when committing the task). Instead, configure a digital start trigger (DAQmxCfgDigEdgeStartTrig) and make it retriggerable (DAQmxSetStartTrigRetriggerable). You'll actually want to set lowTime to be equal to the initialDelay due to a peculiarity with how M Series implemented retriggerable counter output.
This way, you start the counter task once in software, and it will continue to trigger off of your analog sample clock.
Stepping through the code, it's clear that it is still failing in the call to DAQmxCreateCOPulseChanTime, even after making sure that lowTime (tloticks) and initialDelay (tdigdelay) are the same value. Unfortunately, the error is obscure about which property doesn't have a supported value.
DAQmxCreateCOPulseChanTime is the first call following the task is initially created. Do I need to call anything else prior to this? Does the order of task-configuring calls matter?
I have incorporated your other suggestions, replacing calls to DAQmxSetPauseTrigType, DAQmxSetDigLvlPauseTrigSrc and DAQmxSetDigLvlPauseTrigWhen with calls to DAQmxCfgDigEdgeStartTrig and DAQmxSetStartTrigRetriggerable (not sure whether DAQmxCfgImplicitTiming still applies). However, we never reach these because of the above problem.
I think I am going to have to play around with the DAQmxCreateCOPulseChanTime parameters until I at least get something that DAQmx accepts and then take it from there.
What are the values of each of the input parameters when the error is returned?
Time parameter values on calling DAQmxCreateCOPulseChanTime are:
initialDelay = 2.0e-5 (20us)
lowTime = 2.0e-5
highTime = 1.0e-5
I tried varying these with a range appropriate for my application, and also with vastly longer times (e.g. 1s), with the initialDelay=lowTime constraint, and still get the same error. Do the values have to be a strict multiple of some clock interval?
Thanks for your continued help.
Those values should be fine, maybe one of the other inputs isn't correct (like an invalid channel name or something like that?). I might be overlooking something since I have actually never programmed in Delphi/pascal before.