Analog read from all 7 inputs using PRU and a host C program for beaglebone black

1

I'm kinda new to the beaglebone black world running on a AM335X Cortex A8 processor and I would like to use the PRU for fast analog read with the maximum sampling rate possible.

I would like to read all 7 inputs in a loop form like:

while( n*7 < sampling_rate){ //initial value for n = 0
    read(AIN0); //and store it in shared memory(7*n + 0)
    read(AIN1); //and store it in shared memory(7*n + 1)
    read(AIN2); //and store it in shared memory(7*n + 2)
    read(AIN3); //and store it in shared memory(7*n + 3)
    read(AIN4); //and store it in shared memory(7*n + 4)
    read(AIN5); //and store it in shared memory(7*n + 5)
    read(AIN6); //and store it in shared memory(7*n + 6)
    n++;
}

so that I can read them from a host program running on the main processor. Any idea how to do so? I tried using a ready code called ADCCollector.c from a package named AM335x_pru_package but I can't figure out how to get all the addresses and values of the registers used.

This is the code I was trying to modify (ADCCollector.p):

.origin 0 // offset of the start of the code in PRU memory
.entrypoint START // program entry point, used by debugger only

#include "ADCCollector.hp"

#define BUFF_SIZE 0x00000fa0 //Total buff size: 4kbyte(Each buffer has 2kbyte: 500 piece of data
#define HALF_SIZE BUFF_SIZE / 2

#define SAMPLING_RATE 1 //Sampling rate(16khz) //***//16000
#define DELAY_MICRO_SECONDS (1000000 / SAMPLING_RATE) //Delay by sampling rate
#define CLOCK 200000000 // PRU is always clocked at 200MHz
#define CLOCKS_PER_LOOP 2 // loop contains two instructions, one clock each
#define DELAYCOUNT DELAY_MICRO_SECONDS * CLOCK / CLOCKS_PER_LOOP / 1000 / 1000 * 3  //if sampling rate = 98000 --> = 3061.224

.macro DELAY
    MOV r10, DELAYCOUNT
    DELAY:
        SUB r10, r10, 1
        QBNE DELAY, r10, 0
.endm

.macro READADC
    //Initialize buffer status (0: empty, 1: first buffer is ready, 2: second buffer is ready)
    MOV r2, 0
    SBCO r2, CONST_PRUSHAREDRAM, 0, 4 

    INITV:
        MOV r5, 0 //Shared RAM address of ADC Saving position 
        MOV r6, BUFF_SIZE  //Counting variable 

    READ:
        //Read ADC from FIFO0DATA
        MOV r2, 0x44E0D100 
        LBBO r3, r2, 0, 4 
        //Add address counting
        ADD r5, r5, 4
        //Write ADC to PRU Shared RAM
        SBCO r3, CONST_PRUSHAREDRAM, r5, 4 

        DELAY

        SUB r6, r6, 4
        MOV r2, HALF_SIZE
        QBEQ CHBUFFSTATUS1, r6, r2 //If first buffer is ready
        QBEQ CHBUFFSTATUS2, r6, 0 //If second buffer is ready
        QBA READ

    //Change buffer status to 1
    CHBUFFSTATUS1:
        MOV r2, 1 
        SBCO r2, CONST_PRUSHAREDRAM, 0, 4
        QBA READ

    //Change buffer status to 2
    CHBUFFSTATUS2:
        MOV r2, 2
        SBCO r2, CONST_PRUSHAREDRAM, 0, 4
        QBA INITV

    //Send event to host program
    MOV r31.b0, PRU0_ARM_INTERRUPT+16 
    HALT
.endm

// Starting point
START:
    // Enable OCP master port
    LBCO r0, CONST_PRUCFG, 4, 4      //#define CONST_PRUCFG      C4     taken from ADCCollector.hp
    CLR r0, r0, 4
    SBCO r0, CONST_PRUCFG, 4, 4

    //C28 will point to 0x00012000 (PRU shared RAM)
    MOV r0, 0x00000120
    MOV r1, CTPPR_0
    ST32 r0, r1

    //Init ADC CTRL register
    MOV r2, 0x44E0D040
    MOV r3, 0x00000005
    SBBO r3, r2, 0, 4

    //Enable ADC STEPCONFIG 1
    MOV r2, 0x44E0D054
    MOV r3, 0x00000002
    SBBO r3, r2, 0, 4



    //Init ADC STEPCONFIG 1
    MOV r2, 0x44E0D064
    MOV r3, 0x00000001 //continuous mode
    SBBO r3, r2, 0, 4

    //Read ADC and FIFOCOUNT
    READADC

Another question is: if I simply changed the #define Sampling_rate from 16000 to any other number below or equal to 200000 in the (.p) file, I will get that sampling rate? or should I change other things?

Thanks in advance.

real-time
sampling
beagleboneblack
adc
asked on Stack Overflow Sep 25, 2014 by user3787524

2 Answers

0

I used the c wrappers from libpruio: http://www.freebasic.net/forum/viewtopic.php?f=14&t=22501

and then use this code to get all my ADC values:

#include "stdio.h"
#include "c_wrapper/pruio.h" // include header
#include "sys/time.h"

//! The main function.
int main(int argc, char **argv) {
    struct timeval start, now;
    long mtime, seconds, useconds;    
    gettimeofday(&start, NULL);
    int i,x;
    pruIo *io = pruio_new(PRUIO_DEF_ACTIVE, 0x98, 0, 1); //! create new driver structure
    if (pruio_config(io, 1, 0x1FE, 0, 4)){ // upload (default) settings, start IO mode
      printf("config failed (%s)\n", io->Errr);}
    else {

        do {
            gettimeofday(&now, NULL);
            seconds  = now.tv_sec  - start.tv_sec;
            useconds = now.tv_usec - start.tv_usec;
            mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
            printf("%lu",mtime);
            for(i = 1; i < 9; i++) {
                printf(",%d", io->Adc->Value[i]); //0-66504 for 0-1.8v
            }
            printf("\n"); 

            x++;
        }while (mtime < 100);

        printf("count: %d \n", x);

        pruio_destroy(io);        /* destroy driver structure */
    }
    return 0;
}
answered on Stack Overflow Jan 9, 2015 by tomzooi
0

In your example you use libpruio in IO mode (synchronous) and therefore you have no control over the sampling rate, since the host CPU doesn't work in real-time.

To get the maximum sampling rate (as mentioned in the OP) you have to use either RB or MM mode. In those modes libpruio buffers the samples in memory and the host can access them asynchronously. See example rb_file.c (or triggers.bas) in the libpruio package.

answered on Stack Overflow Jan 12, 2015 by TJF • edited Jan 12, 2015 by Kalman

User contributions licensed under CC BY-SA 3.0