BeagleBone Black GPIOs

The BeagleBone Black is a great development platform. In contrast to the Raspberry PI it’s a solid hardware design without any major bugs. For a reasonable price you get some really nice hardware specs: 512MiB RAM, 1GHz AM335x ARM with an FP accelerator, 4GiB on-board flash AND and external SDcard slot, Ethernet, USB-Host, HDMI, 2 on-board programmable microprocessors (the PRUs) and last but definitely not least: 69 digital GPIO pins and 7 analog inputs to an 12bit ADC.

The best part, you get to control all of that Linux SysFS filesystem. Toggle a pin? Just write to the right file. Read an onewire temperature sensors value? Just read from a file. You may notice a pattern. That ease of use and range of possibilities makes it perfect for a multitude of projects.

No more interfacing with an ATMega or PIC microcontroller via SPI or USB emulated serial. No trade-off what component might best do what, just handle everything in one place. No surprise, it was the obvious choice for our current projects: KiloBaser, our Rapid DNA Prototyper as well as our briefcase sized Bioreactor, aptly called the BriefcaseReactor.

In fact, the BeagleBone people make it really easy on their users, by letting you control everything via the devices webserver using their JavaScript extension „BoneScript“.

This article however is for the people who want to go deeper and use the Bone natively, for potentialy much more precise control over what your system does. So if you’re fine with BoneScript, then this Blog is NOT FOR YOU.

Most everything that BoneScript does, we can do better by directly manipulating SysFS files, thus not limiting us to JavaScript but enabling us to use everything from shellscripts, to Python, C++ or even Go.

Find out which GPIO corresponds to which pin.

The AM335x has four built-in GPIO controllers, named gpio0[], gpio1[], gpio2[] and gpio3[]. For each controller, there is one page of memory which controls each gpio controller. Each controller is responsible for 32 GPIOs. Each 32bit word has a specific function. Like pin configuration, controlling or setting a specific pin-state. Each bit in each of these words controls a GPIO pin. Choose function by choosing the word, choose GPIO by choosing the bit. This is basically what the Linux Kernel does for you, when you use the SysFS interface. This is also how the different GPIOs are numbered in Linux. Linux gpio/gpi0/ is gpio0[0] (bit 0 on gpio chip 0), Linux gpio/gpio1/ is bit1 on chip 0 and so on, till we get to Linux gpio/gpio32/ which is bit0 on chip1 (gpio1[0]).

So, when you now take a look at Table 12 and 13 of the BeagleBoneBlack SRM and read that e.g. Pin P8_3 on the expansion header is routed to gpio1[6] inside the chip, it’s easy to calculate that in Linux you see this as gpio/gpio38/ since 38=1*32+6.

Or you could check these nice images from the bone101 page

(Images embedded from the beaglebone.org website, since errors have been known to creep in and we want to display the most up to date ones)

Control a pin via SysFS

Have a look at directory /sys/class/gpio/. Populate the directory by writing the number of the Linux GPIO you want to control to /sys/class/gpio/export. Each gpio has a directory you can use to control the pin.

BeagleBone Terminal

				
					root@beaglebone:~#  cd /sys/class/gpio/

root@beaglebone:/sys/class/gpio#  ls
export  gpio60  gpiochip0  gpiochip32  gpiochip64  gpiochip96  unexport

root@beaglebone:/sys/class/gpio#  echo 60 > export

root@beaglebone:/sys/class/gpio#  ls
export  gpio60  gpiochip0  gpiochip32  gpiochip64  gpiochip96  unexport

root@beaglebone:/sys/class/gpio#   cd gpio60

root@beaglebone:/sys/class/gpio/gpio60#  ls
active_low  direction  edge  power  subsystem  uevent  value

root@beaglebone:/sys/class/gpio/gpio60#  cat value
0

root@beaglebone:/sys/class/gpio/gpio60#  echo out > direction

root@beaglebone:/sys/class/gpio/gpio60#  echo 1 > value

root@beaglebone:/sys/class/gpio/gpio60#  echo 0 > value
				
			

