Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

DAQmxRegisterEveryNSamplesEvent buffers up callbacks until the end

Solved!
Go to solution

Using PXIe-6363, DAQmx C API on Win64

I'm trying to get DAQmxRegisterEveryNSamplesEvent to work with both AI and AO, on each sample.

 

My AI task works, which means my callback gets called with every sample.

My AO task waits until it's finished, then calls the callback very quickly.

For example, for 50 AO samples, the analog voltages appear on my scope, then after the 50th sample, my callback gets called 50 times all of a sudden.  This happens even if the output rate is very slow, like 0.5 or 1.0 Hz.

The behavior is the same as if I had asked for DAQmxRegisterEveryNSamplesEvent with an argument of 50 instead of 1.

 

How do I get my AO to call back with each sample as they are written out?  I think I encountered this a year or two ago, and the workaround was to set up the timing so it does only one sample instead of 50, then stop and restart the task in the callback for each sample.  I can live with the resulting extra few ms this adds with each cycle, but it's ugly.  I'd like my code to be called either at the beginning or the end of each analog voltage physically sampled out on the wire.

 

Below is the abbreviated code.

// Set up common channels
char aoPhysNames[100], aiPhysNames[100], doPhysNames[100];
strcpy_s(aoPhysNames, sizeof(aoPhysNames), "Dev1/ao3,Dev1/ao2,Dev1/ao1");
strcpy_s(aiPhysNames, sizeof(aiPhysNames), "Dev1/ai16,Dev1/ai17,Dev1/ai18");
strcpy_s(doPhysNames, sizeof(doPhysNames), "Dev1/port0/line8:31");
char aoNames[] = "XDRIVE,YDRIVE,ZDRIVE";
char aiNames[] = "AUX X,AUX Y,AUX Z";
DAQmxErrChk(DAQmxCreateTask("Analog Output Task", &hAOtask));
DAQmxErrChk(DAQmxCreateTask("Analog Input Task", &hAItask));
DAQmxErrChk(DAQmxCreateAOVoltageChan(hAOtask, aoPhysNames, aoNames, -10.0, 10.0, DAQmx_Val_Volts, NULL));
DAQmxErrChk(DAQmxCreateAIVoltageChan(hAItask, aiPhysNames, aiNames, DAQmx_Val_NRSE, -10.0, 10.0, DAQmx_Val_Volts, NULL));

// ANALOG OUTPUTS
DAQmxErrChk(DAQmxRegisterEveryNSamplesEvent(hAOtask, DAQmx_Val_Transferred_From_Buffer, 1, 0, DAQ::_everyNWriteCallback, this));
DAQmxErrChk(DAQmxRegisterDoneEvent(hAOtask, 0, DAQ::_doneWriteCallback, this));
double outRate = <some formula, about 100 Hz>
DAQmxErrChk(DAQmxCfgSampClkTiming(hAOtask, "", outRate, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, numSamples /*+1*/));

// Set up N input samples as slave to when AO starts, at same frequency as pulse
DAQmxErrChk(DAQmxCfgSampClkTiming(hAItask, "", pulseDef.frequency, DAQmx_Val_Rising, DAQmx_Val_FiniteSamps, pulseDef.count));
DAQmxErrChk(DAQmxRegisterEveryNSamplesEvent(hAItask, DAQmx_Val_Acquired_Into_Buffer, 1, 0, DAQ::_everyNReadCallback, this));
DAQmxErrChk(DAQmxSetStartTrigRetriggerable(hAItask, TRUE));
DAQmxErrChk(DAQmxSetStartTrigDelayUnits(hAItask, DAQmx_Val_Seconds));
DAQmxErrChk(DAQmxSetStartTrigDelay(hAItask, AI_TRIG_DELAY));
DAQmxErrChk(DAQmxCfgDigEdgeStartTrig(hAItask, "ao/SampleClock", DAQmx_Val_Rising));

// Later on, after other non-DAQ stuff....
DAQmxErrChk(DAQmxStartTask(hAItask)); // AI is slave to AO
int32 aoWritten;
DAQmxErrChk(DAQmxWriteAnalogF64(hAOtask, numSamples, FALSE, -1, DAQmx_Val_GroupByScanNumber, (float64 *)outData, &aoWritten, NULL));
DAQmxErrChk(DAQmxStartTask(hAOtask)); // AO starts regardless



int32 CVICALLBACK DAQ::_everyNReadCallback(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData) {
    // This is called correctly at the proper times, with nSamples == 1
	return 0;
}
int32 CVICALLBACK DAQ::_everyNWriteCallback(TaskHandle taskHandle, int32 everyNsamplesEventType, uInt32 nSamples, void *callbackData) {
    // This is called the correct number of times, but only after the DAQ has physically
    // written out all the voltages, as seen on the o-scope.
    // nSamples == 1 each time, which is correct
	return 0;
}

 

0 Kudos
Message 1 of 15
(4,424 Views)

Hey RedmondUser,

 

I managed to reproduce this. Let me look into this more and see if it is intended. So far your original workaround seems to be the most straight forward.

Ren H.
Applications Engineering
National Instruments
0 Kudos
Message 2 of 15
(4,354 Views)

Great!  I'm eager to see a solution.

thank you

 

