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
Example use cases
- accessing pins to enable/reset external devices
- accessing pins while outside the Bela audio thread (e.g.: from
setup()
orcleanup()
) - 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 outputgpio_input
for input without an internal pull resistorgpio_pu
for GPIO input with an internal weak pull-up resistorgpio_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.