libpruio adc pruio_config help

I’m attempting to understanding how to use the libpruio code base.

Goal: Read 2 adc values as fast as possible.
Understanding: libpruio has a shared buffer that the pru writes to in this case from the adc, which will be then consumed by the userland application.

Going over io_input.c there a few things I still don’t understand.

pruio_config(io, 1, 0x1FE, 0, 4)

Could someone expand on these two parameters–I’ve read the docs just not sure I understand them:

Samp number of samples to fetch (defaults to zero)
Tmr timer value in [ns] to specify the sampling rate (defaults to zero, MM only)

So… assuming I want the fastest possible pru sampling dumped into the shared buffer that is then consumed by the shared buffer what could my config call be?

Also the sleep timing call-- are there specific times that need to be waited for specific buffer sizes-- or is that a guess and check sorta thing.

forgive me if these are stupid questions- just trying to wrap my head around this issue.

Hi!

To sample as fast as possible you’ve to configure your steps first, using function AdcUdt::setStep:

  • Avaraging = 0

  • SaD = 0

  • OpD = 0
    Then you call PruIo::config()

  • Samp is the number of samples to fetch. In MM mode it’s the size of the buffer, libpruio stops when the buffer is full. In RB mode the buffer is used as a ring buffer an sampling continues. Samp <= 1 means single mode (no accurate timing = variable sampling rate).

  • Tmr is the number of ns between the samples (MM and RB mode only), so that’s the parameter to specify the sampling rate.
    io_input.c uses single mode. Better start with rb_file.c example:

`
/** \file rb_file.c
\brief Example: fetch ADC samples in a ring buffer and save to file.

This file contains an example on how to use the ring buffer mode of
libpruio. A fixed step mask of AIN-0, AIN-1 and AIN-2 get configured
for maximum speed, sampled in to the ring buffer and from there saved
as raw data to some files.

Licence: GPLv3

Copyright 2014-2015 by Thomas{ dOt ]Freiherr[ At ]gmx[ DoT }net

Thanks for C code translation: Nils Kohrs (nils.kohrs [at] gmail.com)

Compile by: gcc -Wall -o rb_file rb_file.c -lpruio -lprussdrv

\since 0.2.0.2
*/

#include “unistd.h”
#include “time.h”
#include “stdio.h”
#include “…/c_include/pruio.h”

//! The main function.
int main(int argc, char **argv)
{
const uint32 tSamp = 123401; //!< The number of samples in the files (per step).
const uint32 tmr = 5000; //!< The sampling rate in ns (5000 → 200 kHz).
const uint32 NoStep = 3; //!< The number of active steps (must match setStep calls and mask).
const uint32 NoFile = 2; //!< The number of files to write.
const char *NamFil = “output.%u”; //!< The output file names.
struct timespec mSec;
mSec.tv_nsec=1000000;
pruIo *io = pruio_new(PRUIO_DEF_ACTIVE, 0x98, 0, 1); //! create new driver
if (io->Errr){
printf(“constructor failed (%s)\n”, io->Errr); return 1;}

do {
if (pruio_adc_setStep(io, 9, 0, 0, 0, 0)){ // step 9, AIN-0
printf(“step 9 configuration failed: (%s)\n”, io->Errr); break;}
if (pruio_adc_setStep(io,10, 1, 0, 0, 0)){ // step 10, AIN-1
printf(“step 10 configuration failed: (%s)\n”, io->Errr); break;}
if (pruio_adc_setStep(io,11, 2, 0, 0, 0)){ // step 11, AIN-2
printf(“step 11 configuration failed: (%s)\n”, io->Errr); break;}

uint32 mask = 7 << 9; //!< The active steps (9 to 11).
uint32 tInd = tSamp * NoStep; //!< The maximum total index.
uint32 half = ((io->ESize >> 2) / NoStep) * NoStep; //!< The maximum index of the half ring buffer.

if (half > tInd){ half = tInd;} // adapt size for small files
uint32 samp = (half << 1) / NoStep; //!< The number of samples (per step).

if (pruio_config(io, samp, mask, tmr, 0)){ // configure driver
printf(“config failed (%s)\n”, io->Errr); break;}

if (pruio_rb_start(io)){
printf(“rb_start failed (%s)\n”, io->Errr); break;}

uint16 *p0 = io->Adc->Value; //!< A pointer to the start of the ring buffer.
uint16 *p1 = p0 + half; //!< A pointer to the middle of the ring buffer.
uint32 n; //!< File counter.
char fName[20];
for(n = 0; n < NoFile; n++){
sprintf(fName, NamFil, n);
printf(“Creating file %s\n”, fName);
FILE *oFile = fopen(fName, “wb”);
uint32 i = 0; //!< Start index.
while(i < tInd){
i += half;
if(i > tInd){ // fetch the rest(no complete chunk)
uint32 rest = tInd + half - i;
uint32 iEnd = p1 >= p0 ? rest : rest + half;
while(io->DRam[0] < iEnd) nanosleep(&mSec, NULL);
printf(" writing samples %u-%u\n", tInd -rest, tInd-1);
fwrite(p0, sizeof(uint16), rest, oFile);
uint16 *swap = p0;
p0 = p1;
p1 = swap;
}
if(p1 > p0) while(io->DRam[0] < half) nanosleep(&mSec, NULL);
else while(io->DRam[0] > half) nanosleep(&mSec, NULL);
printf(" writing samples %u-%u\n", i-half, i-1);
fwrite(p0, sizeof(uint16), half, oFile);
uint16 *swap = p0;
p0 = p1;
p1 = swap;
}
fclose(oFile);
printf(“Finished file %s\n”, fName);
}
} while(0);
pruio_destroy(io);
return 0;
}

`

