Real-Time Measurement and Control

cancel
Showing results for 
Search instead for 
Did you mean: 

cRIO Analog output is not continuous for high speed sampling

Solved!
Go to solution

Hello everybody

 

I am working with a cRIO-9074 with a NI-9201 AI I/O module and a NI-9263 AO I/O module and the host is a Windows computer. I use Labview 2016.

 

I have created a sine wave with 400 samples (1 period) in a .txt file and I want to be able to send this sine with a frequency of 1kHz to the AO I/O module using a DMA host-to-target FIFO, so my sampling frequency should be 400000 Hz or 100 ticks per sample. I use the AI module to measure simultaneously on two channels (now used for debugging, later on it will have another purpose).

 

I start the VI by prefilling the buffer, but the Empty Elements Remaining of the invoke node (AO.write) stays at the maximum, so I assume that the buffer is emptied as fast/faster than it is filled. When I attach the oscilloscope/AI to the output terminals of the AO I see that it isn't sending a continuous signal. I see bursts (almost dirac impulses) on certain locations (see figure 'Front.png'), but they seem to be correct as they form a sine. How does it come that my signal is so bad (discrete and not continuous) when my data is transferred to the FPGA target with a FIFO? Can I get a continuous signal out of this (because eventually I want to be able to send something else than a sine)? Can someone elaborate on this problem? Should I fill the buffer on the RT and not with my windows computer?

I also added my project.

 

Thank you for your time!

Jasper

 

Download All
0 Kudos
Message 1 of 24
(4,134 Views)

Hi Jasper,

 

I think you are thinking along the right lines. Looking at your code the immediate cause is likely to be your FIFO read is timing out. In this case, it will return a value of 0 which is why the output is driven to 0. To confirm this you could conditionally write to the output (using a case structure) only if the read does not timeout - then I think you will see steps in your waveform instead of spikes (because the value will hold until the next value is available).

 

When you use a FIFO there are actually two buffers to consider - the first is on the host side and that is what you are monitoring. The second is on the FPGA side and is typically much smaller (perhaps 1024 samples by default). For this type of application you have two options:

 

1. If you are just repeating this sine wave you could read from the DMA into a local memory item and rotate over than. This is a little more complicated to write but is then not dependent on the DMA speed.

2. You need to make sure your write speed and buffer sizes can keep up with the generation and there are a few tricks for this.

 

Firstly can you run your host code on the compactRIO controller? This will mean you are truly running over DMA and not the network which could be slower. You are looking at around 1.6MB/s out and I guess around 3.2MB/s in so if your on 100Mb/s network this is not insignificant usage

 

I think your half buffer checks on the FPGA may have issues - I would just monitor this on the host - or just write at the expected rate with a timeout to account for small variations between host and FPGA clocks.

James Mc
========
CLA and cRIO Fanatic
My writings on LabVIEW Development are at devs.wiresmithtech.com
Message 2 of 24
(4,085 Views)

I'm guessing that if you slow your generation rate to like 4KHz or something it works better? You look like you're sending the points over one at a time which is going to be inefficient. You should get rid of the inner for loop and use array subset to create an array with half the buffer in it and pass in the entire half buffer all at once.

Message 3 of 24
(4,068 Views)

Hi James_McN and Nanocyte

 

Thank you for your input.

 

I realized thanks to Nanocyte's post it is ridiculous to send in the signal sample per sample as it takes more time to run through a for loop for every sample than to send it all at once. When I wire an entire array to the FIFO read it works much better. I was able to send in a sine of 100 Hz and it looks perfect on the oscilloscope. For a 1kHz signal it looks good in intervals, but in between the sines there are times it outputs zeros (timeouts, buffer is empty), so I have to find a way to prevent this (send more samples into the buffer more often during acquisition).

The problem with this solution is that the number of samples to load into the FIFO is dependent on the sampling rate. When I want to output a 10 Hz signal it overflows very fast with the settings of the 1 kHz sine. So the program is not really elegant.

 

I was thinking of a solution for this, and as James_MsC said, it would be better to loop the finite number of samples (finite array) on the FPGA target, so you don't have the communication issues, but the problem is that you cannot use arrays on the FPGA. So to tackle this I was thinking of storing the array into a memory block, but now I have to send the array sample per sample to this memory block (see pictures). I should probably use interrupts (haven't done that yet in the pictures) to communicate between the RT and the FPGA. Anyone experience with that? Do you think this will work?

 

Kind regards

Jasper

Download All
0 Kudos
Message 4 of 24
(4,055 Views)

Haha can't believe I missed the looping on putting the data into the FIFO - that will definately struggle.

 

If you want to go with the memory method then you can still use a FIFO to send the data to the memory. Then you just need some sort of trigger to say there are x samples in the FIFO - write them to memory. The signal could be an IRQ or the rising edge of a boolean flag as you were proposing before for the half buffer flags.

James Mc
========
CLA and cRIO Fanatic
My writings on LabVIEW Development are at devs.wiresmithtech.com
Message 5 of 24
(4,049 Views)

Stick to the FIFOs. There's a way to do what you want.

Check out the example at 

LabVIEW 20xx\examples\CompactRIO\FPGA Fundamentals\Data Transfer and Storage\Host to Target Transfer\

It uses a function that creates a waveform that's the desired size. As long as you make sure you have more than the 100ms of data in that buffer, you should be OK.

 

There's other options too. For example, you could have a loop that checks "Empty elements remaining" and generates that many more points making an overflow impossible.

0 Kudos
Message 6 of 24
(4,044 Views)

Hi Nanocyte

 

I will stick with the FIFO's for both communication types (target-to-host & host-to-target), but for the analog output signal I was thinking to transfer the data of the host-to-target FIFO (AO) to a memory block with every value (sample) of my sine on a different address. As this output signal doesn't change during acquisition I reasoned that the communication of the signal could be done once, which would make my life much easier as I shouldn't bother myself with buffer issues. Why do you think this isn't a good solution?

 

Thank you for your time!

Jasper

0 Kudos
Message 7 of 24
(4,031 Views)

I've seen an example somewhere but I don't remember where, but it used a fifo to transfer the waveform to the FPGA and on the FPGA, rather than piping it right out, it stored the FIFO data in a target scoped FIFO. You could think of the FPGA as "Armed" then. The user can then trigger the waveform and pull the data from the target scoped FIFO. Now, the tricky part is if you want to loop the waveform. The way to do that is, when you're firing the target scoped FIFO, you wire it to the AO and a second target scoped FIFO. Then you have a sort of ping pong buffer where you can alternate which buffer's the catcher and which is the pitcher. It's a little complicated, but the performance is very good and you don't have to worry so much about addressing.

Message 8 of 24
(4,029 Views)

Hi Nanocyte,

 

The ping ponging between two target-scoped buffers seems to work fine. Thanks for the useful hints! I'll add my implementation here for future generations. 😉

I have added screenshots of the program. The AO buffer is a host-to-target FIFO buffer. AOH1 & AOH2 are two identical target-scoped FIFO buffers.

 

Thank you all!

Jasper

0 Kudos
Message 9 of 24
(3,980 Views)

I'm glad that worked out for you. There's a few things I don't like.

  • Your use of the i terminal going into that modular divide, eventually you'll hit 2 billion and it won't work any more. You have to come up with a better solution. You can probably turn that while loop into a for loop and then put a while loop around that for loop. This might also get rid of the modular divide which uses a fair amount of resources on the FPGA.
  • The way you initialize "Teller" is a race condition because there's no data dependency. Maybe you can initialize it in the same frame as where you wait for OK to go true. Or, better yet, use a shift register instead of a local. They are a better practice to avoid race conditions just like this one.
  • This is minor but a 10 tick timeout is usually not what you want. You probably want an infinite timeout (use -1) or a 0 timeout with correct handling of the timeout condition.
  • Very minor: you seem to be using teller as a boolean. I'm wondering if you can make it boolean and use a "not" instead of a +. 
Message 10 of 24
(3,960 Views)