Outline

1. Accessing More I/O

So far the examples have shown how to access the GPIO pins on the BeagleBone Black’s P9 header and through the __R30 register. Below shows how more GPIO pins can be accessed.

The following are resources used in this chapter.

1.1. Editing /boot/uEnv.txt to Access the P8 Header on the Black

Problem

When I try to configure some pins on the P8 header of the Black I get an error.

config-pin
bone$ config-pin P8_28 pruout
ERROR: open() for /sys/devices/platform/ocp/ocp:P8_28_pinmux/state failed, No such file or directory

Solution

On the images for the BeagleBone Black, the HDMI display driver is enabled by default and uses many of the P8 pins. If you are not using HDMI video (or the HDI audio, or even the eMMC) you can disable it by editing /boot/uEnv.txt

Open /boot/uEnv.txt and scroll down aways until you see:

/boot/uEnv.txt
###Disable auto loading of virtual capes (emmc/video/wireless/adc)
#disable_uboot_overlay_emmc=1
disable_uboot_overlay_video=1
#disable_uboot_overlay_audio=1

Uncomment the lines that correspond to the devices you want to disable and free up their pins.

Tip

P8 Header Table shows what pins are allocated for what.

Save the file and reboot. You now have access to the P8 pins.

1.2. Accessing gpio

Problem

I’ve used up all the GPIO in __R30, where can I get more?

Solution

So far we have focused on using PRU 0. Mapping bit positions to pin names PRU shows that PRU 0 can access ten GPIO pins on the BeagleBone Black. If you use PRU 1 you can get to an additional 14 pins (if they aren’t in use for other things.)

What if you need even more GPIO pins? You can access any GPIO pin by going through the Open-Core Protocol (OCP) port.

PRU Integration

PRU Integration

The figure above shows we’ve been using the Enhanced GPIO interface when using __R30, but it also shows you can use the OCP. You get access to many more GPIO pins, but it’s a slower access.

gpio.pru0.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// This code accesses GPIO without using R30 and R31
#include <stdint.h>
#include <pru_cfg.h>
#include "resource_table_empty.h"
#include "prugpio.h"

#define P9_11   (0x1<<30)           // Bit position tied to P9_11 on Black
#define P2_05   (0x1<<30)           // Bit position tied to P2_05 on Pocket

volatile register uint32_t __R30;
volatile register uint32_t __R31;

void main(void)
{
    uint32_t *gpio0 = (uint32_t *)GPIO0;

    while(1) {
        gpio0[GPIO_SETDATAOUT]   = P9_11;
        __delay_cycles(100000000);
        gpio0[GPIO_CLEARDATAOUT] = P9_11;
        __delay_cycles(100000000);
    }
}

This code will toggle P9_11 on and off. Here’s the setup file.

setup.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash

export TARGET=gpio.pru0
echo TARGET=$TARGET

# Configure the PRU pins based on which Beagle is running
machine=$(awk '{print $NF}' /proc/device-tree/model)
echo -n $machine
if [ $machine = "Black" ]; then
    echo " Found"
    pins="P9_11"
elif [ $machine = "Blue" ]; then
    echo " Found"
    pins=""
elif [ $machine = "PocketBeagle" ]; then
    echo " Found"
    pins="P2_05"
else
    echo " Not Found"
    pins=""
fi

for pin in $pins
do
    echo $pin
    config-pin $pin gpio
    config-pin -q $pin
done

Notice in the code config-pin set P9_11 to gpio, not pruout. This is because are are using the OCP interface to the pin, not the usual PRU interface.

Set your exports and make.

bone$ source setup.sh
TARGET=gpio.pru0
...
bone$ make
/var/lib/cloud9/common/Makefile:29: MODEL=TI_AM335x_BeagleBone_Black,TARGET=gpio.pru0
-    Stopping PRU 0
-   copying firmware file /tmp/cloud9-examples/gpio.pru0.out to /lib/firmware/am335x-pru0-fw
write_init_pins.sh
-    Starting PRU 0
MODEL   = TI_AM335x_BeagleBone_Black
PROC    = pru
PRUN    = 0
PRU_DIR = /sys/class/remoteproc/remoteproc1

