I'm trying to synchronize a counter output(being used as a digital pulse) and analog output of a custom waveform read in from file on a USB-6211. The goal is to have these run continously until I hit "STOP" and to be able to shift the position of the pulses relative to each other. I created a VI by combining and modifying the Cont.Gen.Wfm.Int-Clk.Non-Regen.VI and the Gen Dig Pulse Train Continous.VI and I'm now able to output both signals at the desired frequency, but I can't seem to get the "Initial Delay" to work properly. The values I enter don't seem to correspond to what is actually happening time-wise, and the spacing seems to vary from run to run(within each run is constant). In a previous post, JohnP suggested:
"Triggering the counter outputs off of the AO Start Trigger and adjusting the "Initial Delay" parameter should give you what you are looking for. Remember to start the counter tasks in software before the AO tasks (so they are armed and ready to go before the AO Start Trigger is sent)."
I've tried using both controls and propertynodes(perhaps incorrectly) but it doesn't seem to make a difference.
I think what is happening is that my arming and triggering sequence is not being done correctly. I've read up on as much information as I could on triggering and arming, but I'm not sure I understand it properly. Most of the examples I've found utilize an external trigger, or do finite Analog Input tasks. Does anyone have a suggestion as to how to start up my two tasks in the proper order? Is something else being done incorrectly here?
P.S. I attached the VI and a sample waveform to read in.
Solved! Go to Solution.
Here's a cleaned-up version of the VI that should work (you don't need to adjust the initial delay on-the-fly right?). Instead of reading from a file I just made a simple 50% square wave to use for testing.
Wow, thanks for the fast and efficient response! I just tested it with the square wave and it works great. I liked how you cleaned up the VI, I'll see if I can learn from that.
For now, I am set. In regards to your question of "on-the-fly" changes of intial delay, that ability would be nice(also scaling the waveform), but not absolutely necessary. I can stop and restart the VI. If I wanted enable on-the-fly changes at a later date, what would be the general idea? From what I've read one cannot change the counter while it's running, so I assume it would need to be retriggered? I also tried to do on-the-fly waveform scaling(as well as switching to constant output), but I couldn't get it to work. When it comes to buffers, if I set my sample size at a number, say 64, and output mode to n-samples, does that mean the buffer only holds 64 samples? I understand that the sample size during continous output helps set the buffer size. I tried to change the output types, sample sizes,etc, but I wasn't quite sure what was going on the behind the scenes.
Lastly, is there some sort of primer on A) property nodes and B) AO, buffers and triggering? I didn't quite understand how to use the Property nodes,and the waveform buffer workings still aren't super clear.
I'm glad the VI got you started on the right track. Here's a good link on DAQmx property nodes that includes a section on AO Regeneration Modes:
Essentially the DAQmx Property Nodes are needed to configure the more advanced features of a DAQ device. You can also use them for some of the basic functionality which is also configurable through the DAQmx Sub VIs. For example:
The documentation on buffering can mostly be found in the DAQmx Help (which unfortunately is not searchable online yet). To explain what's going on, I prefer to look at the entire system as a whole:
The items in red represent places that the data is stored. When we talk about the output buffer, this refers to a space in RAM pre-allocated by DAQmx to use during your task. A brief step-by-step of the process:
When data is generated (or read from a file) it first must exist in LabVIEW Memory.
The DAQmx Write function moves the data from LV memory to the pre-allocated DAQmx Buffer in your system's RAM. You can configure this buffer size in two ways:
1) Let DAQmx pick a buffer size. For Analog Output tasks, this means that the buffer will be the size of the waveform written during the first call to DAQmx Write.
2) When necessary, you could also explicitly set the buffer size with DAQmx Configure Output Buffer.vi
DAQmx will automatically transfer data from the PC buffer to keep data in the device's on-board FIFO. DMA Transfers are not actually used with USB devices but the exact mechanism doesn't affect the basic flow above diagram.
From the on-board FIFO, the data is pushed out by the AO Sample Clock. If the on-board FIFO is empty for some reason during the edge of the clock, DAQmx Write should return an underflow error.
There are several configurable properties relevant to the above graph. A couple of commonly used ones:
Regeneration Mode: enabling regeneration (enabled by default) means that the generation will start over from the beginning of the DAQmx Buffer once it reaches the end. Typically if you are writing new data to the DAQ device you should disable Regeneration so you can have better control of when/where the data is written.
For continuous tasks which do not allow regeneration, you must continually write data to the DAQmx Buffer to avoid an underflow. When DAQmx Write is called, it will block until all of the data provided to its input has been transferred to the DAQmx buffer (or a timeout has been reached).
Use Only Onboard Memory: Setting this property to true (false by default) will prevent unnecessary bus transfers. If the waveform is small enough to fit in the on-board FIFO of the DAQ device, and you wish to repeat the same data over-and-over again, then you could safely set this property to true.
Hopefully that clears up some of the confusion on buffering. I'm running out of room so I'll make another post about the on-the-fly changes.
If the period of your AO waveform isn't changing it should be possible to implement the on-the-fly change to the initial delay using both counters:
ctr0: Configure a Continuous Pulse Train on that has the exact same period as your Analog Output signal. This will be our reference that we can trigger the other counter off of. It will be triggered off of the AO Start Trigger exactly like the code we had before (I would set the initial delay to be 0*).
ctr1: Configure a Retriggerable Single Pulse Generation to be triggered off of the Internal Output of ctr0. Both the initial delay and low time should be set to whatever you want your initial delay to be at the beginning. After the first pulse is generated, the low time is used for the initial delay on each subsequent pulse**.
The key to this method is that low time is changeable on-the-fly. Here is an example that shows how you can write to a Counter Output inside a loop to adjust the Frequency and Duty cycle on the fly. The same procedure applies to High Time and Low Time, just change the instance of the DAQmx Write.
On-the-fly waveform scaling shouldn't be a problem since you're already set up to do non-Regeneration. You would just need to put a control inside your loop to multiply your waveform by before passing it to DAQmx Write.
* In reality the delay will be approximately 25 ns
** Many of us find this behavior confusing but there is no way to change it since it could break existing customer applications. On our new X Series devices we included an Enable Initial Delay on Retrigger property that re-uses the initial delay on retriggerable pulse generation.
Thanks for the detailed responses, it cleared up a lot of questions that have been circulating for me. I will be taking a look at the counter delay VI you linked to shortly, but before that I wanted to follow up on the waveform scaling(amplitude) issue. I have the " Waveform Scale and Offset" VI inside of the While Loop. I noticed awhile back that the Waveform Chart updated immediately upon changing the scale, but that the physical output did not. For fun, I decreased the period of my waveform to approx 0.4 seconds(with 64 samples) and timed how long it took to change the output on the oscilloscope. It seems to be somewhere around 30-45seconds. I changed the "Samples per Channel" on the Sample Clock between 64 and 2, and I couldn't detect a pattern. Is the entire buffer(all 4096 samples) being filled up with repeats of the 64 original samples?
It has been a few months since I last wrote in. I'm not sure if it is better to start a new message or just continue...
I was writing to follow up on John P's message regarding how to change the initial delay of a counter on the fly. I've made several attempts based on the message and on the examples that the message contained, but I still seem to be doing something incorrectly. I can change the value of low time/initial delay, but the output on an oscilloscope doesn't change. I've attached my latest version, to see if anyone can figure out what I am doing wrong.
Also, I wanted to follow up on the ability to change an analog waveform's amplitude on the fly. According to the last round of e-mails, placing a Scale Waveform inside of my While loop should do the trick. I commented that my chart shows the change immediately, but that the output from the card(seen via oscilloscope again) takes several minutes to change. I'm guessing this has something to do with the buffer but not sure.
Take a look at the attached code for what I had in mind to reconfigure the delay on-the-fly.
As far as scaling the AO voltage on-the-fly, it's probably true that your old data is in the buffer and needs to be generated. The buffer is going to be the same size as however many samples are provided to the first call of DAQmx Write. How many samlpes is this and what is your update rate?
In addition to the DAQmx Buffer, there is also an 8191 sample FIFO on the board. Data that is currently in the DAQmx Buffer as well as the on-board FIFO must be first generated before the new data becomes available at the output. You could get fancy with adjusting the write pointer to the DAQmx buffer, but I think the easier solution is probably going to be to break down the file into smaller chunks (assuming it is a large file) and writing less samples per loop iteration with a smaller DAQmx Buffer.
Oh, you'll probably want to have a slight delay in the bottom loop to prevent it from consuming all of your CPU. Adding something like this should do the trick:
There's also a race condition youl'l want to correct. Simply wiring the error from the Start of the bottom task to the top loop should do the trick:
Thanks so much! The Shared Variables were a relevation to me. I had been abstractly wondering how one could pass variables around for some time now. I added the second counter to my exisiting VI and it works beautifully.
As for scaling the analog waveform: my files are pretty small. On average a waveform consist of ~100 samples, and repeats every second. On the sample clock VI for the analog waveform, I set the number of samples by reading the file size, and the rate is determined by dividing # Steps(samples) by the period. Period is also read from the file.
I noticed that your example VI uses an Analog 1D DBL 1chan Nsamp, while I am using the Analog Wfm 1chan Nsamp.(building the waveform first).
Could this be causing problems?