ADC reading by PRU

I’ve tried to find some example of ADC reading by PRU for my project, but I couldn’t find it.
And I made that of source code and attach here for some people who have the same problem with me.
I hope it will be helpful.

[ AM335x ARM-CORE PRU ADC Example ]

  • Sequence of example
  1. Install compiling environment of PRU
  2. Enable PRU
  3. Enable ADC
  4. Example source
  • This example source collects ADC data from AIN0 pin with 16khz sampling rate.
  • The collected data are saved into “Results.txt” file.
  • The example source are “Makefile”, “ADCCollector.c”, “ADCCollector.p”, “ADCCollector.hp”

[ Install compile environment ]

  1. Get a copy of the am335x_pru_package → https://github.com/beagleboard/am335x_pru_package
    You also can download the am335x_pru_package here → https://github.com/beagleboard/am335x_pru_package/archive/master.zip
  2. If you downloaded the archive, unpack it somewhere under your home directory.
  3. Make a new directory /usr/include/pruss/ and copy the files prussdrv.h
    and pruss_intc_mapping.h into it (from am335x_pru_package-master/pru_sw/app_loader/include).
    Check the permissions; if you used the .zip file, these headers will likely have the execute bits on.
    It doesn’t really hurt anything, but is certainly not what you want.
  4. Change directory to am335x_pru_package-master/pru_sw/app_loader/interface
    then run: CROSS_COMPILE= make (note the space between the = and the command).
  5. The previous step should have created four files in am335x_pru_package-master/pru_sw/app_loader/lib: libprussdrv.a, libprussdrvd.a, libprussdrvd.so and libprussdrv.so.
    Copy these all to /usr/lib then run ldconfig.
  6. Change directory to am335x_pru_package-master/pru_sw/utils/pasm_source
    then run source linuxbuild to create a pasm executable one directory level up.
  • If linuxbuild doesn’t have permission to execution, give the permission by run this
    : chmod +x linuxbuild
    Copy it to /usr/bin and make sure you can run it.
    If you invoke it with no arguments, you should get a usage statement.

[ Enable PRU ]
Before using PRU, we need to enable the PRU core, you can do it as shown below

echo BB-BONE-PRU-01 > /sys/devices/bone_capemgr.8/slots

[ Enable ADC ]
Before using ADC, we also need to enable ADC, you can do it as shown below

echo cape-bone-iio > /sys/devices/bone_capemgr.*/slots

[ ADC Example - Makefile]
CFLAGS+=-Wall -Werror
LDLIBS+= -lpthread -lprussdrv

all: ADCCollector.bin ADCCollector

clean:
rm -f ADCCollector *.o *.bin

ADCCollector.bin: ADCCollector.p
pasm -b $^

ADCCollector: ADCCollector.o

[ ADC Example - ADCCollector.p]
// Developed by Youngtae Jo in Kangwon National University (April-2014)

// This program collects ADC from AIN0 with certain sampling rate.
// The collected data are stored into PRU shared memory(buffer) first.
// The host program(ADCCollector.c) will read the stored ADC data
// This program uses double buffering technique.
// The host program can recognize the buffer status by buffer status variable
// 0 means empty, 1 means first buffer is ready, 2 means second buffer is ready.
// When each buffer is ready, host program read ADC data from the buffer.

.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 16000 //Sampling rate(16khz)
#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

.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
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

[ ADC Example - ADCCollector.c]

/******************************************************************************

  • Include Files *
    ******************************************************************************/
    // Standard header files
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <unistd.h>
    #include <string.h>
    #include <time.h>

// Driver header file
#include <pruss/prussdrv.h>
#include <pruss/pruss_intc_mapping.h>

/******************************************************************************

  • Local Macro Declarations *
    ******************************************************************************/
    #define PRU_NUM 0
    #define OFFSET_SHAREDRAM 2048 //equivalent with 0x00002000

#define PRUSS0_SHARED_DATARAM 4
#define SAMPLING_RATE 16000 //16khz
#define BUFF_LENGTH SAMPLING_RATE
#define PRU_SHARED_BUFF_SIZE 500
#define CNT_ONE_SEC SAMPLING_RATE / PRU_SHARED_BUFF_SIZE

