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 pass:[__]R30 register. Below shows how more GPIO pins can be accessed.

The following are resources used in this chapter.

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.

1bone$ *config-pin P8_28 pruout*
2ERROR: 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 always until you see:

Listing 129 /boot/uEnv.txt#
1###Disable auto loading of virtual capes (emmc/video/wireless/adc)
2#disable_uboot_overlay_emmc=1
3disable_uboot_overlay_video=1
4#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.

Accessing gpio#

Problem#

I’ve used up all the GPIO in pass:[__]R30, where can I get more?

Solution#

So far we have focused on using PRU 0. Mapping bit positions to pin names 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

Fig. 766 PRU Integration#

The figure above shows we’ve been using the _Enhanced GPIO interface when using pass:[__]R30, but it also shows you can use the OCP. You get access to many more GPIO pins, but it’s a slower access.

Listing 130 gpio.pru0.c#
 1// This code accesses GPIO without using R30 and R31
 2#include <stdint.h>
 3#include <pru_cfg.h>
 4#include "resource_table_empty.h"
 5#include "prugpio.h"
 6
 7#define P9_11	(0x1<<30)			// Bit position tied to P9_11 on Black
 8#define P2_05	(0x1<<30)			// Bit position tied to P2_05 on Pocket
 9
10volatile register uint32_t __R30;
11volatile register uint32_t __R31;
12
13void main(void)
14{
15	uint32_t *gpio0 = (uint32_t *)GPIO0;
16	
17	while(1) {
18		gpio0[GPIO_SETDATAOUT]   = P9_11;
19		__delay_cycles(100000000);
20		gpio0[GPIO_CLEARDATAOUT] = P9_11;
21		__delay_cycles(100000000);
22	}
23}

gpio.pru0.c

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

Listing 131 setup.sh#
 1#!/bin/bash
 2
 3export TARGET=gpio.pru0
 4echo TARGET=$TARGET
 5
 6# Configure the PRU pins based on which Beagle is running
 7machine=$(awk '{print $NF}' /proc/device-tree/model)
 8echo -n $machine
 9if [ $machine = "Black" ]; then
10    echo " Found"
11    pins="P9_11"
12elif [ $machine = "Blue" ]; then
13    echo " Found"
14    pins=""
15elif [ $machine = "PocketBeagle" ]; then
16    echo " Found"
17    pins="P2_05"
18else
19    echo " Not Found"
20    pins=""
21fi
22
23for pin in $pins
24do
25    echo $pin
26    config-pin $pin gpio
27    config-pin -q $pin
28done

setup.sh

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

Set your exports and make.

 1bone$ *source setup.sh*
 2TARGET=gpio.pru0
 3...
 4bone$ *make*
 5/opt/source/pru-cookbook-code/common/Makefile:29: MODEL=TI_AM335x_BeagleBone_Black,TARGET=gpio.pru0
 6-    Stopping PRU 0
 7-   copying firmware file /tmp/vsx-examples/gpio.pru0.out to /lib/firmware/am335x-pru0-fw
 8write_init_pins.sh
 9-    Starting PRU 0
10MODEL   = TI_AM335x_BeagleBone_Black
11PROC    = pru
12PRUN    = 0
13PRU_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 167 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 TRM 180. Look up P9_11 in the P9 header. 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 30^th^ 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 pass:[__]delay_cycles(0) you can see how fast it is.

gpio.pru0.c with pass:[__]delay_cycles(0)

Fig. 767 gpio.pru0.c with pass:[__]delay_cycles(0)#

The period is 80ns which is 12.MHz. That’s about one forth the speed of the pass:[__]R30 method, but still not bad.

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

PWM with jitter

Fig. 768 PWM with jitter#

The PRU is still as solid as before in its 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 pass:[__]R30 interface.

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.

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.