How fast/reliable is ADC sampling from the PRU

Hello all,

Still very new to the embedded systems world but really love the BBB so far!
I have a project in which I have to sample 4 analog inputs very quickly (at least 44.1 kHz).

I initially thought of using an external ADC and hooking that up through the BBB’s McASP
interface, but on second thought, I’d like to try it with the BBB’s built-in ADC, which although
is less powerful and precise, might be able to do the trick (and would serve as a good
stepping stone for more involved work if needed).

So the idea is pretty straight-forward: use the PRU to read in the ADC signals, put in a
circular buffer, and signal the other PRU as needed to transfer chunks to the main RAM for
processing.

However, I do worry about the sampling rate. Although the PRU is very deterministic, it
seems like accesses through the L3/L4 interconnects are not, i.e. depending on bus load,
copying from the ADC block to the PRU could take a varying amount of time. Is that correct?
This would be an issue because sample points could drift and vary in timing.

If that is indeed the case, what would be the best way to ensure consistent timing?

Thanks in advance!

Hello jleb!

There’s no problem fetching the samples from the ADC subsystem and store them in the external memory block (or DRam area). You don’t need both PRUSS, one can do that.

But the ADC subsystem isn’t fast enough. It needs 14 cycles to perform a step and a further cycle to start the step sequence. In your case that’s 1 + 4 * 14 = 57 cycles at 2.4 MHz, which results in a maximum sampling rate of 42.2 kHz. Depending on the signal impedance, you may need some open delay to stabilize the input.

In order to test the ADC subsystem, you may have a look at libpruio. It can be used to configure ADC steps and fetch samples in a ring buffer.

BR

Hi TJF,

Thanks for the detailed reply.
I will look into libpruio as well.

However, I’m a bit confused regarding the 2.4 MHz part.
Can’t the ADC run at 24 MHz? (Looking at 12.2.2 in the AM335x TRM)
If so, this would give me the breathing room I need, I think.

Thanks!

jleb

Hello jleb!

Sorry for the late answer. I did some testing, meanwhile, and it seems that you’re right. The ADC subsystem is clocked by CLK_M_OSC directly at 24 MHz. (First, I thought there’s an additionaly pre-scaler in the PRCM.)

Find attached a libpruio measurement from four channels, connected to a PWM output by different voltage dividers. The PWM frequency and the sampling rate are configured that way that a complete period plus two pixels get shown in the window. The result is as expected, and even if I increase the frequencies, all looks good up to a sampling rate of 200 kHz.

Screenshot - 04.11.2014 - 17:54:02.png

Hi TJF,

Thanks a lot for the elaborate reply and testing. Much appreciated.
Seems like this is the way to go.

I’ll probably pester you soon with questions regarding libpruio! :slight_smile:

Cheers,

jleb

Hey TFJ,

I did this way and I would be glad of you helped me in this question. I have installed Libpruio0.0.2, and I wrote a simple code just to read the ADC value (Value[1]) from the output port P9_14 to see it commute.(I don’t need all that libraries, but they are there for further algorithms). In the circuit I added a voltage divider with two 10k resistors, to get about 1,7V to not cross 1,8V.

if you help me*

Terça-feira, 9 de Dezembro de 2014 12:01:30 UTC, Luciano escreveu:

Hello Luciano,

the sampling rate could be higher if you activate less steps (in your code steps 1 to 8 are active). And you’re using the ADC steps in the default configuration (open delay 0x98, avaraging 4). You can speed up the step(s) by removing both of them. Also keep in mind that the printf() function calls slow down your program a lot.

BR

BTW:
Why are you calling pruio_config() twice?
Why don’t you use the current libpruio version 0.2?

Hey TFJ,

Thank you for answering my question. Calling pruio_config() twice was my bad. I didn’t notice. I already removed it as the printf() function. Anyway it gives the same result around 10kHz.

