 surgineer
		
			surgineer
		
		
		
		
		
		
		
		
	
			08-15-2023 10:45 AM
I am attempting to use my PXIe-6363 card to modulate 15 digital output channels using the DAQMx C api. The goal is for all 15 channels to toggle high once triggered by a rising edge seen on a digital input channel. Each channel should then toggle low after a variable time between 0.1-15 milliseconds that will vary by channel. Ideally, when the rising edge trigger is detected on the digital input channel, it would also trigger a software interrupt that would initiate sending the pulse widths for each channel that should be executed on the next rising edge trigger.
Are there any recommendations for how I can go about implementing this using the C api? From the installed examples, I can create a timed pulse output using ‘DAQmxCreateCOPulseChanTime’ and select one of the four onboard counters. This would limit me to 4 pulse outputs (one for each counter) when I need 15.
Thanks in advance for any feedback.
 Kevin_Price
		
			Kevin_Price
		
		
		
		
		
		
		
		
	
			08-15-2023 11:09 AM
Caveat: I program in LabVIEW so I'm no help with any specific C syntax. But I know the device family and capabilities pretty well. There's more than one way to do this, here are my initial thoughts, others may be able to refine a bit:
1. You'll want to generate your 15 digital outputs with a digital output task. All outputs should be on port 0 which supports hardware timing. I *think* that for the sake of connector compatibility with other MIO devices, only 8 of port 0's lines are on physical connector 0. The rest will be on connector 1. It's a bit of a hassle but can't be helped.
2. Wire the rising edge into *both* a PFI terminal *and* an available port 0 line. Your finite sampling DO task will use the PFI terminal as its start trigger source. You will define a buffer of DO data for all 15 lines that defines all their states throughout the 15 msec. If you only need 0.1 msec resolution, you can generate at 10 kHz. More resolution requires faster sampling and a larger buffer of data to represent the same total generation time.
3. You'll also have a DI task set for "change detection" timing, sensitive to rising edges on the aforementioned port 0 line. Your main software loop will repeatedly request to read 1 sample from this task. When you receive a sample without a timeout or error, that's your software trigger that initiates the following:
- first make sure the contemporaneous DO task finishes, wait if necessary.
- then write the new data representing the next set of pulse widths to the DO task and start it again so it can be ready for the next trigger.
4. You may benefit from a very recent thread that discusses how to make these repeated re-starts more efficient by Committing the task before the first time you Start it.
-Kevin P
08-17-2023 06:36 PM
Kevin - Thank you for the detailed (and speedy) reply to my question. I appreciate your recommendations. I also looked through the post you referenced on state transitions, which will be very helpful for minimizing overhead during the main loop execution.
Do you know if it is possible to write the pulse data to the DO task before the previous task finishes executing? I ask because the rising edge trigger occurs at a frequency of ~ 60 Hz - with a maximum digital output pulse width of 15 ms, only ~1.6 ms will be left to transmit the next buffer. Ideally, I would like to transmit pulse width data to the DAQ card during the current digital output generation so that it is on board and ready for the next rising edge.
Additionally, once the digital input interrupt is triggered, can I perform other computing tasks - such as creating the pulse data on the host side - before checking to see if the digital output task is complete? I assume that because I am using hardware timing, this should not interfere with the quality of the pulse outputs.
 Kevin_Price
		
			Kevin_Price
		
		
		
		
		
		
		
		
	
			08-18-2023 06:44 AM
Well, that new info complicates things. While *conceivable* that you could sometimes load up the next buffer of digital data within ~1.6 msec, I would definitely not plan on it happening reliably. Instead I'd look to use a different scheme.
Hopefully, you can know what all the buffers need to be *before* the app starts -- otherwise I've probably got no good answer. If so, then here's an outline of how I'd try to approach it:
1. The DO task will now be configured for *continuous* sampling, and will get its sample clock from a counter.
2. That counter will be configured to generate a *finite* pulse train of 15 msec duration. A 10 kHz output would generate 150 samples, a 100 kHz output (for better timing resolution) would generate 1500.
3. The counter will also be configured to be re-triggerable by your external ~60 Hz signal. So once every 60 Hz trigger pulse the counter will issue a burst of clock pulses, followed by a ~1.6 msec idle time before the next trigger and burst.
4. This clock drives all the DO sampling, so you can preload as many data segments as you like into the task buffer. Let's suppose 10 kHz sampling and 150 samples per segment. Let's further suppose that you give yourself a comfortable headstart and predefine the first 2 seconds worth of segments. So that'll be 2 sec * 60 segments/sec or 120 segments. For a clock rate of 10 kHz, that's 1.2 million samples. Sounds like a lot, but pretty trivial to handle on modern PC's.
5. After that, I'd set up a write loop that writes 1 sec worth of samples at a time. DAQmx will manage this for you rather well.
The 2 seconds you wrote originally before starting the task sets the size of the buffer. DAQmx will treat it as circular, so when you try to write the next 1 second worth, the function will block and wait until the 1st second worth of samples have been delivered down to the device, then it'll replace that front half of the 2 second buffer. As you loop around to write the next second, again you'll have to wait for the back half of the buffer to be delivered to the board, then DAQmx will accept your write and let you iterate again. And so on.
An approach something like this lets you store a potentially *very* long sequence of output segments in a file, and then regularly retrieve data from the file and write it to your task buffer. The 1 second iteration pace gives plenty of time to retrieve data from a file and deliver it to your task.
-Kevin P
08-23-2023 06:12 PM
I appreciate your help, Kevin! This seems to be working nicely.