Trying to run the ADC from the PRU

238 views
Skip to first unread message

Phil

unread,
Sep 7, 2016, 9:23:52 AM9/7/16
to BeagleBoard
I am not having success in running the ADC from the PRU, and wondering if anyone can offer some guidance. I suspect my device tree overlay, but I am also uncertain about my PRU code. The overlay compiles and installs without error. The PRU code hangs on the loop that is waiting for FIFO0COUNT to be nonzero.

Both my PRU code and the device tree overlay are shown below. I am running kernel 4.4.19-bone-rt-r13.

Thanks,
Phil

(BTW, this message is cross-posted here. It is not clear to me which forum is the right place for this. Hopefully this won't irritate anyone too badly.)

PRU Code
// Interrupt strobe bit
#define PRU0_R31_VEC_VALID (1<<5)

// Interrupt signal number
// SIGNUM corresponds to pr1_pru_mst_intr[3]_intr_req
// which is named PRU0_ARM_INTERRUPT (19) in pruss_intc_mapping.h
// This interrupt is in turn mapped to PRU Interrupt channel 2,
// and then mapped to host interrupt 2 as well.
#define SIGNUM 3

#define ADC_BASE                0x44e0d000

// ADC registers, offset from ADC_BASE
#define CTRL                    0x0040
#define ADCSTAT                 0x0044
#define ADC_CLKDIV              0x004c
#define STEPENABLE              0x0054

#define STEPCONFIG1             0x0064
#define STEPDELAY1              0x0068
#define STEPCONFIG2             0x006C
#define STEPDELAY2              0x0070
#define STEPCONFIG3             0x0074
#define STEPDELAY3              0x0078
#define STEPCONFIG4             0x007C
#define STEPDELAY4              0x0080
#define STEPCONFIG5             0x0084
#define STEPDELAY5              0x0088
#define STEPCONFIG6             0x008C
#define STEPDELAY6              0x0090
#define STEPCONFIG7             0x0094
#define STEPDELAY7              0x0098
#define STEPCONFIG8             0x009C
#define STEPDELAY8              0x00A0

#define STEPCONFIG9             0x00A4
#define STEPDELAY9              0x00A8
#define STEPCONFIG10            0x00AC
#define STEPDELAY10             0x00B0
#define STEPCONFIG11            0x00B4
#define STEPDELAY11             0x00B8
#define STEPCONFIG12            0x00BC
#define STEPDELAY12             0x00C0
#define STEPCONFIG13            0x00C4
#define STEPDELAY13             0x00C8
#define STEPCONFIG14            0x00CC
#define STEPDELAY14             0x00D0
#define STEPCONFIG15            0x00D4
#define STEPDELAY15             0x00D8
#define STEPCONFIG16            0x00DC
#define STEPDELAY16             0x00E0

#define FIFO0COUNT              0x00e4
#define FIFO0DATA               0x0100

#define FIFO1COUNT              0x00f0
#define FIFO1DATA               0x0200
// Register Group base addresses/pointers
#define PRU0_CTRL_addr          0x00022000
#define PRU1_CTRL_addr          0x00024000
#define PRU_CFG_ptr             c4

// PRUx_Ctrl Registers: Register offsets
#define PRUx_CTRL_CTRL          0x00
#define PRUx_CTRL_STS           0x04
#define PRUx_CTRL_WAKEUP_EN     0x08
#define PRUx_CTRL_CYCLE         0x0c
#define PRUx_CTRL_STALL         0x10
#define PRUx_Ctrl_CTBIR0        0x20
#define PRUx_Ctrl_CTBIR1        0x24
#define PRUx_Ctrl_CTPPR0        0x28
#define PRUx_Ctrl_CTPPR1        0x2c


// Register allocations
// Registers r0-r3 are to be used for general purpose operations
#define adc_base                r4
#define fifo0data               r5
#define local                   c24
#define remote                  c25
#define shared                  c28
#define PRU0_CTRL_ptr           r22

.origin 0
.entrypoint INIT

INIT:
        // constant table offsets
        // This sets the following:
        //      c24 = 0x00000000        local PRU RAM
        //      c25 = 0x00002000        remote PRU RAM
        //      c28 = 0x00010000        shared RAM
        mov     PRU0_CTRL_ptr, PRU0_CTRL_addr
        mov     r0, 0
        sbbo    r0, PRU0_CTRL_ptr, PRUx_Ctrl_CTBIR0, 4
        mov     r0, 0x00000100
        sbbo    r0, PRU0_CTRL_ptr, PRUx_Ctrl_CTPPR0, 4
       
        // local constants
        mov     adc_base, ADC_BASE
        mov     fifo0data, FIFO0DATA
       
        // Enable OCP
        lbco    r0, PRU_CFG_ptr, 4, 4
        clr     r0, 4
        sbco    r0, PRU_CFG_ptr, 4, 4

        // Disable ADC
        lbbo    r0, adc_base, CTRL, 4
        and     r0.b0, r0.b0, 0xff
        sbbo    r0, adc_base, CTRL, 4
       
        // Run ADC at full speed
        mov     r0, 0
        sbbo    r0, adc_base, ADC_CLKDIV, 4
       
        // Configure STEPCONFIGx registers for channels 2-6
        // no averaging, SW-enabled, one-shot
        mov     r0, 0
        mov     r0.b2, 0x10     // channel 2
        sbbo    r0, adc_base, STEPCONFIG1, 4
        mov     r0.b2, 0x18     // channel 3
        sbbo    r0, adc_base, STEPCONFIG2, 4
        mov     r0.b2, 0x20     // channel 4
        sbbo    r0, adc_base, STEPCONFIG3, 4
        mov     r0.b2, 0x28     // channel 5
        sbbo    r0, adc_base, STEPCONFIG4, 4
        mov     r0.b2, 0x30     // chennal 6
        sbbo    r0, adc_base, STEPCONFIG5, 4
       
        // Configure STEPDELAYx registers for channels 2-6
        mov     r0, 0
        sbbo    r0, adc_base, STEPDELAY1, 4
        sbbo    r0, adc_base, STEPDELAY2, 4
        sbbo    r0, adc_base, STEPDELAY3, 4
        sbbo    r0, adc_base, STEPDELAY4, 4
        sbbo    r0, adc_base, STEPDELAY5, 4
       
        // enable steps 1-5
        mov     r0, 0x0000003E
        sbbo    r0, adc_base, STEPENABLE, 4

        // Set STEPCONFIG registers protected, use tags
        mov     r0, 0x00000001
        sbbo    r0, adc_base, CTRL, 4

MAIN:
        // enable the ADC
        mov     r0, 0x00000003
        sbbo    r0, adc_base, CTRL, 4

        // wait for conversions complete
WAIT_FOR_FIFO0:
        lbbo    r0, adc_base, FIFO0COUNT, 4
        qbne    WAIT_FOR_FIFO0, r0, 5            // GETTING STUCK HERE
       
        // signal C code
        MOV     R31.b0, PRU0_R31_VEC_VALID | SIGNUM

        //qba     MAIN

        halt

Device Tree Overlay
/dts-v1/;
/plugin/;

/{
compatible = "ti,beaglebone", "ti,beaglebone-black";
part-number = "pruss_enable";
version = "00A0";

/* This overlay uses the following resources */
exclusive-use =
        "pru0",
        "pru1",
        "tscadc",
       
        "P9.29",    // GPIO3[15]    fet_off
        "P9.30",    // GPIO3[16]    fet_on
        "P9.31",    // GPIO3[14]    triac
       
        "P9.37",    // AIN2         TC_REF
        "P9.38",    // AIN3         TC
        "P9.33",    // AIN4         VL_SENSE
        "P9.36",    // AIN5         VH_SENSE
        "P9.35";    // AIN6         IH_SENSE

fragment@0 {
    target = <&am33xx_pinmux>;
    __overlay__ {
        pru_pins: pinmux_pru_pins {
            pinctrl-single,pins = <
                0x190 0x05  // P9_31 $PINS=100 pr1_pru0_pru_r30_0 mode 5 output pulldown
                0x194 0x05  // P9_29 $PINS=101 pr1_pru0_pru_r30_1 mode 5 output pulldown
                0x198 0x05  // P9_30 $PINS=102 pr1_pru0_pru_r30_2 mode 5 output pulldown
                >;
            };
        };
    };

// fragment@1 {
//     target = <&tsadc>;
//     __overlay__ {
//         status = "okay";
//         adc {
//             ti,adc-channels = <2 3 4 5 6>;
//             };
//         };
//     };
   
fragment@2 {         // Enable the PRUSS
    target = <&pruss>;
    __overlay__ {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&pru_pins>;
       
        dig_pru_pins {
            pin-names = "FET_OFF","FET_ON","TRIAC" ;
            gpios = <&gpio3 14 0        // P9.31
                     &gpio3 15 0        // P9.29
                     &gpio3 16 0>;      // P9.30
            };
        };
    };

};

William Hermans

unread,
Sep 7, 2016, 11:36:02 AM9/7/16
to beagl...@googlegroups.com
The only thing I can think of is that you're not enabling the ADC control register. Below is a code snippet from another post.
//Init ADC CTRL register
    MOV r2, 0x44E0D040     MOV r3, 0x00000005     SBBO r3, r2, 0, 4


From this post: https://groups.google.com/forum/#!msg/beagleboard/0a4tszlq2y0/SQ-Vwyr9A_AJ


The second post. I would not bother replying to that post, but who knows maybe someone would answer ? I doubt it though.

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/beagleboard/eb9582b5-ee7e-49bc-84f2-dde3e768180c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

TJF

unread,
Sep 8, 2016, 7:40:19 AM9/8/16
to BeagleBoard
Hi Phil!

After boot the ADC subsystem is disabled by default. You have to enable the CM_WKUP_ADC_TSC_CLKCTRL register in the CONTROL MODULE before you can use it:

mov  r0, 0x44E004BC
mov  r1
, 2
sbbo r1
, r0, 0, 4

Place this snippet after OCP enbled and before any ADC register access.

Regards
Message has been deleted

Phil

unread,
Sep 15, 2016, 5:26:24 PM9/15/16
to BeagleBoard

I gave this a try, with no success.  I have found that the PRU cannot access the ADC registers (1) - it is some bus access conflict between the PRU and Linux. I had seen reference to this somewhere a long while ago, and a solution to it, but have been unable to find the information again. Trying your code snippet, adding a pre-read of address 0x44E004BC before writing the value 2, causes the PRU to hang, which I interpret as the PRU waiting for a memory access that Linux isn't allowing it to have.

For further testing, I wrote some C fragments using mmap() to get ahold of the ADC register space, and I was able to get that to work. I don't believe that is a great way to go though.

(1) I know this because I tried reading the ADC REVISION register using the PRU and got zero as a result.

Phil

unread,
Sep 15, 2016, 5:27:55 PM9/15/16
to BeagleBoard


On Wednesday, September 7, 2016 at 10:36:02 AM UTC-5, William Hermans wrote:
The only thing I can think of is that you're not enabling the ADC control register. Below is a code snippet from another post.
//Init ADC CTRL register
    MOV r2, 0x44E0D040     MOV r3, 0x00000005     SBBO r3, r2, 0, 4


From this post: https://groups.google.com/forum/#!msg/beagleboard/0a4tszlq2y0/SQ-Vwyr9A_AJ


The second post. I would not bother replying to that post, but who knows maybe someone would answer ? I doubt it though.


Thanks for the suggestion, but that doesn't fix the problem. I can't even access the ADC registers from the PRU. See my response to TJF.

William Hermans

unread,
Sep 15, 2016, 5:36:19 PM9/15/16
to beagl...@googlegroups.com
The PRU hads to access the ADC through the L3_interconnect bus too . . . so the control register for the L3_interconnect must also be enabled.

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard+unsubscribe@googlegroups.com.

TJF

unread,
Sep 17, 2016, 3:11:16 AM9/17/16
to BeagleBoard

Am Donnerstag, 15. September 2016 23:26:24 UTC+2 schrieb Phil:
(1) I know this because I tried reading the ADC REVISION register using the PRU and got zero as a result.

It takes some cycles before the ADC is up and running. Check the ID register several times. In libpruio it works similar to this snippet (find original code in file pruio_adc.p):

  LDI  r2, 0b10             // load clock demand value
  MOV  r1
, 0x44E004BC       // load CM_WKUP_ADC_TSC_CLKCTRL address
  SBBO r2
, r1, 0, 1         // write clock register
  LDI  r5
, 0                // clear timeout counter
  MOV  r3
, 0x44E0D000       // load ADC address
AdcWait:
  LBBO r0
, r3,  0, 4        // load ADC REVISION
  QBNE
AdcCopy, r0, 0       // if ADC is up -> copy config
  ADD  r5
, r5, 1            // increase timeout counter
  QBGE
AdcWait, r5.b1, 16   // if no timeout -> wait

AdcCopy:

Regards
 

Phil

unread,
Sep 20, 2016, 10:20:26 AM9/20/16
to BeagleBoard
On Thursday, September 15, 2016 at 4:36:19 PM UTC-5, William Hermans wrote:
The PRU hads to access the ADC through the L3_interconnect bus too . . . so the control register for the L3_interconnect must also be enabled.

 
That makes sense... But the TRM seems not to disclose the location of this register. Some googling indicates that the L3/L4 interconnects are documented elsewhere.

OTOH, I am still wondering if my kernel isn't the root cause. It seems clear that other people have been able to get ADC control from the PRU, but with 3.8 or 4.1 kernels. I could revert to an older kernel version, but that feels like the wrong approach. I have also been looking at the IIO system, which may be a better approach as it would be more "standard" and portable than using the PRU. (But I *like* using the PRU - it's a nice hammer looking for a nail!)