/******************************************************************************

  • Functions declarations *
    ******************************************************************************/
    static void Enable_ADC();
    static void Enable_PRU();
    static unsigned int ProcessingADC1(unsigned int value);

/******************************************************************************

  • Global variable Declarations *
    ******************************************************************************/
    static void *sharedMem;
    static unsigned int *sharedMem_int;

/******************************************************************************

  • Main *
    *****************************************************************************/
    int main (int argc, char
    argv[])
    {
    FILE *fp_out;
    unsigned int ret;
    tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
    int i = 0, cnt = 0, total_cnt = 0;
    int target_buff = 1;
    int sampling_period = 0;

if(argc != 2){
printf("\tERROR: Sampling period is required by second\n");
printf("\t %s [sampling period]\n", argv[0]);
return 0;
}
sampling_period = atoi(argv[1]);

/* Enable PRU /
Enable_PRU();
/
Enable ADC /
Enable_ADC();
/
Initializing PRU /
prussdrv_init();
ret = prussdrv_open(PRU_EVTOUT_0);
if (ret){
printf("\tERROR: prussdrv_open open failed\n");
return (ret);
}
prussdrv_pruintc_init(&pruss_intc_initdata);
printf("\tINFO: Initializing.\r\n");
prussdrv_map_prumem(PRUSS0_SHARED_DATARAM, &sharedMem);
sharedMem_int = (unsigned int
) sharedMem;

/* Open save file */
fp_out = fopen(“Results.txt”, “w”);
if(fp_out == NULL){
printf("\tERROR: file open failed\n");
return 0;
}

/* Executing PRU. /
printf("\tINFO: Sampling is started for %d seconds\n", sampling_period);
printf("\tINFO: Collecting");
prussdrv_exec_program (PRU_NUM, “./ADCCollector.bin”);
/
Read ADC */
while(1){
while(1){
if(sharedMem_int[OFFSET_SHAREDRAM] == 1 && target_buff == 1){ // First buffer is ready
for(i=0; i<PRU_SHARED_BUFF_SIZE; i++){
fprintf(fp_out, “%d\n”, ProcessingADC1(sharedMem_int[OFFSET_SHAREDRAM + 1 + i]));
}
target_buff = 2;
break;
}else if(sharedMem_int[OFFSET_SHAREDRAM] == 2 && target_buff == 2){ // Second buffer is ready
for(i=0; i<PRU_SHARED_BUFF_SIZE; i++){
fprintf(fp_out, “%d\n”, ProcessingADC1(sharedMem_int[OFFSET_SHAREDRAM + PRU_SHARED_BUFF_SIZE + 1 + i]));
}
target_buff = 1;
break;
}
}

if(++cnt == CNT_ONE_SEC){
printf(".");
total_cnt += cnt;
cnt = 0;
}

if(total_cnt == CNT_ONE_SEC * sampling_period){
printf("\n\tINFO: Sampling completed …\n");
break;
}
}

fclose(fp_out);
printf("\tINFO: PRU completed transfer.\r\n");
prussdrv_pru_clear_event (PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);

/* Disable PRU*/
prussdrv_pru_disable(PRU_NUM);
prussdrv_exit();

return(0);
}

/*****************************************************************************

  • Local Function Definitions *
    ****************************************************************************/
    /
    Enable ADC */
    static int Enable_ADC()
    {
    FILE *ain;

ain = fopen("/sys/devices/bone_capemgr.8/slots", “w”);
if(!ain){
printf("\tERROR: /sys/devices/bone_capemgr.8/slots open failed\n");
return -1;
}
fseek(ain, 0, SEEK_SET);
fprintf(ain, “cape-bone-iio”);
fflush(ain);
return 0;
}

/* Enable PRU */
static int Enable_PRU()
{
FILE *ain;

ain = fopen("/sys/devices/bone_capemgr.8/slots", “w”);
if(!ain){
printf("\tERROR: /sys/devices/bone_capemgr.8/slots open failed\n");
return -1;
}
fseek(ain, 0, SEEK_SET);
fprintf(ain, “BB-BONE-PRU-01”);
fflush(ain);
return 0;
}

/*

  • FIFO0DATA register includes both ADC and channelID
  • so we need to remove the channelID
    */
    static unsigned int ProcessingADC1(unsigned int value)
    {
    unsigned int result = 0;

result = value << 20;
result = result >> 20;

return result;
}

[ ADC Example - ADCCollector.hp]
// *****************************************************************************/
// file: PRU_memAccess_DDR_PRUsharedRAM.hp
//
// brief: PRU_memAccess_DDR_PRUsharedRAM assembly constants.
//
//
// © Copyright 2012, Texas Instruments, Inc
//
// author M. Watkins
// *****************************************************************************/

#ifndef PRU_memAccess_DDR_PRUsharedRAM_HP
#define PRU_memAccess_DDR_PRUsharedRAM_HP

// ***************************************
// * Global Macro definitions *
// ***************************************

// Refer to this mapping in the file - \prussdrv\include\pruss_intc_mapping.h
#define PRU0_PRU1_INTERRUPT 17
#define PRU1_PRU0_INTERRUPT 18
#define PRU0_ARM_INTERRUPT 19
#define PRU1_ARM_INTERRUPT 20
#define ARM_PRU0_INTERRUPT 21
#define ARM_PRU1_INTERRUPT 22

#define CONST_PRUCFG C4
#define CONST_PRUDRAM C24
#define CONST_PRUSHAREDRAM C28
#define CONST_DDR C31

// Address for the Constant table Block Index Register (CTBIR)
#define CTBIR 0x22020

// Address for the Constant table Programmable Pointer Register 0(CTPPR_0)
#define CTPPR_0 0x22028

// Address for the Constant table Programmable Pointer Register 1(CTPPR_1)
#define CTPPR_1 0x2202C

.macro LD32
.mparam dst,src
LBBO dst,src,#0x00,4
.endm

.macro LD16
.mparam dst,src
LBBO dst,src,#0x00,2
.endm

.macro LD8
.mparam dst,src
LBBO dst,src,#0x00,1
.endm

.macro ST32
.mparam src,dst
SBBO src,dst,#0x00,4
.endm

.macro ST16
.mparam src,dst
SBBO src,dst,#0x00,2
.endm

.macro ST8
.mparam src,dst
SBBO src,dst,#0x00,1
.endm

// ***************************************
// * Global Structure Definitions *
// ***************************************

.struct Global
.u32 regPointer
.u32 regVal
.ends

// ***************************************
// * Global Register Assignments *
// ***************************************

.assign Global, r2, *, global

#endif //PRU_memAccess_DDR_PRUsharedRAM

I've tried to find some example of ADC reading by PRU for my project, but I
couldn't find it.
And I made that of source code and attach here for some people who have the
same problem with me.
I hope it will be helpful.

Thanks! At first glance, it looks rather helpful. I'd encourage you to
submit a fork to the am335x_pru_package on Github as many people are
doing to share software examples for using the PRUs. It would
certainly reduce the number of steps required for people to reproduce
your work.

[ AM335x ARM-CORE PRU ADC Example ]
- Sequence of example
1. Install compiling environment of PRU
2. Enable PRU
3. Enable ADC
4. Example source
    - This example source collects ADC data from AIN0 pin with 16khz
sampling rate.
- The collected data are saved into "Results.txt" file.
- The example source are "Makefile", "ADCCollector.c", "ADCCollector.p",
"ADCCollector.hp"

[ Install compile environment ]
1. Get a copy of the am335x_pru_package ->
GitHub - beagleboard/am335x_pru_package
    You also can download the am335x_pru_package here ->
https://github.com/beagleboard/am335x_pru_package/archive/master.zip
2. If you downloaded the archive, unpack it somewhere under your home
directory.
3. Make a new directory /usr/include/pruss/ and copy the files prussdrv.h
    and pruss_intc_mapping.h into it (from
am335x_pru_package-master/pru_sw/app_loader/include).
Check the permissions; if you used the .zip file, these headers will likely
have the execute bits on.
It doesn't really hurt anything, but is certainly not what you want.
4. Change directory to
am335x_pru_package-master/pru_sw/app_loader/interface
    then run: CROSS_COMPILE= make (note the space between the = and the
command).
5. The previous step should have created four files in
am335x_pru_package-master/pru_sw/app_loader/lib: libprussdrv.a,
libprussdrvd.a, libprussdrvd.so and libprussdrv.so.
    Copy these all to /usr/lib then run ldconfig.
