Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

PCI-6281 Triggering Fundamentals: why does my system hang?

I'm new to NI, and I have a basic question.  The docs have just gotten me tied up in knots on this one.

I have a PCI-6281.    I want to generate a voltage ramp while at the same time synchronously measuring a voltage, to be triggered by software.  Like a one-sweep oscilloscope.

I can generate the ramp and subsequently read the voltage, but I can't figure out how get them to start at the same time. 

I'm programming in Python, using the ctypes module to call the DAQmx functions in the dll (because I already know how and want to avoid yet another learning curve) but that fact is immaterial, I think.    Pretend I'm writing in C.  It shouldn't matter.  At least it hasn't up to now.

I've tried lots of options, and can't list them all here.  But here's what I've been doing most recently.  I'll describe it schematically rather than pasting in my code because I guess that my difficulty is very high-level:  the meaning of tasks, channells,  triggers, etc.

I've temporarily given up on an "internal" software trigger, although it sounds like the way to go eventually.  Now I generate a voltage pulse on one of the counter outputs (successfully) and I'm trying to wire it to the right place to serve as a hardware trigger.

I'm trying to set up three tasks:  A TriggeredWrite, a TriggeredRead, and a GeneratePulse.    The hope is that the pulse will trigger the Read and Write simultaneously. 

I configure the TriggeredRead and TriggeredWrite to be nearly identical (number of samples, internal clock, rate, etc) I'm doing the SetUp for the TriggeredRead in the following order:

DAQmxCreateTask
DAQmxCreateAIVoltageChan
DAQmxCfgSampClkTiming
DAQmxCfgDigEdgeStartTrig(taskHandleTriggeredRead,
                                                     '/Dev1/PFI9',   # Pin 3
                                                     DAQmx_Val_Rising
                                                     )
DAQmxStartTask
DAQmxReadAnalogF64

Then I do the corresponding thing for the write.   I'm hoping that the thing is now set up just waiting for PFI9 to go high, at which time it will simultaneously write and read (and return my data).

I've physically wired terminal 2 (/Dev1/ctr0) to terminal 3 (/Dev1/PFI9)
(In a stand-alone test, the expected trigger pulse appears at the expected terminal.)

Then I intend to "fire the trigger", i.e., start the task GeneratePulse that produces the HW pulse. But the program doesn't get that far.

The program hangs during DAQmxReadAnalogF64. When I quit the program I get a message telling me that it's still waiting for data, or perhaps my trigger isn't set up right.  I expected the program to zoom on past DAQmxReadAnalogF64, as I thought this call was just preparing the system.  

If I swap the order and do the Write SetUp first, indeed the program does zoom through DAQmxWriteAnalogF64 (without producing a voltage ramp) and the program again hangs at DAQmxReadAnalogF64.    If I manually (with a wire and a voltage source) bring terminal 3 high, the program continues past DAQmxReadAnalogF64 ... but neither the Write nor Read are performed.

I apologize if there is too much / too little information.  I'm happy to fill in details as needed.

Regards,
-garyp
0 Kudos
Message 1 of 10
(4,082 Views)
Update: 

I'm narrowing down my problem.
The TriggeredRead task starts as expected, but sits and *waits* for the trigger before moving on.  Meanwhile, the program can't do anything.

The TriggeredWrite works differently:  After the task is configured, the DAQmxWriteAnalogF64  function returns *immediately* and waits for a trigger in the background.  

In the latter case, I can then route a digital pulse (generated on the DAQ)  to the trigger terminal, and the write takes place.  I can't do that for the read because we're hung in DAQmxReadAnalogF64.

Both the read and the write respond properly to an externally provided trigger pulse.

How can I get the Read to wait for a trigger in the background?

The docs mention that a software trigger precludes teh need for separate threads.  I don't know what that means, but if using a software trigger would help, could someone tell me how its done?  I can find no help or examples.

tia, garyp
0 Kudos
Message 2 of 10
(4,061 Views)

Hello GaryP,

     If I understand you correctly you're looking to synchronously acquire and generate analog voltages using your PCI-6281 with NI-DAQmx in Python.  If this is a case there is a C++ example that already does this--using it you can infer the Python equivalent.  This example is for continuous synchronized acquisition and generation, but the basics of synchronizing the two tasks are the same if you change to finite acquisition.  Unless you want to do finite retriggerable data acquisition in hardware then you shouldn't need to configure the pulse train task.  The analog input and analog output sample clocks are divided down from the same timebase so in order to synchronize them you only need to make sure they start at the same time in order for them to be synchronized.  In this example the analog output task is configured to start from the analog input start trigger.  Therefore if you start the analog output task before the analog input task they will start at the same time because the analog output task will be started and waiting for the trigger from the analog input task.  Then when you start the analog input task the two will start together and be synchronized.

 

In keeping with high level descriptions of what needs to be done I’ve copied the “Steps:” from the comments of this example as they should apply to any:

Steps:

1.  Create analog input and output voltage channels for measurement and acquisition.