William Hermans

unread,
Sep 20, 2016, 12:49:34 PM9/20/16
to beagl...@googlegroups.com
OTOH, I am still wondering if my kernel isn't the root cause. It seems clear that other people have been able to get ADC control from the PRU, but with 3.8 or 4.1 kernels. I could revert to an older kernel version, but that feels like the wrong approach. I have also been looking at the IIO system, which may be a better approach as it would be more "standard" and portable than using the PRU. (But I *like* using the PRU - it's a nice hammer looking for a nail!)

I
it could be that I'm thinking of OCP port, which you're already enabling.

So, yes . . . it could be your kernel, but I some how doubt that is the issue here. One thing you could do is enable the ADC's from userspace using cape manager, and set the ADC into continuous mode. Like so: http://processors.wiki.ti.com/index.php/Linux_Core_ADC_User%27s_Guide#Continuous_Mode. Then just read from FIF0DATA using the PRU's.

Then perhaps you can narrow things down some. This is how I initially used /dev/mem + mmap() for the ADC's then I found this project: https://github.com/ehayon/BeagleBone-GPIO, examined the code, and customized one of the header files for my own needs. Then just used that.

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard+unsubscribe@googlegroups.com.

Phil

unread,
Sep 21, 2016, 12:13:59 PM9/21/16
to BeagleBoard
Alright, I finally got this working, thanks to both William and TJF. In the end, my problem boils down to coding sloppiness on my part, as well as being constantly interrupted with other tasks. After spending a solid block of uninterrupted time doing some careful testing, the following code snippets are what I found to work. The only device tree overlay I needed is what I have below; no need to load the BB-ADC overlay.