6. Change directory to am335x_pru_package-master/pru_sw/utils/pasm_source
    then run source linuxbuild to create a pasm executable one directory
level up.
- If linuxbuild doesn't have permission to execution, give the permission by
run this
  : chmod +x linuxbuild
Copy it to /usr/bin and make sure you can run it.
If you invoke it with no arguments, you should get a usage statement.

[ Enable PRU ]
Before using PRU, we need to enable the PRU core, you can do it as shown
below
# echo BB-BONE-PRU-01 > /sys/devices/bone_capemgr.8/slots

Note that the "8" isn't always an "8", you should do:

# echo BB-BONE-PRU-01 > /sys/devices/bone_capemgr.*/slots

[ Enable ADC ]
Before using ADC, we also need to enable ADC, you can do it as shown below
# echo cape-bone-iio > /sys/devices/bone_capemgr.*/slots

[ ADC Example - Makefile]
CFLAGS+=-Wall -Werror
LDLIBS+= -lpthread -lprussdrv

all: ADCCollector.bin ADCCollector

clean:
rm -f ADCCollector *.o *.bin

ADCCollector.bin: ADCCollector.p
pasm -b $^

ADCCollector: ADCCollector.o

[ ADC Example - ADCCollector.p]
// Developed by Youngtae Jo in Kangwon National University (April-2014)

// This program collects ADC from AIN0 with certain sampling rate.
// The collected data are stored into PRU shared memory(buffer) first.
// The host program(ADCCollector.c) will read the stored ADC data
// This program uses double buffering technique.
// The host program can recognize the buffer status by buffer status
variable
// 0 means empty, 1 means first buffer is ready, 2 means second buffer is
ready.
// When each buffer is ready, host program read ADC data from the buffer.

.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 16000 //Sampling rate(16khz)
#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

.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
    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

[ ADC Example - ADCCollector.c]

/******************************************************************************
* Include Files
*
******************************************************************************/
// Standard header files
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

// Driver header file
#include <pruss/prussdrv.h>
#include <pruss/pruss_intc_mapping.h>

/******************************************************************************
* Local Macro Declarations
*
******************************************************************************/
#define PRU_NUM 0
#define OFFSET_SHAREDRAM 2048 //equivalent with 0x00002000

#define PRUSS0_SHARED_DATARAM 4
#define SAMPLING_RATE 16000 //16khz
#define BUFF_LENGTH SAMPLING_RATE
#define PRU_SHARED_BUFF_SIZE 500
#define CNT_ONE_SEC SAMPLING_RATE / PRU_SHARED_BUFF_SIZE

/******************************************************************************
* Functions declarations
*
******************************************************************************/
static void Enable_ADC();
static void Enable_PRU();
static unsigned int ProcessingADC1(unsigned int value);

/******************************************************************************
* Global variable Declarations
*
******************************************************************************/
static void *sharedMem;
static unsigned int *sharedMem_int;