Pardon my ignorance but about the ADC, how can I activate less steps?
And how can I remove the default configuration (open delay 0x98, avaraging 4)? It is in the library “pruio_c_wrapper.h”? If so for what value should I change (#define PRUIO_DEF_AVRAGE 4)?

The thing about libpruio 0.2, is that I’ve already installed yesterday, but when I try to do the stepper example, it gives the message (setValue P1 error (no pin control)), and I don’t know yet what the problem might be, and I need to use the GPIO to do the same algorithm above. So I was still trying something on libpruio0.0.2. Maybe you can help me figure it out, if possible.

I thank you again,
Luciano.

Quarta-feira, 10 de Dezembro de 2014 16:24:09 UTC, TJF escreveu:

Hello Luciano,

Pardon my ignorance but about the ADC, how can I activate less steps?

And how can I remove the default configuration (open delay 0x98, avaraging 4)? It is in the library “pruio_c_wrapper.h”? If so for what value should I change (#define PRUIO_DEF_AVRAGE 4)?

never change the header file(s) unless you realy know what you’re doing!

Instead, change the parameters in the function calls.

  • pruio_new() configures default steps for ADC (1 to 8 for AIN-0 to AIN-7). Set all parameters to 0 (zero) for maximum speed, like
    pruio_new(io, 0, 0, 0);

    Find details here in the documentation.

  • pruio_config() configures the steps to use in its second parameter (third for C wrapper). Obviously you used step 2 (AIN-1), so write
    `
    pruio_config(io, 1, 1 << 2, 0, 4); // 1 << 2 = step 2 !!! libpruio-0.2 !!!

    `

    Find details here in the documentation.

The thing about libpruio 0.2, is that I’ve already installed yesterday, but when I try to do the stepper example, it gives the message (setValue P1 error (no pin control)), and I don’t know yet what the problem might be, and I need to use the GPIO to do the same algorithm above. So I was still trying something on libpruio0.0.2. Maybe you can help me figure it out, if possible.

The thing about libpruio-0.0.x is that pinmuxing doesn’t work at all. In your above case the pin P9_14 is in a matching mode by default. But other pins may not work with version 0.0.2.

The ‘no pin control’ error is because you executed the example as normal user, but pinmuxing requires admin privileges. Either configure all four pins before you start the example. Or, more simple, start the example with admin privileges like
sudo ./stepper
(On Ubuntu you’ve to enter your password and hit Return.)

Good luck! And take some time to read the docu.

Hey TFJ,

Thanks for clarifying my questions.
About Libpruio0.2, I tried exactly that command and stills giving the same error (setValue P1 error (no pin control).

In BBB, I’m using Debian (BeagleBone Black - 2GB eMMC) 2014-05-14, available by the beagleboard.org latest images. I don’t know if it might have something related with the version, because I get an error like below when I execute the command “echo libpruio > /sys/devices/bone_capemgr.slots”.

But when I went to /sys/devices/ it shows bone_capemgr.9, like was in Libpruio0.0.2.

So, when I want to run the other examples like io_input.c, I use the command echo PRUSSDRV> /sys/devices/bone_capemgr.9/slots, it works, except stepper and pwm_cap, giving the same error as before (setValue P1 error (no pin control).

About the installation I did all the steps required with admin previleges (http://users.freebasic-portal.de/tjf/Projekte/libpruio/doc/html+/ChaPreparation.html).

Pardon my ignorance, but I really don’t know what I’m doing wrong. So, if you could help me again, I would aprecciate very much. Already thank you for your attention.

Best regards,

Luciano.

Quinta-feira, 11 de Dezembro de 2014 16:35:18 UTC, TJF escreveu:

Those instructions were for version 0.0.2. (Sorry, I forgot to remove them from the server. How did you find them?)

Here’s the current libpruio-0.2 installation guide:

http://users.freebasic-portal.de/tjf/Projekte/libpruio/doc/html/_cha_preparation.html#SecInstallation

Good luck! And don’t hesitate to ask again in case of further trouble.

Hey TFJ,

Sorry, I copied and pasted the wrong link. I really did the installation following that link for libpruio 0.2 (http://users.freebasic-portal.de/tjf/Projekte/libpruio/doc/html/_cha_preparation.html#SecInstallation). The other link I found at google, when I wanted to compare libpruio 0.0.2 and libpruio 0.2.

And another thing is, that in this command “wget http://www.freebasic-portal.de/dlfiles/452/BBB_fbc-1.00.tar.bz2”, it downloaded “bbb_fbc-0.0.2.tar”, not the freebasic 1.0.0. So I searched and I found this one " http://www.freebasic-portal.de/dlfiles/589/BBB_fbc-1.00.tar.bz2", and it downloads “BBB_fbc-1.00.tar”.

I did install just like the instructions, and the result is the same. Can you see what might be the problem? Thank you for your attention.

Best regards,
Luciano.

Terça-feira, 16 de Dezembro de 2014 6:32:04 UTC, TJF escreveu:

Hey TFJ,

I forgot something. This link “http://www.freebasic-portal.de/dlfiles/539/libpruio-0.2.tar.bz2” from the installation section, it downloads “FB_prussdrv-0.0.tar”, so I found this, to download “libpruio-0.2.tar” (http://www.freebasic-portal.de/dlfiles/592/libpruio-0.2.tar.bz2).

Best regards,
Luciano.

Terça-feira, 16 de Dezembro de 2014 6:32:04 UTC, TJF escreveu:

Hey TFJ,

Just to confirm, I went to the directories and all the needed files are in the place they should be installed.
I don't know but maybe it is because of the capemgr in /sys/devices, as I referred above. Can it be?

Best regards,
Luciano.

Hi Luciano,

thanks for your reports. I didn’t know that each update gets a new path and that the download works even when the filename is wrong. I’ll have to fix this in the documentation.

Hey TFJ,

Just to confirm, I went to the directories and all the needed files are in the place they should be installed.
I don’t know but maybe it is because of the capemgr in /sys/devices, as I referred above. Can it be?

Best regards,
Luciano.

I’m not sure if I get this. In your post above you spoke about an error message, but I cannot find it. So a shot in the dark here.

After executing (admin privileges, !!! mind the position of the 'A' !!! in the copy command)

cp src/config/libpruio-0A00.dtbo /lib/firmware/libpruio-00A0.dtbo echo libpruio > /sys/devices/bone_capemgr.slots

all examples should work (pwm_adc, pwm_cap and stepper need admin privileges).

BR

Hey TFJ,

I tried that way and it didn’t work. Meanwhile I was searching about it and I found a website relating something like my problem at this forum (http://www.freebasic.net/forum/viewtopic.php?f=14&t=22501&hilit=sample+rate&start=75).
I read the user facine comment, I tried and stepper worked.

Then I was scrolling and he said he still had problems. He proposed a solution and then I saw the “enum pinMixung”.
I got the code you (TFJ) proposed next and I put it in the library pruio.h at /usr/local/include.

I already tried algorithms and I think it’s ok. I don’t know if this method I used will bring secondary effects once I didn’t understand welI what I’ve changed. Maybe you can tell me, if possible.
I didn’t have the oportunity to do some tests about the sample rate, because I don’t have an oscilloscope this moment. But when I have the oportunity I will report my results to you .

I thank you very much for the time you spent helping me. :slight_smile:

Best regards,
Luciano.

Terça-feira, 16 de Dezembro de 2014 16:53:13 UTC, TJF escreveu:

@Luciano

I did this way and I would be glad of you helped me in this question. I have installed Libpruio0.0.2, and I wrote a simple code just to read the ADC value (Value[1]) from the output port P9_14 to see it commute.(I don’t need all that libraries, but they are there for further algorithms). In the circuit I added a voltage divider with two 10k resistors, to get about 1,7V to not cross 1,8V.

I made a similar test running this loop (FreeBASIC, libpruio-0.2.2 in IO mode)

`
IF .Adc->setStep(1, 0, 0, 0, 0) THEN _ ’ configure fast Adc step
?“ADC setStep failed (” & *.Errr & “)” : EXIT DO

``FOR i AS INTEGER = 0 TO c
o = IIF(.Adc->Value[1] > &h7FFF, 0, 1)
IF .Gpio->setValue(POUT, o) THEN _ ’ set GPIO output
?“GPIO setValue failed (” & *.Errr & “)” : EXIT DO
NEXT
IF .Cap->Value(PIN, @f2, @d2) THEN _ ’ get CAP input
?“Cap->Value failed (” & *.Errr & “)” : EXIT DO, DO

...
`

The ADC step 1 is configured for maximum speed. I have no osziloscope, so I measured the output frequency by a CAP pin.

Here’s typical output from executing the above snippet about 50 times, frequency values in Hz:

`
ADC closed loop:
Minimum: 54704.59375
Avarage: 61088.24790736607
Maximum: 80321.28125

`

@Luciano

I changed your loop logic a bit, so that the GPIO output only changes after the ADC value is present

`
IF .Adc->setStep(1, 0, 0, 0, 0) THEN _ ’ configure fast Adc step
?“ADC setStep failed (” & *.Errr & “)” : EXIT DO

IF .config(1, 1 SHL 1) THEN _
?“config failed (” & *.Errr & “)” : EXIT DO

FOR i AS INTEGER = 0 TO c
WHILE .Adc->Value[1] <= &h7FFF : WEND ’ wait until high
IF .Gpio->setValue(POUT, 0) THEN _ ’ set GPIO low
?“GPIO setValue failed (” & *.Errr & “)” : EXIT DO
WHILE .Adc->Value[1] > &h7FFF : WEND ’ wait until low
IF .Gpio->setValue(POUT, 1) THEN _ ’ set GPIO high
?“GPIO setValue failed (” & *.Errr & “)” : EXIT DO
NEXT
if .Cap->Value(PIN, @f2, @d2) then _ ’ get CAP input
?“Cap->Value failed (” & *.Errr & “)” : EXIT DO, DO

`

and I get higher frequencies

ADC closed loop: Minimum: 69589.421875 Avarage: 91196.43975694444 Maximum: 93196.6484375

Hey TJF,

So, if I’m understandig it right, what I conclude is that with only one ADC working, the maximum sample rate we can get is around 93 kHz.
Above when you answered to jleb, you said:

“The result is as expected, and even if I increase the frequencies, all looks good up to a sampling rate of 200 kHz.”

How can I get that sample rate? 200kHz is the maximum according to the features of processor AM3359. (http://www.ti.com/product/AM3359/datasheet/abstract#SPRS7176831).

Thank you for your attention.

BR,
Luciano.

Quarta-feira, 17 de Dezembro de 2014 19:03:03 UTC, TJF escreveu:

@Luciano

I changed your loop logic a bit, so that the GPIO output only changes after the ADC value is present

`
IF .Adc->setStep(1, 0, 0, 0, 0) THEN _ ’ configure fast Adc step
?“ADC setStep failed (” & *.Errr & “)” : EXIT DO

IF .config(1, 1 SHL 1) THEN _
?“config failed (” & *.Errr & “)” : EXIT DO

FOR i AS INTEGER = 0 TO c
WHILE .Adc->Value[1] <= &h7FFF : WEND ’ wait until high
IF .Gpio->setValue(POUT, 0) THEN _ ’ set GPIO low
?“GPIO setValue failed (” & *.Errr & “)” : EXIT DO
WHILE .Adc->Value[1] > &h7FFF : WEND ’ wait until low
IF .Gpio->setValue(POUT, 1) THEN _ ’ set GPIO high
?“GPIO setValue failed (” & *.Errr & “)” : EXIT DO
NEXT
if .Cap->Value(PIN, @f2, @d2) then _ ’ get CAP input
?“Cap->Value failed (” & *.Errr & “)” : EXIT DO, DO

`

and I get higher frequencies

ADC closed loop: Minimum: 69589.421875 Avarage: 91196.43975694444 Maximum: 93196.6484375

Quarta-feira, 17 de Dezembro de 2014 19:03:03 UTC, TJF escreveu: