How fast/reliable is ADC sampling from the PRU

4,535 views
Skip to first unread message

jleb

unread,
Nov 2, 2014, 8:24:48 PM11/2/14
to beagl...@googlegroups.com
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!

TJF

unread,
Nov 3, 2014, 10:12:22 AM11/3/14
to beagl...@googlegroups.com
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

jleb

unread,
Nov 3, 2014, 10:45:43 AM11/3/14
to beagl...@googlegroups.com
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

TJF

unread,
Nov 4, 2014, 11:57:48 AM11/4/14
to beagl...@googlegroups.com
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

jleb

unread,
Nov 4, 2014, 10:43:31 PM11/4/14
to beagl...@googlegroups.com
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! :)

Cheers,

jleb

Luciano

unread,
Dec 9, 2014, 7:01:30 AM12/9/14
to beagl...@googlegroups.com
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.

-----------------------

/*
Test ADC max sample rate
Libpruio 0.0.2
Compile by:
gcc -Wall -o ReadADC ReadADC.c /usr/local/lib/freebasic/fbrt0.o -lpruio -L"/usr/local/lib/freebasic/" -lfb -lpthread -lprussdrv -ltermcap -lsupc++
*/
#define _GNU_SOURCE 1
#include "stdio.h"
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include "../c_wrapper/pruio_c_wrapper.h" 
#include "../c_wrapper/pruio_pins.h"
#include <stdlib.h>
#include <stdbool.h> 
#include <time.h>

#define P1 P9_14

int main(int argc, char **argv)
{

  PruIo *io = pruio_new(0, 0x98, 0 ,1);   
  pruio_config(io, 0, 0x1FE, 0, 4, 0);    

if (io->Errr) {
               printf("initialisation failed (%s)\n", io->Errr);}

if (pruio_gpio_set(io, P1, PRUIO_OUT1, PRUIO_LOCK_CHECK)) {
                   printf("failed setting P1 (%s)\n", io->Errr);}

if (pruio_config(io, 0, 0x1FE, 0, 4, 0)) {
                       printf("config failed (%s)\n", io->Errr);}

        while (1) 
                {
                        if (io->Value[1] < 25000  ) 
                        {
                                pruio_gpio_out(io, P1, 1);
                                printf(" %6d ", io->Value[1]);
                                printf("\n");
                        }
                        else
                        {
                                pruio_gpio_out(io, P1, 0);
                                printf(" %6d ", io->Value[1]);
                                printf("\n");
                        }
                }
  pruio_destroy(io);                   
        return 0;
} // end of main

---------------------
The thing is that I'm only getting about 10kHz. It is normal to just get this sample rate?


Thanks in advance.
Luciano.

luciano...@gmail.com

unread,
Dec 9, 2014, 11:55:38 AM12/9/14
to beagl...@googlegroups.com, luciano...@gmail.com
if you help me*

luciano...@gmail.com

unread,
Dec 10, 2014, 11:55:50 AM12/10/14
to beagl...@googlegroups.com, luciano...@gmail.com
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:
Am Dienstag, 9. Dezember 2014 13:01:30 UTC+1 schrieb Luciano:
The thing is that I'm only getting about 10kHz. It is normal to just get this sample rate

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?

TJF

unread,
Dec 11, 2014, 11:35:18 AM12/11/14
to beagl...@googlegroups.com, luciano...@gmail.com
Hello Luciano,


Am Mittwoch, 10. Dezember 2014 17:55:50 UTC+1 schrieb luciano...@gmail.com:
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.

luciano...@gmail.com

unread,
Dec 15, 2014, 2:17:54 PM12/15/14
to beagl...@googlegroups.com, luciano...@gmail.com
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.

TJF

unread,
Dec 16, 2014, 1:32:04 AM12/16/14
to beagl...@googlegroups.com, luciano...@gmail.com


Am Montag, 15. Dezember 2014 20:17:54 UTC+1 schrieb luciano...@gmail.com:
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.

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.

luciano...@gmail.com

unread,
Dec 16, 2014, 3:47:18 AM12/16/14
to beagl...@googlegroups.com, luciano...@gmail.com
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.

luciano...@gmail.com

unread,
Dec 16, 2014, 3:54:06 AM12/16/14
to beagl...@googlegroups.com, luciano...@gmail.com
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:

luciano...@gmail.com

unread,
Dec 16, 2014, 5:12:02 AM12/16/14
to beagl...@googlegroups.com
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.

TJF

unread,
Dec 16, 2014, 11:53:13 AM12/16/14
to beagl...@googlegroups.com, luciano...@gmail.com
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.
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

luciano...@gmail.com

unread,
Dec 16, 2014, 4:57:41 PM12/16/14
to beagl...@googlegroups.com, luciano...@gmail.com
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. :)

Best regards,
Luciano.

TJF

unread,
Dec 17, 2014, 9:58:37 AM12/17/14
to beagl...@googlegroups.com, luciano...@gmail.com
@Luciano


Am Dienstag, 9. Dezember 2014 13:01:30 UTC+1 schrieb 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


TJF

unread,
Dec 17, 2014, 2:03:03 PM12/17/14
to beagl...@googlegroups.com
@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


luciano...@gmail.com

unread,
Dec 17, 2014, 3:24:46 PM12/17/14
to beagl...@googlegroups.com
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.

TJF

unread,
Dec 19, 2014, 9:04:00 AM12/19/14
to beagl...@googlegroups.com, luciano...@gmail.com


Am Dienstag, 16. Dezember 2014 22:57:41 UTC+1 schrieb luciano...@gmail.com:
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.
...
once I didn't understand welI what I've changed. Maybe you can tell me, if possible. 

The header file pruio.h is generated manualy by translating FB source to C. I just forgot to translate this enumerators (will be fixed in next version). There's also a copy / paste error in one of the function names (can't remember which ATM). Just check the mails in the FB thread you mentioned.
 
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 .
 
As I said, you can use a CAP pin to measure the frequency (as I did).

TJF

unread,
Dec 19, 2014, 9:22:13 AM12/19/14
to beagl...@googlegroups.com, luciano...@gmail.com


Am Mittwoch, 17. Dezember 2014 21:24:46 UTC+1 schrieb luciano...@gmail.com:
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. 

That's wrong. The above snippet is a closed loop control. Its output toggles at maximum 93 kHz. This means the controller runs at twice that speed (186 kHz). This is 200 kHz ADC sampling rate plus some time to check the value and switch the GPIO output.

This additional time varies, depending on the load of the host CPU (interrupts handling network, grafics, keyboard, mouse, ...), as you can see in the differences between Minimum and Maximum values. If you're going to layout a controller, dimension it for the Minimum frequency (in this case 120 kHz = 2 * 60 kHz).
 
BR

luciano...@gmail.com

unread,
Dec 20, 2014, 3:50:49 PM12/20/14
to beagl...@googlegroups.com, luciano...@gmail.com
Hey TJF,

I used the CAP pin and I got low values of frequency, using just one ADC, and it varies.


I think it is because of the printf() function as you refered once. How can I get the real frequency without using the printf() or an oscilloscope? And how can I get its minimum, average e maximum?

This is the code I used.

--------------------
#define P1 P9_14
#define P2 P9_42
int main(int argc, char **argv)
{
        pruIo *Io = pruio_new(PRUIO_DEF_ACTIVE, 0, 0, 0); //! create new driver structure

    if (Io->Errr) {
               printf("initialisation failed (%s)\n", Io->Errr);}

        if (pruio_cap_config(Io, P1, 2.)) { // configure input pin
          printf("failed setting input @P_IN (%s)\n", Io->Errr);}

        float f1,d1;

         if (pruio_config(Io, 1, 2 , 0, 4)) {
                       printf("config failed (%s)\n", Io->Errr);}

        if (pruio_gpio_setValue(Io, P1, 0x8F)) {
                   printf("failed setting P1 (%s)\n", Io->Errr);}

        while (1)
{
                                double a= Io->Adc->Value[1];

                        if (a < 25000  )
                         {
                                pruio_gpio_config(Io, P1, 0x8F);

}
                        else
                        {
                                pruio_gpio_config(Io, P1, 0xF);

                        }
                  if (pruio_cap_Value(Io, P1, &f1, &d1)) { //    get current input
                        printf("failed reading input @P1 (%s)\n", Io->Errr); }

                        printf("\r    Frequency: %10f , Duty: %10f     ", f1, d1); // info

                }
  pruio_destroy(Io);
        return 0;
} // end of main

----------------------

Thank you for your attention.
BR,
Luciano.

Lenny

unread,
Dec 21, 2014, 6:19:08 AM12/21/14
to beagl...@googlegroups.com, luciano...@gmail.com
The ADC can definitely sample at 1.6 MHz, that is four channels could go up to 400 kHz each (sequentially). The 1.6 MHz correspond to the minimal 15 steps for one sample when setting the ADC clock to 24 MHz, that is, setting the CLKDIV register to zero, and not to 7 as it is set by default (which explains your 200kHz theoretical sampling rate). I have my own assembler code which works, but as it is a bit older, it is not in line with newer things like libpruio. If you want to have a look at my code to get it working let me know and I'll send it to you.

luciano...@gmail.com

unread,
Dec 21, 2014, 7:42:28 AM12/21/14
to beagl...@googlegroups.com, luciano...@gmail.com
Hey Lenny,

At this moment I need fast control of current and tension. That depends of the ADC sample rate. I would be very thankful if you sent it.
Best regards,
Luciano.

TJF

unread,
Dec 21, 2014, 10:42:47 AM12/21/14
to beagl...@googlegroups.com, luciano...@gmail.com

Am Sonntag, 21. Dezember 2014 12:19:08 UTC+1 schrieb Lenny:
The ADC can definitely sample at 1.6 MHz, that is four channels could go up to 400 kHz each (sequentially). The 1.6 MHz correspond to the minimal 15 steps for one sample when setting the ADC clock to 24 MHz, that is, setting the CLKDIV register to zero, and not to 7 as it is set by default (which explains your 200kHz theoretical sampling rate). I have my own assembler code which works, but as it is a bit older, it is not in line with newer things like libpruio. If you want to have a look at my code to get it working let me know and I'll send it to you.

Hi Lenny,

which hardware do you refer to?

On my Beaglebone Black the register ADC_CLKDIV is 0 (zero) by default.

And yes, it's true, that each single step samples at 1.6 MHz. But when I trigger the ADC subsystem by software and restart the step sequence (write to register STEPENABLE) at more than 250 kHz, the timing gets inaccurate. It doesn't matter how many steps are active. That's why I limit the sum-sampling rate to 200 kHz in libpruio to be at the save site (200 kHz also mentioned in the TI TRM).

BR

Lenny

unread,
Dec 22, 2014, 7:59:10 PM12/22/14
to beagl...@googlegroups.com, luciano...@gmail.com
Hi TJF and luciano,

below you can find the assembler code which I used. The critical configuration step is this one
    //ADC_CLKDIV
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000000   //the 24MHz-clock rate is divided by VALUE+1 to yield ADC_CLOCK
    SBBO VALUE,ADDR,0x4C,4 //important to write all 4 bytes even though only the first 2 count - if this is not done, the change doesnt take effect for some reason 

For some reason, I struggled for some time believing that the ADC_CLKDIV register was set to zero. Finally, all read and write operations went wrong if I only did a 1 or 2 Byte read/write operation to the register. It only worked properly when I read/wrote a full 4 Bytes to the register.

The main loop of my program arms a single ADC acquisition per cycle, waits for some time, and then retrieves a single value from the FIFO. There are two sources of timing jitter:
1) The read operation from the FIFO takes on my BBB something between 42 and 44 PRU clock cycles. This is as far as I observed not fully deterministic (some memory bus synchronization issue i guess). The solution for this which I used is to have PRU0 running just as a timer (and performing some well-timed calculations in the meantime), and let the PRU1, after it has retrieved a new ADC value, stall until PRU0 has finished its well-defined number of clock cycles and launch a new acquisition only after the PRU0-PRU1 synchronisation has taken place. If one is careful about the number of clock cycles per loop and the moments when one arms and reads out the ADC, then this method only costs a few (<10) clock cycles but improves control over the sampling time a lot.

2) As far as I observed, the ADC clock is not synchronized with the PRU clock. So if the loop frequency is not a fraction of 24 MHz, there is a "beating" between the two processes. If the loop frequency is a fraction of 24 MHz, then there is from time to time a "glitch" between the two clocks. I used the code below for a PID controller. When I set it to pure proportional response, I observe random jumps in the group delay by 1/24MHz. For my application this is not a problem. However, after knowing this, if I would do this again, I would use an external ADC triggered by the PRU for exactly this reason. If you should happen to find a way to internally synchronize the ADC and PRU clocks, I would be really grateful for a comment.

With all this working, I can read data from a single ADC channel and output this on the r30 register pins (hooked to a parallel DAC) at 1.4 MHz rate (im not using the full 1.6 MHz because of the random glitches in point 2) with only a few degrees phase jitter, again due to point 2). If the synchronization with two PRUs interests you, you can also check out the full code for both PRU's in the attached zip file.

//prucode.hp
#define ADC_TSC    0x44E0D000
#define FIFO0COUNT 0x44E0D0E4
#define FIFO0DATA  0x44E0D100

//Register usage for PRU1

#define CALLREG r0.w2
#define ENABLESTEPBUFFER r0.b0
#define DIVISORSHIFT r0.b1

//Registers holding constants
#define ADC_ADDR r1
#define OUTMASK r2

//Temporary values - used only for setup routines
#define ADDR r3
#define VALUE r4

#define X0 r4

.macro PAUSE
    QBNE ENDPROGRAM,r0,r0
.endm

.macro PAUSE10
    PAUSE
    PAUSE
    PAUSE
    PAUSE
    PAUSE
   
    PAUSE
    PAUSE
    PAUSE
    PAUSE
    PAUSE
.endm

.macro PAUSE100
    PAUSE10
    PAUSE10
    PAUSE10
    PAUSE10
    PAUSE10
   
    PAUSE10
    PAUSE10
    PAUSE10
    PAUSE10
    PAUSE10
.endm

//**********FIFO ROUTINES**************//
//attention: all offsets (3rd argument) are reduced by one, as ADC_ADDR was increased by one before

//Read a value from the FIFO0 buffer of the ADC
.macro  READFIFO
.mparam dst
    LBBO    dst,ADC_ADDR,0xFF,4
.endm

//Read a how many values are stored in the FIFO0 register of the ADC
.macro  READFIFOCNT
.mparam dst
    LBBO    dst,ADC_ADDR,0xE3,2
.endm

//Read the status register of the ADC IRQ
.macro  READFIFOIRQ
.mparam dst
    LBBO    dst,ADC_ADDR,0x23,2
.endm

//Clear the stats register of the ADC IRQ
.macro  CLEARFIFOIRQ
.mparam dst
    MOV     dst,0x0FFF
    SBBO    dst,ADC_ADDR,0x27,2
    READFIFOIRQ dst
.endm

//Trigger a new ADC single-shot measurement
.macro ENABLESTEP1
    //STEPENABLE:   
    //enable only STEP1 a.k.a. bit 1
    MOV ENABLESTEPBUFFER,0x02
    SBBO ENABLESTEPBUFFER,ADC_ADDR,0x53,1
.endm

//********* End of FIFO routines *******//


Next file with executable code


// prucode1.p
// Code for PRU1

.setcallreg r0.w2
.origin 0
.entrypoint START

//This is the first instruction that will be executed. For convenience, the START section appears only after several definitions
FIRSTINSTRUCTION:
    JMP START

//Many definitions: Register names, Constants, Macros etc.
#include "prucode.hp"

/////////////////////CALL routines////////////

//empty FIFO0 buffer so that zero values are in the 'pipeline'
EMPTYFIFOBUFFER:
    //get IRQ status before emptying
    READFIFOIRQ VALUE1.w0

    EMPTYFIFOBUFFERLOOP:
        //read a value
        READFIFO VALUE0.w0
        //check buffer count
        READFIFOCNT VALUE0.w2
        //read more values until the buffer is empty
        QBNE EMPTYFIFOBUFFER,VALUE.w2,0x0000

    //clear and read out the status of the buffer
    CLEARFIFOIRQ VALUE1.w2
    RET

//Init the whole program
INIT:
    //global config settings   
    LBCO    VALUE, CONST_PRUCFG, 4, 4
    CLR     VALUE, VALUE, 4       // Clear SYSCFG[STANDBY_INIT] to enable OCP master port
    CLR        VALUE, VALUE, 3        // set no-standby mode
    SET        VALUE, VALUE, 2        // set no-standby mode
    CLR        VALUE, VALUE, 1        // enable no-idle mode
    SET        VALUE, VALUE, 0        // enable no-idle mode
    SBCO    VALUE, CONST_PRUCFG, 4, 4
   
    // Configure the programmable pointer registers
    //Configs for PRU1 at offset 0x000024000, for PRU0 at offset 0x00022000
    MOV     ADDR, 0x00024000 
    //CONST_PRU01DRAM (C24) and CONST_PRU10DRAM (C25) offset (bits 11-8) config with bits 0-7 resp. 16-23
    MOV     VALUE, 0x00000000
    SBBO    VALUE,ADDR,0x20,4
    //CONST_PRUSHAREDRAM (C28) bits 23-8 are given by bits 15:0 of VALUE
    MOV     VALUE, 0x00010000>>8
    SBBO    VALUE,ADDR,0x28,4
    //CONST_DDR (C31) bits 23-8 are given by bits 31:16 of VALUE
    MOV     VALUE, 0x00000000
    SBBO    VALUE,ADDR,0x2C,4

    // Initialize Pru Ram to zero
    MOV VALUE,0x00000000
    FILLDRAM VALUE
    FILLSHAREDRAM VALUE

    RET
   
SETUPADC:
    // SETUP ADDRESS REGISTERS
    MOV ADC_ADDR,ADC_TSC
   
    //because Immediate value addressing only goes up to 255 and
    //because we need an offset of 256 for accessing the FIFO, increase the base address by 1
    //this means, that all other offsets need to be reduced by 1 in the following w.r.t. their offsets from the TRM
    ADD ADC_ADDR,ADC_ADDR,1
   
    //disable TSC_ADC_SS module,dont write channel id into FIFO
    //and disprotect step config registers       
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000004    //as above said already: disprotect and disable...
    SBBO VALUE,ADDR,0x40,4  //write value to CTRL register
   
    //ADC_CLKDIV
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000000   //the 24MHz-clock rate is divided by VALUE+1 to yield ADC_CLOCK
    SBBO VALUE,ADDR,0x4C,4 //important to write all 4 bytes even though only the first 2 count - if this is not done, the change doesnt take effect for some reason 
   
    //TS_CHARGE_STEPCONFIG
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000000    //no unnecessary delay, do nothing here
    SBBO VALUE,ADDR,0x5C,4  
   
    //TS_CHARGE_DELAY
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000001    //value must be greater than 0
    SBBO VALUE,ADDR,0x60,4  
   
    //STEPCONFIG1: SW continuous and no averages
    //bits 22-19: AINx; bit1-0 = 01 for continuous
    //bits 4-2 averaging: 100=16avg,000no avg
    MOV ADDR,ADC_TSC
    //MOV VALUE,0x00080001 //continuous mode
    MOV VALUE,0x00080000 //sw one-shot mode
    SBBO VALUE,ADDR,0x64,4  
   
    //STEPDELAY1 - add highest two bytes any value if the sampling need to be slowed down
    //integrate signal at analog input over 1 analog sample cycles
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000000    //bits 31-24: SampleDelay; bits 17-0: OpenDelay
    SBBO VALUE,ADDR,0x68,4   //STEPDELAY1 

    //for future implementations of averaging etc., we also configure steps 2 and 3 although they are never used here
   
    //STEPCONFIG2: SW continuous and no averages
    //bits 22-19: AINx; bit1-0 = 01 for continuous
    //bits 4-2 averaging: 100=16avg,000no avg
    MOV ADDR,ADC_TSC
    //MOV VALUE,0x00080001 //continuous mode
    MOV VALUE,0x00080000 //one-shot mode
    SBBO VALUE,ADDR,0x6C,4  
   
    //STEPDELAY2
    //integrate signal at analog input over 1 analog sample cycles
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000000    //bits 31-24: SampleDelay; bits 17-0: OpenDelay
    SBBO VALUE,ADDR,0x70,4   //STEPDELAY2 
   
    //STEPCONFIG3: SW continuous and no averages
    //bits 22-19: AINx; bit1-0 = 01 for continuous
    //bits 4-2 averaging: 100=16avg,000no avg
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00080001   
    SBBO VALUE,ADDR,0x74,4  

    //STEPDELAY3
    //integrate signal at analog input over 1 analog sample cycles
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000000  //bits 31-24: SampleDelay; bits 17-0: OpenDelay
    SBBO VALUE,ADDR,0x78,4   //STEPDELAY2 
   
    //STEPENABLE:   
    //enable only STEP1 a.k.a. bit 1
    //here: disable all steps for now
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000000
    SBBO VALUE,ADDR,0x54,4
   
    //FIFO0THRESHOLD
    MOV ADDR,ADC_TSC
    MOV VALUE,0x000000FF //bits 5:0  = Value-1 for FIFO Threshold interrupt generation
    SBBO VALUE,ADDR,0xE8,4
   
    //SYSCONFIG
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000000 //bits 3:2  00=force idle, 01=no idle, 10=smart idle, 11=smart idle+wakeup
    SBBO VALUE,ADDR,0x10,4
   
    //enable TSC_ADC_SS module,dont write channel id
    //and write-protect step config registers
    MOV ADDR,ADC_TSC
    MOV VALUE,0x00000001    
    SBBO VALUE,ADDR,0x40,4

    RET

START:
    //Zero all registers to avoid ambiguity
    ZERO &r0,124
    //Initialise memory and registers
    CALL INIT
   
    //Setup ADC
    CALL SETUPADC
   
    //empty FIFO0 buffer in case it is not clean
    CALL EMPTYFIFOBUFFER
    //re-zero the buffer variables used by EMPTYFIFOBUFFER
    MOV VALUE0,0x00000000
    MOV VALUE1,0x00000000
   
MAINLOOP:
    //ask for a new value from the ADC, whose preparation takes about 190 clock cycles
    //(should be only 125 cycles (625ns) and once this worked, but recently there are problems. Maybe due to readout latency from FIFO?)
    ENABLESTEP1
   
    //wait the required amount of time, and a bit more to make sure we dont read a value when there is none
    PAUSE100
    PAUSE100

    //do the same as in PID-Loop without sending Enablestep
    //retrieve sampled analog data
    READFIFO X0
    //now we have the last adc sampling value (from AIN1) in the register X0 and can write it to DDR memory or do whatever else we want
   //........

    JMP MAINLOOP

code.zip

luciano...@gmail.com

unread,
Jan 2, 2015, 9:08:07 AM1/2/15
to beagl...@googlegroups.com, luciano...@gmail.com
Hey TJF,

When possible, can you tell me something about my results when I got the sample rate using the CAP pin?

Thanks for your attention,
Luciano.

TJF

unread,
Jan 2, 2015, 11:48:13 PM1/2/15
to beagl...@googlegroups.com, luciano...@gmail.com
Am Freitag, 2. Januar 2015 15:08:07 UTC+1 schrieb luciano...@gmail.com:
Hey TJF,

When possible, can you tell me something about my results when I got the sample rate using the CAP pin?

Hi Luciano,

sorry, I can't, because I don't understand your code. The sequence

#define P1 P9_14
#define P2 P9_42
int main(int argc, char **argv)
{
       pruIo *Io = pruio_new(PRUIO_DEF_ACTIVE, 0, 0, 0); //! create new driver structure

    if (Io->Errr) {
              printf("initialisation failed (%s)\n", Io->Errr);}

        if (pruio_cap_config(Io, P1, 2.)) { // configure input pin
         printf("failed setting input @P_IN (%s)\n", Io->Errr);}
...

should through an error since P1 (= P9_14) has no CAP capability. Why don't you stop the program in case of that error? And where is the error message in your output picture? Are you sure you did run that code?

Also

...

                 
if (pruio_cap_Value(Io, P1, &f1, &d1)) { //    get current input
                       printf("failed reading input @P1 (%s)\n", Io->Errr); }

                  printf("\r    Frequency: %10f , Duty: %10f     ", f1, d1); // info
...

cannot work due to the same reason (missing CAP capability of P1) and should through error messages. You should have used P2 in both cases.

BR

luciano...@gmail.com

unread,
Jan 3, 2015, 12:31:17 AM1/3/15
to beagl...@googlegroups.com, luciano...@gmail.com
Hey TJF,

You're right! I copied and pasted the wrong code. I used the P2 (P9_42) for the CAP pin and I got that results.

--------

#define P1 P9_14
#define P2 P9_42

int main(int argc, char **argv)
{
        pruIo *Io = pruio_new(PRUIO_DEF_ACTIVE, 0, 0, 0); //! create new driver structure

    if (Io->Errr) {
               printf("initialisation failed (%s)\n", Io->Errr);}

        if (pruio_cap_config(Io, P2, 2.)) { // configure input pin
          printf("failed setting input @P2 (%s)\n", Io->Errr);}

        float f1,d1;

         if (pruio_config(Io, 1, 2 , 0, 4)) {
                       printf("config failed (%s)\n", Io->Errr);}

        if (pruio_gpio_setValue(Io, P1, 0x8F)) {
                   printf("failed setting P1 (%s)\n", Io->Errr);}

   while (1)
                {
                                double a= Io->Adc->Value[1];

                        if (a < 25000  )
                         {
                                pruio_gpio_config(Io, P1, 0x8F);
                        }
                        else
                        {
                             pruio_gpio_config(Io, P1, 0xF);
                        }
                  if (pruio_cap_Value(Io, P2, &f1, &d1)) { //    get current input
                        printf("failed reading input @P2 (%s)\n", Io->Errr); }
                        printf("\r    Frequency: %10f , Duty: %10f     ", f1, d1); // info
                }
  pruio_destroy(Io);
        return 0;
} // end of main
-------

So, as I said, I think it is because of the printf() function as you refered once. How can I get the real frequency without using the printf() or an oscilloscope? And how can I get its minimum, average e maximum?

BR,
Luciano.

Paco Gil

unread,
Mar 6, 2015, 6:47:00 AM3/6/15
to beagl...@googlegroups.com, luciano...@gmail.com
Hi TJF,

Did you find Lenny code interesting??  maybe libpruio can sample as fast as 1.4-1.6 MHz??

regards,

TJF

unread,
Mar 6, 2015, 5:42:50 PM3/6/15
to beagl...@googlegroups.com, luciano...@gmail.com


Am Freitag, 6. März 2015 12:47:00 UTC+1 schrieb Paco Gil:
maybe libpruio can sample as fast as 1.4-1.6 MHz??

It can. Just configure the ADC steps accordingly.

Lenny code is not interesting for a multi purpose library like libpruio.

BR

marcelo...@gmail.com

unread,
May 21, 2015, 9:48:11 AM5/21/15
to beagl...@googlegroups.com
Lenny, I'm sorry for this very late answer, but I found this topic really useful and you guys have given me the right directions.
I got to check all the information you gave here in the SPRUH73 guide (technical ashsssfyiafgygrf) and definitely, if one uses the default 3MHz ADC clock or the CLK_M_OSC 24MHz clock, that wouldn't perfectly sync with the PRUs as 200MHz is not a multiple of these values.
However, if I understood correctly, the PRCM acts like a prescaler, so maybe if you set PRCM = multiple of 3, do the clocks sync then?
I know, maybe you REALLY need the whole 24MHz... But in case your application accepts 8MHz, there's a chance of syncing them.

What do you think?

marcelo...@gmail.com

unread,
May 26, 2015, 9:39:22 AM5/26/15
to beagl...@googlegroups.com
It seems I was wrong, I noticed just the same amount of lost samples when dividing the 24MHz by 1, 3 or 6.

TJF

unread,
Dec 10, 2014, 11:24:09 AM12/10/14
to beagl...@googlegroups.com, luciano...@gmail.com
Am Dienstag, 9. Dezember 2014 13:01:30 UTC+1 schrieb Luciano:
The thing is that I'm only getting about 10kHz. It is normal to just get this sample rate

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?

Reply all
Reply to author
Forward
0 new messages