/******************************************************************************
* Main
*
******************************************************************************/
int main (int argc, char* argv)
{
FILE *fp_out;
    unsigned int ret;
    tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
int i = 0, cnt = 0, total_cnt = 0;
int target_buff = 1;
int sampling_period = 0;

if(argc != 2){
printf("\tERROR: Sampling period is required by second\n");
printf("\t %s [sampling period]\n", argv[0]);
return 0;
}
sampling_period = atoi(argv[1]);

/* Enable PRU */
Enable_PRU();
/* Enable ADC */
Enable_ADC();
/* Initializing PRU */
    prussdrv_init();
    ret = prussdrv_open(PRU_EVTOUT_0);
    if (ret){
        printf("\tERROR: prussdrv_open open failed\n");
        return (ret);
    }
    prussdrv_pruintc_init(&pruss_intc_initdata);
    printf("\tINFO: Initializing.\r\n");
    prussdrv_map_prumem(PRUSS0_SHARED_DATARAM, &sharedMem);
    sharedMem_int = (unsigned int*) sharedMem;
/* Open save file */
fp_out = fopen("Results.txt", "w");
if(fp_out == NULL){
printf("\tERROR: file open failed\n");
return 0;
}

/* Executing PRU. */
printf("\tINFO: Sampling is started for %d seconds\n", sampling_period);
    printf("\tINFO: Collecting");
    prussdrv_exec_program (PRU_NUM, "./ADCCollector.bin");
/* Read ADC */
while(1){
while(1){
if(sharedMem_int[OFFSET_SHAREDRAM] == 1 && target_buff == 1){ // First
buffer is ready
for(i=0; i<PRU_SHARED_BUFF_SIZE; i++){
fprintf(fp_out, "%d\n", ProcessingADC1(sharedMem_int[OFFSET_SHAREDRAM + 1 +
i]));
}
target_buff = 2;
break;
}else if(sharedMem_int[OFFSET_SHAREDRAM] == 2 && target_buff == 2){ //
Second buffer is ready
for(i=0; i<PRU_SHARED_BUFF_SIZE; i++){
fprintf(fp_out, "%d\n", ProcessingADC1(sharedMem_int[OFFSET_SHAREDRAM +
PRU_SHARED_BUFF_SIZE + 1 + i]));
}
target_buff = 1;
break;
}
}

if(++cnt == CNT_ONE_SEC){
printf(".");
total_cnt += cnt;
cnt = 0;
}

if(total_cnt == CNT_ONE_SEC * sampling_period){
printf("\n\tINFO: Sampling completed ...\n");
break;
}
}

fclose(fp_out);
    printf("\tINFO: PRU completed transfer.\r\n");
    prussdrv_pru_clear_event (PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);

    /* Disable PRU*/
    prussdrv_pru_disable(PRU_NUM);
    prussdrv_exit();

    return(0);
}

/*****************************************************************************
* Local Function Definitions
*
*****************************************************************************/
/* Enable ADC */
static int Enable_ADC()
{
FILE *ain;

ain = fopen("/sys/devices/bone_capemgr.8/slots", "w");

This isn't always at "8".

if(!ain){
printf("\tERROR: /sys/devices/bone_capemgr.8/slots open failed\n");
return -1;
}
fseek(ain, 0, SEEK_SET);
fprintf(ain, "cape-bone-iio");

If you did this from the command-line, why are you doing it again here
within your program?

fflush(ain);
return 0;
}

/* Enable PRU */
static int Enable_PRU()

Again, this seems redundant with the command-line. If you are going to
do it within your program, I believe you need to make it a bit more
flexible.

{
FILE *ain;

ain = fopen("/sys/devices/bone_capemgr.8/slots", "w");
if(!ain){
printf("\tERROR: /sys/devices/bone_capemgr.8/slots open failed\n");
return -1;
}
fseek(ain, 0, SEEK_SET);
fprintf(ain, "BB-BONE-PRU-01");
fflush(ain);
return 0;
}

/*
* FIFO0DATA register includes both ADC and channelID
* so we need to remove the channelID
*/
static unsigned int ProcessingADC1(unsigned int value)
{
unsigned int result = 0;

result = value << 20;
result = result >> 20;

Wouldn't a mask be more efficient? I would think you were trying to
sign-extend, but you aren't. Well, the C compiler is probably smart
enough to optimize this out anyway.

Hi Youngtae,

I am trying to create a little example to read ADC data at high speed using the PRU. I would like to build something basic,
so that other people can use it after to build something useful on top of that. I am a beginner in the PRU area, so I would appreciate
if you can share your code with me, so I can start working from there. I will obviously put your name on my code, if you don’t mind to help
me with this little project.

Thank you in advanced

What about libpruio? (Flexible step configuration, up to 8Mb samples (limited by the kernel driver), programmable trigger events to start the measurement, …)

Hello,

Thanks for the samples, it will make a great help on performing the ADC reading using PRU.

I’m trying to use the code for this task, but when I run the sample application, I’m getting a segmentation fault.

Since I’m new on the BeagleBone issues, any tips regarding what I should be looking (regarding version, libraries, etc) would be very helpful.

The BBB is running the Angstrom default distro. Also, I’ve noticed that pasm compiler on the pru package is at 0.85 version while the OS pasm version is 0.84 (and the 0.84 returns a seg fault when used to compile the sample codes). Maybe this is a hint, but I do not know which additional files I’d should take a look at.

Thanks in advance for your attention.

Best regards,

Rodrigo

I’m trying to use the code for this task, but when I run the sample application, I’m getting a segmentation fault.

Segmentation faults in 90% occasions come from null-pointer dereferencing.
Please add few debug prints to your code and tell us where the crash happens.

(by the way, do you have/see any printout on the console, before the “segmentation fault”?)

Hi Rodrigo,

If you send me your code, I can help you to find the problem in your program. I have been writing some changes to the original work of Youngtae and it is working OK for me.

Regards,

Antonio

Hello,

Sorry for the delay on answering the mails group, but on the beginning of the week I do not have much time available to work on the BeagleBone issues.

I’ve downloaded the most recent Angstrom image (on the site) and I’ve just updated the BeagleBone Black now.

Regarding the segmentation fault: I was following the instructions on this page: http://boxysean.com/blog/2012/08/12/first-steps-with-the-beaglebone-pru/

The seg fault has happened in the execution of the sample: ./PRU_memAccessPRUDataRam

The info was something like:

AM33XX INFO: Initializing example. INFO: Executing example.

Segmentation fault.

(The remaining messages were not showed).

Since is is a sample code, I was thinking that this code would be reliable enough to confirm iif my BBB’s PRU is correctly configured and running.

In fact I’m trying to read analog data (ADC) using PRU (because the usual way, using the file system is not fast enough for what I need - but I accept any suggestions to get data from ADC in a 10KHz sample rate ).

I was planning to use the libpruio Library (my assembly is a little bit rusty and this library should help me to achieve this task) but last week I’ve got stuck into this seg fault issue.

Now, with an updated OS, I’ll try to follow these path again (perhaps I’ll be more lucky this time).

Is it possible to use the libpruio (and PRUSSDRV) on Angstrom or should I install Ubuntu or Debian instead?

Thanks for all your information and best regards,

Rodrigo.

Hello Rodrigo,

Hello,

I need to thank you very much.

I could successfully compile and run your code and this is exactly what I was looking for.

So, again: thank you very much.

If I may point just one minor detail: the declaration of the following two functions:

static void Enable_ADC();
static void Enable_PRU();

They are not matching the respective definitions (which are using int as return type) and this causes a compilation error.

But, after the corrections the code has run smoothly. :slight_smile:

Best regards,

Rodrigo T. Caropreso

Hi Rodrigo,

Can you send me your code? otherwise it is impossible for me to answer all your questions.

Regards,

Antonio

Hello sir, I have some question regarding your code please.

What is the maximum sampling rate that I can get using this code and what should I change to get that sampling rate?
If I want to read from all 7 analog pins, how can I do that?

Thank you for your help and your effort.

I got one error, and I don’t know how to solve this.

“ERROR: Sampling period is required by second”

Anyone can help me?

I use BBBIOlib-master. You can get it from https://github.com/VegetableAvenger/BBBIOlib
I think it is better than this library in my own opinion :slight_smile: It is easier to be installed and is complete. It also contains examples.

Thanks Mahmoud, I’ll try with this library .

Best regards

Using the PRU is a round about way to get regular sampling. The ADC can be
configured to sample at a regular rate and place samples in a FIFO and
interrupt as the FIFO fills. The PRU isn't needed. See the TRM.

Finnaly Works!

Thanks a lot!

That’s not entirely correct. Yes, you can configure the ADCSS to measure in continous mode, but you don’t have control over the timing. To get an exact sampling rate it needs a trigger, either one of the TIMER-[4-7] or a software trigger (ie. from PRU code). When using the PRU for trigger, it can also do FIFO clearance and low level data manipulation, like scaling the samples to 16 bit (ie. to use them for audio data). The key benefits of the PRU solution are being more flexible and coding single source on a real-time unit.

You can control the timing on the ADC with the open/close times on each step.

I have the ADC by itself working well enough for an 8KHz sampling with noise
filtering using the oversampling/averaging for audio purposes. The driver
convert the samples as 16bit (simple bit shift) as it unloads the FIFO. The
bigger problem is noise on the ADC itself.

Using the PRU would slam the internal bus twice eatting up bandwidth. It is no
big deal if you don't need the bandwidth...

I can use this code to get two ADC channels at the same time?