arduino-libraries / Arduino_AdvancedAnalog

Advanced Analog Library
GNU Lesser General Public License v2.1
7 stars 5 forks source link

Synchronized ADC Capture #54

Closed jmdodd95682 closed 4 months ago

jmdodd95682 commented 5 months ago

AdvancedADC gives you two options for capturing two analog signals simultaneously. The first is to bind both analog input pins to the same AdvancedADC instance. This works great, but sadly, means your sample rate is cut in half. The sample_rate provided applies to the sampling of both inputs. So a 1M Samp./sec rate really means each is sampled at 500K Samp./sec. If you trying to push the limits of sample rate, this is not a great solution.

The second way is to bind each analog input to a different AdvancedADC. The library will automatically assign the two pins to two different ADC blocks. The sample rate of each will be maximum. Very nice. However, this creates another problem. The begin() routine takes approximately 100 us on GIGA R1 to initialize and start the ADC capture. So if I say something like:

adc_input[0].load(res, sample_rate, num_samples, QUEUE_DEPTH,1,&(probes[0]));
adc_input[1].load(res, sample_rate, num_samples, QUEUE_DEPTH,1,&(probes[1])));

What happens is that the second ADC input capture is delayed by about 100us. So the two signals are not synchronized. If you're trying to capture two signals with reasonable synchronization, that is not going to work. I analyzed the timing of each line of code in the begin() routine and found that two lines are causing almost all the delay:

https://github.com/arduino-libraries/Arduino_AdvancedAnalog/blob/f7d081c4a56f1a87f8b4a9f027f2efb2b4174b7c/src/AdvancedADC.cpp#L187

and

https://github.com/arduino-libraries/Arduino_AdvancedAnalog/blob/f7d081c4a56f1a87f8b4a9f027f2efb2b4174b7c/src/AdvancedADC.cpp#L200-L202

I have a solution. I created two new routines in AdvancedADC.cpp I call load() and fire().
load() is identical to the begin() routine up to a point, including the long-delay lines above. It then returns. Essentially load() allocates the DMA and ADC etc, but does not start the capture. The fire() routine (which only takes about 4 us) does the rest of what was in begin() and starts the capture. This is working and providing a synchronization error of about 4-6 us. Not bad.

This is probably NOT the solution that someone else would prefer, but it works for me. Now I have full sample-rate and okay synchronization between two or three analog inputs.

I guess I would say the issue I have is that begin() takes too long (106 us!). Either this needs to be shortened significantly or the idea of the load() and fire() would also work.

leonardocavagnis commented 5 months ago

Dear @jmdodd95682, Thanks for the useful analysis and measurements! Could you please create a PR with your proposed solution? I'll take a look at it. Thanks

jmdodd95682 commented 5 months ago

Sure. I can do this.

jmdodd95682 commented 5 months ago

okay I submitted a PR. I renamed the functions to ready() and start(). I also added a function called clear() to the AdvancedADC. I found cases where when starting the ADC capture the DMA would (for some unknown reason) think it already had DMA Buffers in the read queue. Running clear() after ready() and before start() fixes this issue.