Running the ADC with two channels at 800 kS/s per channel (1.6 MS/s total)

Hi all,

I just wanted to share that I managed to run the ADC unit in a BBG at (almost?) 800 kS/sec sampling two channels, which means a total sampling rate of 1.6 mS/sec. I forked a small project from rvega and played with the code. You can find my code at:

https://github.com/l4m4re/bbb-pru

The adc code is in the apps/adc directory.

Perhaps this code is helpful for others, too. The PRU code configures the ADC and the current setup samples AIN-1 and AIN-2 at 800 kS/sec.

Regards,

Arend Lammertink.

You do realize that you can achieve the same sample rate with the IIO driver in Linux?

Regards,
John

Not delving into each implementation, and not knowing anything about the IIO driver, I would think the PRU implementation would offload the utilization of the Linux CPU (Arm9?) vs the IIO driver. Is this true?

Also I thin the posted examples are worthwhile to those interfacing with an external ADC and to those who are wanting more PRU coding examples…

regards and thanks for posting your project!

jeff

You do realize that you can achieve the same sample rate with the IIO
driver in Linux?

No, I didn't know that. Thanks for mentioning that.

Not delving into each implementation, and not knowing anything about the IIO
driver, I would think the PRU implementation would offload the utilization
of the Linux CPU (Arm9?) vs the IIO driver. Is this true?

I'm not really sure whether or not the call to prussdrv_pru_wait_event
is really implemented as an interrupt or that it actually polls.

In the guide,

http://processors.wiki.ti.com/index.php/PRU_Linux_Application_Loader_API_Guide

there is a call to prussdrv_start_irqthread , which is not in the
prussdrv.h (version 0.20170730 from my own repo http://beagle.tuks.nl
), so I kept the pthread code from the original source...

If it's a real interrupt, the arm gets like 128 samples (block_size) a
time and thus gets interrupted 128 times less, assuming the IIO driver
uses adc interrupts.

However, since the pru code is in c, one can also offload some further
code to the pru, depending on what one wishes to do with the samples.

In my case, for example, I am working on a project to study the
so-called "capacitor soakage" phenomenon:

In this application, I charge and discharge a capacitor under control
of the beagle, whereby I use the adc to measure voltage and current
over/trough the capacitor.

In this case, the voltage over the capacitor is used to switch the
charge and discharge control mosfets on/off, which I am currently
doing with the arm . I might move that to the pru as well, which would
improve reaction time and thus accuracy of the measurement cycle.

Also I thin the posted examples are worthwhile to those interfacing with an
external ADC and to those who are wanting more PRU coding examples..

regards and thanks for posting your project!

Thanks for your comments and regards,

Arend.

The IIO driver uses DMA to transfer samples from the ADC to the buffer, so the CPU utilization is very low. Using the PRU, the ARM processor has to copy the Buffer from the PRU memory to ARM memory, and this causes the ARM CPU utilization to be higher than using the IIO driver. The IIO driver freeze up the PRU to do something more important.

Regards,
John

Hi Arend, thanks for sharing your project!

Hi Thomas,

Hi Arend, thanks for sharing your project!

If it's a real interrupt, the arm gets like 128 samples (block_size) a
time and thus gets interrupted 128 times less, assuming the IIO driver
uses adc interrupts.
...
In this case, the voltage over the capacitor is used to switch the
charge and discharge control mosfets on/off, which I am currently
doing with the arm . I might move that to the pru as well, which would
improve reaction time and thus accuracy of the measurement cycle.

How can you improve reaction time, when you get then samples in blocks of
128? In worst case (when your triggering value is at the beginning of the
block) you'll add further latency.

For fast reaction time you have to evaluate the samples one-by-one, and in
this case the maximum sampling rate is in the range of 200 to 250 kS/s. DMA
transfer in blocks is useful for measuring tasks, but not for closed loop
controlls.

The PRU polls the fifo's of the adc and fetches any samples as soon as
they become available. So, by moving the mosfet switch on/off code
*to* *the* *PRU*, one can indeed evaluate the samples one by one,
provided the PRU is fast enough to:

a) keep up with the adc WHILE
b) doing some additional processing whenever a sample becomes available.

Also, with the PRU one can directly write to the GPIO registers and
thus bypass the sysfs interface, reducing further latency.

In other words: when comparing this PRU fifo access technique to DMA
(block) access, one obtains the *possibility* to implement closed
loop controls *on* *the* *PRU*.

Regards,

Arend.

Hi all,

When working further with this code, I found out that interrupts and
thus samples were missed, which was quite unpleasant for my
project......

In the project I used this, I replaced the dual buffer strategy with a
ring buffer.

I have just updated the bbb-pru example code such that:

1) missed interrupts are detected;
2) the sampling thread is run at high priority.

It turns out that with a blocksize less than about 2k, interrupts are
being missed when the arm is under load (like when compiling another
project at the same time). See some log files in the sample code
directory apps/adc.

The example code is at: https://github.com/l4m4re/bbb-pru

The project where I implemented a ring buffer is at:
https://github.com/l4m4re/SoakageTester

Most of the code is in Sampler.cpp and pru.c, but it's quite messy at
the moment and subject to change as well. I may add a ring buffer
example to the bbb-pru code project at some point, so others can make
use of this.

All in all, this code is useful (IMHO) as a base for implementing
closed loop controls on the pru, while using the arm to store the data
and/or do post-processing, etc. The use of a ring buffer is helpful,
because in that case one can afford to miss a few interrupts without
losing data, while still having the data available on the arm
reasonably fast *most* of the time...

Regards,

Arend.

Arend Lammertink, MScEE,
Goor, The Netherlands.
W: http://www.tuks.nl
T: +316 5425 6426