BR

Thanks! That code makes is much closer to what I am looking for.

Question-- When trying to compile and run it I get

root@beaglebone:~/libpruio-0.2/src/c_examples# ./rb_file config failed (sample rate too big)

Its failing on the call to config–

if (pruio_config(io, samp, mask, tmr, 0))

Is that expected? Should I just adjust the samp var? or is something I using out-of-date?

Hi!

or is something I using out-of-date?

Yes, outdated. In the new (unpublished) code in file src/pruio/pruio_adc.bas at line 128

d *= (Conf->ADC_CLKDIV + 1) * 417 '417 ≈ 1000000 / 2400 (= 1 GHz / 2.4 MHz)

d += 30 ’ PRU cycles for restart [GHz]

IF Tmr <= d THEN .Errr = @“sample rate too big” : RETURN .Errr

is replaced by

d = (d * (Conf->ADC_CLKDIV + 1) * 1000) \ 24
IF Tmr <= d ORELSE Tmr < 5000 THEN _
.Errr = @“sample rate too big” : RETURN .Errr

BTW: The example triggers.bas may give you an idea how to use MM mode.

BR

Thanks- You have been very helpful.

Another question I have is about precise timing; Does the PRU have access to any high precision timers / a way for the cpu to know down to the microsecond how long between 2 data points — Even if they are very far apart say for example- sampling only AIN-0 with averaging set to 24 and the tmr set @ 100,000-- could the time between the first point and the last point be accurate to 50 +/- microsecond across multiple hours ?

The ideal solution would allow for the pru to send back a 32/64 bit timer every X samples.

Hi!

The PRUSS have access to various timers. libpruio uses the PRU intern IEP timer (100 MHz, no OCP latency) to start the ADC measurements. It should be possible to achieve the specified accuracy (in MM or RB mode).

There is no 64 bit timer for the PRUSS. The IEP timer resets after each period, so it doesn’t make sense to send the value after X samples.

It is not possible to set avaraging to 24. Possible values are 16, 8, 4, 2 and 1 (= no avaraging). When you specify 24 for the Av parameter in the AdcUdt::setStep() call, libpruio will adapt it to 16.

BR

Hi!

Forgive me in advance for my english skills. I’m in a project in which i need to read beagle adc with timming accuracy and i don’t know how to do it. I’ve tried the rb_file.c mentioned before but even replacing that code i’m still getting the same error, sample rate too big. Do I have to do anything else after replace it?

Thanks!!

Hi!

This “sample rate too big” is a too rigid error message (a bug). The post you quoted explains how to fix it. Just adapt the pruio_adc.bas code and recompile the library. Find further information in this thread.

BR