Other uses of GPIO pins

Bela provides 16 digital I/O channels, which are sampled synchronously to the audio and can be easily written, read or tri-stated with a precision of about 22 microseconds. However, for some uses you may want to access some of the GPIO pins directly from your program, without having them sampled automatically by the Bela real-time core.

Table of contents

  1. Example use cases
  2. Finding an available pin
  3. Configuring the pin
    1. config-pin
    2. Configuring the pinmuxer via device tree overlay
  4. Accessing a pin

Example use cases

  • accessing pins to enable/reset external devices
  • accessing pins while outside the Bela audio thread (e.g.: from setup() or cleanup())
  • accurate, minimally invasive measurements of CPU time for specific blocks of code: simply toggle a pin before and after the section you want to measure and monitor it with an external oscilloscope
  • performing non-time-critical digital I/O (e.g.: reading buttons or driving LEDs) when you need more than the 16 I/Os provided by the Bela core.
  • manually driving the on-board LEDs for BelaMini (requires running with --disable-led).

Finding an available pin

Most pins of the BeagleBone or the PocketBeagle can perform different functions. The one function that is currently enabled depends on the pinmuxer (pin-multiplexer) controller. In order for a pin to work as a GPIO, you will have to make sure that the pinmuxer settings for the corresponding pin are in mode 7 (GPIO). Note that any of the pins normally used by Bela for digital I/O are already correctly configured as GPIO, but you will not be able to use them with the method below while Bela is running with digital enabled. However, you can disable the digital I/O in Bela (this is achieved by unticking the tickbox “Use digital” in the IDE, or equivalently, if you are using the command line, use the -G 0 flag) and safely access those pins for your own use while a Bela program is running (so you can skip the steps below and jump to “Using a GPIO pin” below). Whatever you do, make sure you do not use any of the other pins used by Bela. Here is a list of the pins on the BeagleBone and the pins used by Bela: (P8, P9). And these are for BelaMini.

Configuring the pin

The procedure to set the pinmuxer is simpler when using config-pin. config-pin is available out of the box on BelaMini/PocketBeagle, but in order to use it on the BeagleBone, you need to load the device tree overlay /lib/firmware/cape-universalh-00A0.dtbo and make sure you use the latest BB-BELA overlay from this repo. The procedure without config-pin is much more convoluted, but it is explained below for completeness. Use one of the methods below then jump to Accessing a pin below.

config-pin

If you are on PocketBeagle or on BeagleBone with cape-universalh enabled, you can use config-pin by running a command in a terminal, once logged onto Bela via ssh, or in the console at the bottom of the IDE. The command has to be of the type:

config-pin <pin-name> <mode>

Where <pin-name> is of the form Px.yy where x is the header number (1 or 2 on PocketBeagle/BelaMini and 8 or 9 on the BeagleBone/Bela/CTAG) and yy is the pin number and mode has to be one of the available modes for the pin. Pins that can be set to GPIO function will have at least the following modes available:

  • gpio for GPIO output
  • gpio_input for input without an internal pull resistor
  • gpio_pu for GPIO input with an internal weak pull-up resistor
  • gpio_pd for GPIO input with an internal weak pull-down resistor

Each pin will have more modes available. You can query the modes available for a pin with:

config-pin -l <pin-name>

To sum up with an example, the following command sets pin P8.14 to an input GPIO with internal pull-up resistor

config-pin P8.14 gpio_pu

Configuring the pinmuxer via device tree overlay

This method is more complex but more flexible as it allows to set a pin to any of the available functions, which may not be exposed through the config-pin interface, and also customise its I/O characteristics further. First, you need the pinmuxer address (ADDR) and the GPIO number. How to retrieve these varies depending on the board you have:

Bela/CTAG (BeagleBone)

Here is a list of the pins on the BeagleBone and the pins used by Bela. diagram (P8, P9). Pick a pin that is not in use by Bela and look up the GPIO number in the corresponding column and the pinmuxer address in the ADDR/OFFSET column. The ADDR value starts with 8 or 9 (remove the leading 0x when using this value below).

BelaMini (PocketBeagle)

The PocketBeagle does not have a nice and exhaustive list as the BeagleBone. You will first have to look at the BelaMini pinout and identify an available pin that can work as a GPIO. Once you identify your desired pin, search for the pin name(e.g.: P1.31) in this (hard to parse) page. From the column containing the pin name, look for the value of the “Control register” and “Mux mode 7” lines. We will call the “Control register” value “ADDR” from now on. The “Mux mode 7” line should look something like gpio x.yy. To find out the GPIO number(in order to access it with your code), you will need to get the value of the expression x * 32 + yy. For instance, P1.31 has ADDR = 9A0 and the Mux mode 7 value is gpio 3.18, therefore the gpio number is 3 * 32 + 18, that is 114 (this should match the number in the BelaMini pinout).

Pinmuxer setting

Once you have the ADDR value you can check what is the current pinmuxer setting for that pin (e.g.: for P9_11 on the BeagleBone, it is 0x870) and then running on the board:

grep 870 $PINS

this will return a line like the following

pin 28 (44e10870.0) 00000027 pinctrl-single

which denotes that the pin is in mode 27, which corresponds to GPIO, receiver enabled, pulldown (see notes at the bottom of the P8/P9 diagram files above), so you are good to go. If the pin is not set as a GPIO (and it is not used by Bela), then you can set it to GPIO by loading an appropriate device tree overlay. If you need to set the pin’s muxer in a device tree overlay, you will need to use the OFFSET value instead of ADDR. The OFFSET is obtained from the ADDR value by removing 0x800, that is changing the first digit to 0 if it was an 8 or to 1 if it was a 9.

Accessing a pin

From the command line

The Linux driver makes the GPIO chip available at /sys/class/gpio/. Here you can use commands such as:

echo $GPIO_NUMBER > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio$GPIO_NUMBER/direction
echo 1 > /sys/class/gpio/gpio$GPIO_NUMBER/value

There are plenty of tutorials available online for using the sysfs interface.

From C++

For proper, safe (and slow!!), access through the kernel you can use the kernel drivers sysfs interface, using the functions declared in https://github.com/BelaPlatform/Bela/blob/master/include/GPIOcontrol.h and implemented in https://github.com/BelaPlatform/Bela/blob/master/core/GPIOcontrol.cpp. You may want to use these in case you have multiple processes accessing the same GPIO pins.

For accurate, fast, real-time safe access, you will instead want to use the methods of the Gpio class. Just #include <Gpio.h> and go for it. This class uses memory-mapped Gpios for real-time performance, without going through the Linux kernel. For en examples of its use, check out Digital/synchronous-gpio.