PRU Code
//--------------------------------------------------------------------
// Enable and/or turn on hardware
// Code in this section must be ordered as listed for proper
// hardware startup.

        // Enable OCP
        lbco    r0, PRU_ICSS_CFG_base, PRU_CFG_SYSCFG, 4
        clr     r0, 4
        sbco    r0, PRU_ICSS_CFG_base, PRU_CFG_SYSCFG, 4

        // Enable ADC clock, wait for ADC module to start before proceeding
        mov     r0, CM_WKUP_base + CM_WKUP_ADC_TSC_CLKCTRL

        mov     r1, 2
        sbbo    r1, r0, 0, 4
        mov     r1, 0                           // clear timeout counter
AdcWait:
        LBBO    r0, adc_base, ADC_REVISION, 4   // load ADC REVISION
        QBNE    AdcUp, r0, 0                    // exit if ADC is running
        ADD     r1, r1, 1                       // increase timeout counter
        QBGE    AdcWait, r1.b1, 16              // if no timeout -> wait
AdcUp:

//--------------------------------------------------------------------


Device Tree Overlay
/dts-v1/;
/plugin/;

/{
compatible = "ti,beaglebone", "ti,beaglebone-black";
part-number = "pru_enable";

version = "00A0";

/* This overlay uses the following resources */
exclusive-use =
        "pru0",
       
        "P9.29",    // GPIO3[15]    fet_off
        "P9.30",    // GPIO3[16]    fet_on
        "P9.31";    // GPIO3[14]    triac

       
fragment@0 {
    target = <&am33xx_pinmux>;
    __overlay__ {
        pru_pins: pinmux_pru_pins {
            pinctrl-single,pins = <
                0x190 0x05  // P9_31 $PINS=100 pr1_pru0_pru_r30_0 mode 5 output pulldown
                0x194 0x05  // P9_29 $PINS=101 pr1_pru0_pru_r30_1 mode 5 output pulldown
                0x198 0x05  // P9_30 $PINS=102 pr1_pru0_pru_r30_2 mode 5 output pulldown
                >;
            };
        };
    };

William Hermans

unread,
Sep 21, 2016, 2:43:54 PM9/21/16
to beagl...@googlegroups.com
The only device tree overlay I needed is what I have below; no need to load the BB-ADC overlay.

Right, technically, you do not need an overlay, and you do not need any driver modules either. As directly twiddling the ADC module registers, you essentially are creating your own driver.

Now, with that said, it could be eventually you'll end up using the overlays, and setting up the ADC in userspace using the iio driver. But both ways have it's pluses and minuses. I'm sure you can make the determination for yourself.

--
For more options, visit http://beagleboard.org/discuss
---
You received this message because you are subscribed to the Google Groups "BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard+unsubscribe@googlegroups.com.

TJF

unread,
Sep 23, 2016, 8:57:06 AM9/23/16
to BeagleBoard
Hi Phil!

Your code is incomplete. The error handling is missing, for the case that the ADC subsystem doesn't come up. Your code just continues.


Am Mittwoch, 21. September 2016 18:13:59 UTC+2 schrieb Phil:
PRU Code
//--------------------------------------------------------------------
// Enable and/or turn on hardware
// Code in this section must be ordered as listed for proper
// hardware startup.

        // Enable OCP
        lbco    r0, PRU_ICSS_CFG_base, PRU_CFG_SYSCFG, 4
        clr     r0, 4
        sbco    r0, PRU_ICSS_CFG_base, PRU_CFG_SYSCFG, 4

        // Enable ADC clock, wait for ADC module to start before proceeding
        mov     r0, CM_WKUP_base + CM_WKUP_ADC_TSC_CLKCTRL
        mov     r1, 2
        sbbo    r1, r0, 0, 4
        mov     r1, 0                           // clear timeout counter
AdcWait:
        LBBO    r0, adc_base, ADC_REVISION, 4   // load ADC REVISION
        QBNE    AdcUp, r0, 0                    // exit if ADC is running
        ADD     r1, r1, 1                       // increase timeout counter
        QBGE    AdcWait, r1.b1, 16              // if no timeout -> wait
AdcUp:

//--------------------------------------------------------------------

But it needs something like

...

        QBGE    AdcWait, r1.b1, 16              // if no timeout -> wait


        SBCO    r1
, DRam, 0, 4                  // store timeout counter in DRam
        HALT                                    
// stop PRU, since ADC isn't ready
AdcUp:
...

Regards
 

Phil

unread,
Sep 23, 2016, 12:11:57 PM9/23/16
to BeagleBoard
Thanks, good catch! A question though: Under what circumstances would the ADC not start? I've been running the ADC now and have not had a startup fail.

TJF

unread,
Sep 23, 2016, 12:54:05 PM9/23/16
to BeagleBoard


Am Freitag, 23. September 2016 18:11:57 UTC+2 schrieb Phil:
A question though: Under what circumstances would the ADC not start?

Thunderstorm, lightning, cosmic rays, ... hardware damage. I don't know.

In any case it's better to stop with an error message, than configuring a subsystem that isn't running.

Regards

Akshay Gahlot

unread,
Oct 22, 2016, 7:57:08 AM10/22/16
to BeagleBoard

I am facing some issues in the project you suggested : https://github.com/ehayon/BeagleBone-GPIO/blob/master/src/gpio.c
 in the last function analogRead(PIN p) it always reads the last 12 bits  from the FIFO0DATA register
I am using this code and value is got from reading from this register is somewhat misleading (from misleading i mean they are not the same value that we read from "/sys/bus/iio/devices/iio:device0/in_voltage%d_raw" )
So plz suggest what could be wrong in it and also tell me this if  2 or more pins are configured to FIFO0 then how beaglebone differentiates between which pin's ADC data is present in it and how can use it correct the output readings.   
Reply all
Reply to author
Forward
0 new messages