0 Kudos
Message 3 of 15
(4,344 Views)

Can you give me a bit of background information on what you are trying to achieve with it? And how is this functionality going to be used in your application?

Ren H.
Applications Engineering
National Instruments
0 Kudos
Message 4 of 15
(4,332 Views)

I'm driving a HelmHoltz coil amplifier with a series of analog values.  At each analog value, I'm also generating N pulses on CTR0 (or digital outputs on port P0), and at approx the same time and exactly the same frequency, I'm collection the N analog inputs.  The pulses trigger an external circuit board which measures the resulting magnetic field, and the AI reads a voltage from a reference magnetometer which also measures the magnetic field.

With each analog output, I'm doing a printf to the console to show the user what field is being generated by the AO.

With each analog input, I'm doing a ReadAnalog64F and storing it in a structure.

When the external circuit board receives the pulse, it sends serial data to another .exe that is running on the machine.  The .exe then queries my program for the most recent value of the AI (reference magnetometer), and stores both field values in a file.  The overall goal is to stimulate the coil with, say, 50 different analog values, and read the field back N times for each value.

 

In a previous version, I did one AO write at a time, and kept track of the samples myself from within the DoneWrite callback.  Within this callback, I stopped and restarted the various tasks.  This worked, except the timing was a little variable due to the heavy software-timed nature of starting and stopping tasks.  This doesn't actually affect the real-world results, but it seemed unclean, software-wise, since everything should be doable in the DAQ.

 

With the new version, I'm writing all AO samples, and I tied the AO sample clock to the start trigger of the pulse an AI tasks, setting both of those as retriggerable.  I've set up the frequency of the AO sample clock to be slightly longer than N pulses, to give basically front and back porch timing of a few milliseconds, put a slight delay in the AI and pulse tasks, so the AO goes first, then the AI is triggered, then the pulse is trigged, a few milliseconds apart, to give time for the external system to settle.  This is a much tighter system, and everything is synchronized by the hardware.  I start only one task, and wait for it to finish, etc.  No stopping and stopping with software, or keeping track of the samples myself.

 

Everything seems to work as far as the triggering, timing, etc., as seen on the scope.  The only issue is that the AO callbacks are bunched up until the end.  So if it takes 5 seconds to do the whole thing (50 AO samples@10Hz with 10 pulses each@100Hz), the user sees nothing on the screen for 5 seconds, then all of a sudden 50 printf's showing the output.

 

If there is a way to have EveryNWrite called back while writing, that would be great.

thanks

 

--------

 

edit:

I just noticed I have the ReadAnalog64F, in the EveryNRead callback, wrapped in a CriticalSection, since the buffer it writes to is read by another thread so I wanted to protect it.  Is it possible this is screwing up the EveryNWrite callback?

0 Kudos
Message 5 of 15
(4,327 Views)

Removing the critical section did not fix the problem.

0 Kudos
Message 6 of 15
(4,322 Views)
Solution
Accepted by topic author RedmondUser

Hi,

 

I think the fundamental issue that you're running into is that, your code will not know the current state of your DAQ hardware unless that information is passed back to it somehow. I tried looking through all of the DAQmx channel properties and I don't think something exists that will tell you which sample it is currently outputting, or what the current output value is.

 

You can find a list of all properties using the instructions here:

http://digital.ni.com/public.nsf/allkb/99F49408D8D0BD9386256D2100496728

 

Perhaps you could use a spare AI channel to monitor your AO channel? This is not the most elegant solution, but you would retain the performance that the hardware timing gives you.

Dale S.
RF Systems Engineer - NI
0 Kudos
Message 7 of 15
(4,302 Views)

I don't necessarily need the callback to tell me the value or the index, as ultimaely I can maintain a counter on my own.  I just want the callback to let me know each time it's changing the output.

 

Are you suggesting setting up a dummy AI which mirrors the AO timing just for the callback functionality?  That might work.  I wouldn't need to physically wire the AI to the AO, as the software can count and know what the output value was. 

 

thanks

0 Kudos
Message 8 of 15
(4,295 Views)

What about insead of using DAQmx_Val_FiniteSamps, I tell it to write using DAQmx_Val_Cont_Samps? The AI task is setup using continuous samples, so maybe this is the trick to getting a callback each time instead of at the end.

 

If the callback works in this case, I can stop the task manually after the last sample; otherwise, the timing continues via hardware and I don't need to worry about software latencies.

 

I'll try this today if I get a chance.

 

0 Kudos
Message 9 of 15
(4,292 Views)

I tried this for AO but it had the opposite effect.  Instead of buffering up all the callbacks until the end of the AO as when using DAQmx_Val_FiniteSamps, using DAQmx_Val_ContSamps called all the callbacks at the beginning, and then some.  Instead of calling only 50 callbacks for 50 samples, it called it back a few thousands times, because I think it was filling the buffer with multiple copies of my samples.  I tried reducing the buffer size to 1 but it errored on minimum buffer size; reducing to 2 errored on trying to write all 50 samples.  Leaving the buffer at full size, but reducing CfgSampleClkTiming to 1 sample had no effect.

 

So next I'll try using a dummy AI task on the same frequency as the AO task.

 

 

0 Kudos
Message 10 of 15
(4,288 Views)