Discussion

When you run the code you see P9_11 toggling on and off. Let’s go through the code line-by-line to see what’s happening.

Table 1. gpio.pru0.c line-by-line
Line Explanation

2-5

Standard includes

5

The AM335x has four 32-bit GPIO ports. Lines 55-58 of prugpio.h define the addresses for each of the ports. You can find these in Table 2-2 page 180 of the AM335x Technical Reference Manual. Look up P9_11 in the P9 Header Table. Under the Mode7 column you see gpio0[30]. This means P9_11 is bit 30 on GPIO port 0. Therefore we will use GPIO0 in this code.

You can also run gpioinfo and look for P9_11.

5

Line 103 of prugpio.h defines the address offset from GIO0 that will allow us to clear any (or all) bits in GPIO port 0. Other architectures require you to read a port, then change some bit, then write it out again, three steps. Here we can do the same by writing to one location, just one step.

5

Line 104 of prugpio.h is like above, but for setting bits.

5

Using this offset of line 105 of prugpio.h lets us just read the bits without changing them.

7,8

This shifts 0x1 to the 30th bit position, which is the one corresponding to P9_11.

15

Here we initialize gpio0 to point to the start of GPIO port 0’s control registers.

18

gpio0[GPIO_SETDATAOUT] refers to the SETDATAOUT register of port 0. Writing to this register turns on the bits where 1’s are written, but leaves alone the bits where 0’s are.

19

Wait 100,000,000 cycles, which is 0.5 seconds.

20

This is line 18, but the output bit is set to 0 where 1’s are written.

How fast can it go?

This approach to GPIO goes through the slower OCP interface. If you set __delay_cycles(0) you can see how fast it is.

gpio.pru0.c with __delay_cycles(0)

gpio.pru0.c with __delay_cycles(0)

The period is 80ns which is 12.MHz. That’s about one forth the speed of the __R30 method, but still not bad.

If you are using an oscilloscope, look closely and you’ll see the following.

PWM with jitter

PWM with jitter

The PRU is still as solid as before in it’s timing, but now it’s going through the OCP interface. This interface is shared with other parts of the system, therefore the sometimes the PRU must wait for the other parts to finish. When this happens the pulse width is a bit longer than usual thus adding jitter to the output.

For many applications a few nanoseconds of jitter is unimportant and this GPIO interface can be used. If your application needs better timing, use the __R30 interface.

1.3. Configuring for UIO Instead of RemoteProc

Problem

You have some legacy PRU code that uses UIO instead of remoteproc and you want to switch to UIO.

Solution

Edit /boot/uEnt.txt and search for uio. I find

###pru_uio (4.4.x-ti, 4.9.x-ti, 4.14.x-ti & mainline/bone kernel)
uboot_overlay_pru=/lib/firmware/AM335X-PRU-UIO-00A0.dtbo

Uncomment the uboot line. Look for other lines with uboot_overlay_pru= and be sure they are commented out.

Reboot your Bone.

bone$ sudo reboot

Check that UIO is running.

bone$ lsmod | grep uio
uio_pruss              16384  0
uio_pdrv_genirq        16384  0
uio                    20480  2 uio_pruss,uio_pdrv_genirq

You are now ready to run the legacy PRU code.

1.4. Converting pasm Assembly Code to clpru

Problem

You have some legacy assembly code written in pasm and it won’t assemble with clpru.

Solution

Generally there is a simple mapping from pasm to clpru. pasm vs. clpru notes what needs to be changed. I have a less complete version on my eLinux.org site.

Discussion

The clpru assembly can be found in PRU Assembly Language Tools.