2.  Set timing parameters for continuous generation and acquisition. The sample rate and number of samples are set to the same values for each task.

3.  Configure start triggers that will synchronize both tasks by triggering the output task from the AI Start Trigger.

4.  Create the signal to be generated by the output task.

5.  Create an CNiDAQmxAnalogSingleChannelWriter and associate it with the output task by using the task's stream. Call CNiDAQmxAnalogSingleChannelWriter::WriteMultiSample to write the data.

6.  Call CNiDAQmxTask::Start() on each task to start the acquisition and generation.  Note: The output task must start before the input task because it is waiting on the input task for the start trigger.

7.  Create an CNiDAQmxAnalogSingleChannelReader and associate it with the input task by using the task's stream. Use the CNiDAQmxAnalogSingleChannelReader::InstallEventHandler method to register the callback for the asynchronous read method. Call CNiDAQmxAnalogSingleChannelReader::ReadMultiSampleAsync to begin the read.

8.  Inside the callback, read the data and display it.

9.  Call CNiDAQmxAnalogSingleChannelReader::ReadMultiSampleAsync again inside the callback to perform another read.

10.  Destroy the CNiDAQmxTask object to clean-up any resources associated with the task.

11.  Handle any CNiDAQmxExceptions, if they occur.

<Continued Below...>

Brooks
0 Kudos
Message 3 of 10
(4,047 Views)

<Continued from above...> 

    If you’d like to view the C++ code for this example it installs with NI-DAQmx if you include support for VS2005 C++.  If you’ve already installed this support the example should be located in the following path:  C:\Documents and Settings\All Users\Documents\National Instruments\NI-DAQ\Examples\MStudioVC2005\Synchronization\Multi-Function\SyncAIAO_DigStart.  If you don’t have these examples you can install them by modifying your installation of NI-DAQmx from the Windows Add/Remove programs utility.

     I hope this helps get you started, and if I’ve misinterpreted what you’re trying to do please post back with more information about what you’re trying to do.  Particularly it would be helpful to know:

1.  Is this finite or continuous acquisition/generation?  Either of these can be done with the above example with minimal changes; it is configured to be continuous as is.

2.  If it is finite do you need it to be retriggerable?  If you want to be able to retrigger this you’ll either need to generate a pulse train to use as the sample clock, or you can stop and restart the two tasks—the first option can be done in hardware (by configuring a retriggerable pulse generation task), the second is in software by stopping and restarting the task.

Have a good night!

Brooks
0 Kudos
Message 4 of 10
(4,046 Views)
Thanks, Brooks.

Your suggestion helped.  My program doesn't hang anymore.  The output signal is correctly generated.  But I am not capturing the input signal.  I can see the input signal on the 'scope, so I know that it's there and occurs at the same time as the output signal.   One possibility (of many) is that the triggering is still not right.  I thought it would help to monitor the /Dev1/ao/StartTrigger and /Dev0/ai/StartTrigger to see if they occur nearly simultaneously (desired) or if there is a delay between them of 300 ms, the length of my output ramp  (indicating that the Read is starting after the Write has finished).  


I tried to make the Trigger signals available using the following Python code (nearly identical to the same C code):
-------------------
        DAQmxConnectTerms('/Dev1/ai/StartTrigger',
                                              '/Dev1/PFI0',  #pin 11
                                              DAQmx_Val_DoNotInvertPolarity
                                              )
---------------------
and similar for the AI StartTrigger.
Should that bring the StartTrigger signal to terminal 11 ?   I can't see it on the 'scope.  I don't know if that's because my attempt to connect the terminals (in software) was done incorrectly, or because I made some other mistake in the trigger code.  If I knew that I should be able to see the Trigger pulse, I would know where to start looking.


Another possibility is that I'm not implementing the WaitUntilTaskDone correctly.
Here's the outline of what I have now:
-----------------------
CreateWriteTask
SetUpWriteToStartOn"/Dev1/ai/StartTrigger"
CreateReadTask
SetUpReadToStartOnCommand
StartWrite
StartRead
WaitUntilTaskDone(theReadTask)
StopAndClearBothTasks
------------------------
Any glitches in that outline?

To answer your question:  I think I want a Finite acquisition.  I'll click on a button and the ramp will be generated, the signal read, and then the hardware stops and waits for the next button push.   Sounds Finite to me.

Thanks again,
garyp
0 Kudos
Message 5 of 10
(4,008 Views)

Hello Gary,

 

The Dev1/ai/StartTrigger is an internally available channel on the PCI-6281 so you do not need to route it externally.  Assuming you're using a DAQmx Trigger function call to configure the analog output (write) task to start on the "Dev1/ai/StartTrigger" this will be internally routed.  Also, if this is what you're doing, if the analog output task is starting then that means the analog input task is also started.

 

This may be a silly question, but looking at your pseudo code I don’t see a DAQmx Read called anywhere.  I also don't see a DAQmx Write, but since you're getting your output I know one must be there.  Is it possible that you've omitted this step?  It would fit the symptoms you're seeing.

 