As a simple test you can connect a jumper wire from pin P9_4 (3.3V) to P9_12 (gpio/gpio60/, gpio1[28]). Once you do, the value should display 1. If you pull it out, or if you connect it to GND, it returns to 0.

GPIO SysFS directory

File

Function

direction

Decides whether this is an input or output pin. Writing in or out to this file, changes the behaviour of the value-file accordingly.

value

if direction is in, reading this file tells you if the external signal connected to the pin is 1 (3.3V) or 0 (0V)
if direction is out, writing to this file sets the voltage we output on the pin to either 3.3V (1) or 0V (0)

active_low

If 1, the meaning of 1 and 0 in the value-file is reversed: 0 (3.3V) and 1 (0.V)


Using Device-Tree Overlays


Once you have experimented a bit, you will find that for some GPIOs, changing their value in SysFS does NOT change the corresponding pins voltage output.

Most pins on the BeagleBone are multi-purpose. They can be routed (muxed) to different modules inside the AM335x chip. If you’ve already looked at Table 12 and 13 of the BeagleBoneBlack SRM you’ve seen that each pin can have one of 8 different functions, only only of those is GPIO: Mode 0x7.
Others modes include routing to the PWM, SPI, UART, HDMI, PRUSS sub-modules. So, unless you configure a pin to MUX MODE 0x7, changing anything in /sys/class/gpio won’t have any effect!

Take a look at SysFS to see which pins are configured for which MUX MODE.

BeagleBone Terminal

				
					root@beaglebone:~# less /sys/kernel/debug/pinctrl/44e10800.pinmux/pins
				
			

Unfortunately this does not tell you much on its own. You have to look at Table 9-7 CONTROL_MODULE REGISTERS in the AM335x Reference Manual to make sense of that. See which memory address configures which pin. Fortunately most of the time we don’t need to check which MUX MODE a pin has, we just set the mode the way we want it and that’s that.

Since Linux Kernel 3.8, we can use Device-Tree Overlays for that.
To make it really easy I’ve cobbled together a Device-Tree Overlay Generator for the BeagleBone. First however, let’s have a look at some of the Device Tree Overlays which the BeagleBone Black shipped with: PWM-Overlays.

Find any usable overlay’s name by it’s filename in /lib/firmware minus the version number and file-extension.
Enable it by echoing the name into the cape-managers slots-file.
Disable it by subtracting its slot number from the slots-file, but remember that removing some overlays like ADC or UART might crash the Bone.

In this example we will activate the PWM sub-system (am33xx_pwm) as well as the overlay, routing pin P8_13 to said chip sub-system (bone_pwm_P8_13).

BeagleBone Terminal

				
					root@beaglebone:~# cat /sys/devices/bone_capemgr.?/slots
 0: 54:PF---
 1: 55:PF---
 2: 56:PF---
 3: 57:PF---
 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI

root@beaglebone:~# ls /lib/firmware/bone_pwm_*
/lib/firmware/bone_pwm_P8_13-00A0.dtbo  /lib/firmware/bone_pwm_P8_45-00A0.dtbo  /lib/firmware/bone_pwm_P9_21-00A0.dtbo
/lib/firmware/bone_pwm_P8_19-00A0.dtbo  /lib/firmware/bone_pwm_P8_46-00A0.dtbo  /lib/firmware/bone_pwm_P9_22-00A0.dtbo
/lib/firmware/bone_pwm_P8_34-00A0.dtbo  /lib/firmware/bone_pwm_P9_14-00A0.dtbo  /lib/firmware/bone_pwm_P9_28-00A0.dtbo
/lib/firmware/bone_pwm_P8_36-00A0.dtbo  /lib/firmware/bone_pwm_P9_16-00A0.dtbo  /lib/firmware/bone_pwm_P9_29-00A0.dtbo

root@beaglebone:~# echo am33xx_pwm > /sys/devices/bone_capemgr.?/slots
root@beaglebone:~# echo bone_pwm_P8_13 > /sys/devices/bone_capemgr.?/slots

root@beaglebone:~# cat /sys/devices/bone_capemgr.?/slots
 0: 54:PF---
 1: 55:PF---
 2: 56:PF---
 3: 57:PF---
 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
 6: ff:P-O-L Override Board Name,00A0,Override Manuf,am33xx_pwm
 7: ff:P-O-L Override Board Name,00A0,Override Manuf,bone_pwm_P8_13

root@beaglebone:~# echo -7 > /sys/devices/bone_capemgr.?/slots

root@beaglebone:~# cat /sys/devices/bone_capemgr.?/slots
 0: 54:PF---
 1: 55:PF---
 2: 56:PF---
 3: 57:PF---
 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
 6: ff:P-O-L Override Board Name,00A0,Override Manuf,am33xx_pwm

root@beaglebone:~# dmesg | tail -n 12
[  100.046933] bone-capemgr bone_capemgr.9: part_number 'bone_pwm_P8_13', version 'N/A'
[  100.047112] bone-capemgr bone_capemgr.9: slot #7: generic override
[  100.047160] bone-capemgr bone_capemgr.9: bone: Using override eeprom data at slot 7
[  100.047208] bone-capemgr bone_capemgr.9: slot #7: 'Override Board Name,00A0,Override Manuf,bone_pwm_P8_13'
[  100.047457] bone-capemgr bone_capemgr.9: slot #7: Requesting part number/version based 'bone_pwm_P8_13-00A0.dtbo
[  100.047505] bone-capemgr bone_capemgr.9: slot #7: Requesting firmware 'bone_pwm_P8_13-00A0.dtbo' for board-name 'Override Board Name', version '00A0'
[  100.047557] bone-capemgr bone_capemgr.9: slot #7: dtbo 'bone_pwm_P8_13-00A0.dtbo' loaded; converting to live tree
[  100.055224] bone-capemgr bone_capemgr.9: slot #7: #2 overlays
[  100.064402] bone-capemgr bone_capemgr.9: slot #7: Applied #2 overlays.
[  100.128001] pwm_test pwm_test_P8_13.15: unable to request PWM
[  100.150420] platform pwm_test_P8_13.15: Driver pwm_test requests probe deferral
[  112.592029] bone-capemgr bone_capemgr.9: Removed slot #7

root@beaglebone:~#
				
			

You can make that semi-permanent by pre-pending lines like the following to /etc/rc.local. Just replace $DTBONAME with the identifier of your overlay:

echo $DTBONAME > /sys/devices/bone_capemgr.?/slots
Even better, you can edit uEnv.txt on the BEAGLE_BONE fat boot partition. Tell the Linux kernel directly to load (or not load) some overlays. uEnv.txt has plenty of examples but basically it comes down to this:

cape_disable=capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN
cape_enable=capemgr.enable_partno=BB-UART5,am33xx_pwm,bone_pwm_P8_46
Using cape_enable=capemgr.enable_partno= you can tell the kernel to load any overlay found in /lib/firmware While cape_disable=capemgr.disable_partno= keeps the kernel from loading any part that was originally specified in dtbs/am335x-boneblack.dtb Some Overlays or functions you cannot unload once they are in the Device-Tree. You have to keep them from loading, in precisely that manner.

If you want a list of the parts not in /lib/firmware, you need to decompile am335x-boneblack.dtb and have a look inside:

BeagleBone Terminal

				
					
root@beaglebone:~# dtc -O dts -o ~/am335x-boneblack.dts -I dtb /boot/uboot/dtbs/am335x-boneblack.dtb
root@beaglebone:~# grep part-number ~/am335x-boneblack.dts | sed 's/^\s\+//'
part-number = "BB-BONE-GEIGER";
part-number = "BB-BONE-NIXIE";
part-number = "BB-BONE-TFT-01";
part-number = "BB-BONE-RTC-01";
part-number = "BB-BONE-HEXY-01";
part-number = "BB-BONE-MRF24J40";
part-number = "BB-BONE-RS232-01";
part-number = "BB-BONE-GPS-01";
part-number = "BB-BONE-EMMC-2G";
part-number = "BB-BONELT-HDMI";
part-number = "BB-BONELT-HDMIN";
part-number = "BB-BONE-DVID-01";
part-number = "BB-BONE-EMMC-2G";
part-number = "BB-BONE-GEIGER";
part-number = "BB-BONE-LCD3-01";
part-number = "BB-BONE-WTHR-01";
part-number = "BB-BONELT-HDMI";
part-number = "BB-BONE-NIXIE";
part-number = "BB-BONE-TFT-01";
part-number = "BB-BONE-RTC-01";
part-number = "BB-BONE-HEXY-01";
part-number = "BB-BONE-MRF24J40";
part-number = "BB-BONE-EXPTEST";
part-number = "BB-BONE-RS232-01";
part-number = "BB-BONE-GPS-01";
part-number = "BB-BONELT-HDMIN";
part-number = "2191";
part-number = "BB-BONE-LOGIBONE";
				
			