If this is not the case then the issue may be the sample clock.  How are you configuring the sample clock?  If it is an external clock then are you sure that the input task is receiving it?

 

Cheers,

Brooks
0 Kudos
Message 6 of 10
(3,988 Views)
Concerning /Dev1/ai/StartTrigger, I was trying to make it available to the outside world so that I could see it on the scope for debugging (to see exactly when the tasks start).   But I see no signal when I wire and configure as described above.   Should the configuration above expose the StartTrigger?  What should a start trigger pulse look like   (height, polarity, duration)?

I do execute Read and Write commands; they are bound up in the phrase, e.g.,  "SetUpRead".   I have played around with DAQmxStartTask calls.  Having two, having none, having one.   Right now I have two, one for the read and one for the write.  I think that the default behavior of a Read is to automatically call StartTask (?) so I might be doing something wrong by calling StartTask explicitly.

I may have to pare down the code to a minimum and post it.   Let's try to avoid that for the time being.

Timing:  I use internal clock, set up with the following code (python code; hopefully self-explanitory):
-------------------------
 nidaq.DAQmxCfgSampClkTiming(self.taskHandleTriggeredWrite,        
                                        "",               
                                        float64(self.Rate),  
                                        DAQmx_Val_Rising, 
                                        DAQmx_Val_FiniteSamps,
                                        uInt64(self.Nsamp)
                                        )

nidaq.DAQmxCfgSampClkTiming(self.taskHandleTriggeredRead,       
                                                  "",              
                                                  float64(self.Rate), 
                                                  DAQmx_Val_Rising,  
                                                  DAQmx_Val_FiniteSamps,
                                                  uInt64(self.Nsamp)
                                                  )
------------------------------------------------               


and here's how I set up Write to start on Read's start trigger:
------------------------------------------------
nidaq.DAQmxCfgDigEdgeStartTrig(self.taskHandleTriggeredWrite,
                                                     '/Dev1/ai/StartTrigger',
                                                     DAQmx_Val_Rising,
                                                     )
-----------------------------------------------
                
Any thoughts?
-garyp
0 Kudos
Message 7 of 10
(3,964 Views)

Hello Gary,

Here is how the sequence should go.  If there are multiple items for one number the order of those calls doesn't matter.  I.e. it doesn't matter which task you create first, but they both need to be created before you configure their timing.

1.  Create read and write tasks.
2.  Configure timing for read and write tasks.
3.  Configure write task to start on the /Dev1/ai/StartTrigger.
6.  Start write task.
7.  Start read (ai) task.  (This will start the write task was well).
8.  Call the DAQmx Read and Write functions until all samples are done.  This may be in a loop.
9.  Clear the read and write tasks.

If you are following this sequence and you see that the write task has started and is working correctly then the read task must also have been started since it triggered the write task.  You are correct that you need to call two DAQmx Start Task functions and the order is very important.  In this setup you need to start the write task and then the read task.  When you start the write task it then waits to receive its start trigger and since that trigger is the ai start trigger it needs to be started before the read task.  If you reverse the order the read task will start, but the write task will never receive its start trigger.  While the default behavior of the DAQmx Read is to start the task, that is not the preferred method and for purpose of synchronization doing it this way would just make things difficult if its even possible.

The three function calls you posted look like they have all the correct parameters so my primary concern is the order in which they are called.  If you find that you're already using the order I posted above then it may be helpful if you can post some of your code, preferably the smallest section that can reproduce the issue you're seeing.

Have a great night!

Brooks
0 Kudos
Message 8 of 10
(3,933 Views)
I've gotten my program to work the way I want, but I used a different procedure than that suggested above.  For some reason I've not been able to find the magic combination that Brooks is suggesting, despite the fact that it seems so sensible and logical.

What works is to *not* call any StartTask functions at all, while at the same time setting the AutoStart flag in the DAQmxWriteAnalogF64 call to 1.  There could be hidden pitfalls, but so far it has been working without any errors.  Let me know if there's any downside to doing it this way.

thanks again,
-garyp
0 Kudos
Message 9 of 10
(3,921 Views)

Hi Gary,

I'm glad to hear you've got it working.  As long as it’s doing what you want I think that it should be just fine.  I did however notice a problem with my instructions in my last post.  The write task needs to have its output buffered before you start the task.  That means the correct pseudo code should read:

1.  Create read and write tasks.
2.  Configure timing for read and write tasks.
3.  Configure write task to start on the /Dev1/ai/StartTrigger.
6.  Call the DAQmx Write to buffer the output.
7.  Start write task.
8.  Start read (ai) task.  (This will start the write task output as well).
9.  Call the DAQmx Read and DAQmx Is Task Done VI (for the write) until all samples are done.  This may be in a loop.
10.  Clear the read and write tasks.

Sorry for the confusion, the original instruction list had this correctly, but I flipped it in the last post.  Once again, if it is working the way you expect then you’re probably okay.  It just may be a little bit harder to make sure the program is executing the way you think it is with your current setup.

Cheers,

Brooks
0 Kudos
Message 10 of 10
(3,899 Views)