The command dtc used here is the Device-Tree Compiler. It’s pre-installed on the BeagleBone and allows you to compile the text-version of a Device-Tree to the binary format used by the kernel and vice versa. Read the man-page for more information.

For the bare-Bone user, the most interesting ones are the pre-enabled BB-BONELT-HDMI, BB-BONELT-HDMIN and BB-BONE-EMMC-2G. Disabling these in uEnv.txt can free up a lot of pins for GPIO use. Take care however, if you plan on disabling BB-BONE-EMMC-2G, which controls the pins that connect the internal MMC flash (mmc1). While it’s a good idea to use an external microSD card (mmc0), you might still want to access the internal mmc1 for storage purposes.

Furthermore, no matter the MUX setting, the pins P8_3 to P8_6 and P8_20 to P8_25 will always be physically connected to the internal flash! So if you plan on preserving the data on there, steer clear of those pins.

If you decide you really need these pins, put the MMC1 into reset before changing the MUX setting or connecting anything to those pins.

Excerpt from BB-SRM:

BEFORE the SW reinitializes the pins, it MUST put the eMMC in reset. This is done by taking eMMC_RSTn (gpio1[20]) LOW after the eMMC has been put into a mode to enable the reset line.

Controlling a PWM pin


Each PWM capable pin, once activated via the slots file, has, no surprise, also its own directory in SysFS. You can find them in /sys/devices/ocp.?/ where ? is a number that changes with each driver version.

In the following example we see that PWM on P8_13 is currently running and set to a period of 500 000ns (2kHz) but the duty is 0, so the pin is constantly at 3.3V (polarity==1). Since we want a duty of 50% we echo half the value of the period-file into the duty-file. The result is a PWM signal that periodically alternates between 250µs at 3.3V and 250µs at 0V.

BeagleBone Terminal

				
					
root@beaglebone:~# cd /sys/devices/ocp.?/pwm_test_P8_13.??/
root@beaglebone:/sys/devices/ocp.3/pwm_test_P8_13.11# ls
driver  duty  modalias  period  polarity  power  run  subsystem  uevent
root@beaglebone:/sys/devices/ocp.3/pwm_test_P8_13.11# cat duty
0
root@beaglebone:/sys/devices/ocp.3/pwm_test_P8_13.11# cat period
500000
root@beaglebone:/sys/devices/ocp.3/pwm_test_P8_13.11# cat run
1
root@beaglebone:/sys/devices/ocp.3/pwm_test_P8_13.11# cat polarity
1
root@beaglebone:/sys/devices/ocp.3/pwm_test_P8_13.11# PERIOD=$(root@beaglebone:/sys/devices/ocp.3/pwm_test_P8_13.11# echo $((PERIOD/2)) > duty
root@beaglebone:/sys/devices/ocp.3/pwm_test_P8_13.11# cat duty
250000
				
			
PWM SysFS directory

File

Function


period

Period of the PWM signal in ns. Must be larger or equal to duty.


duty

Duty of the PWM signal in ns, i.e. what part of the period should the signal stay at the value set in polarity


run

Whether the PWM signal is enabled


polarity

On 1 the signal starts at 3.3V for the duration of duty. On 0 the signal starts at 0V and raises to 3.3V after the duration specified in duty


Creating GPIO Device-Tree Overlays

Read on

Share this article: