Hardware Interfacing on the BeagleBone

Photo of the BeagleBone ARM Cortex based development board.

The BeagleBone is an ARM applications processing platform that runs Angstrom Linux and provides a lot of 3.3V I/O connections, for more background see my unboxing and getting started article. In the unboxing review I ran a bit of bundled java-script that flashed a light on and off, but there's much more to using I/O on this powerful processor. Like with most embeded controllers these days half the effort is in getting the right function selected and enabling the pin drivers. There are several steps and modes of operation which I'll go through separately. I'll discuss command line tools and python control here, but there is a pre-written javascript library called bonescript.js that comes pre-loaded on the board that provides an Arduino-like syntax and runs on the node.js javascript interpreter, I'm not going to discuss that here as it is under heavy development and has only very rudimentary features at the moment. I'd recommend having a look through the code if you have a BeagleBone, it's accessible from Cloud9 IDE on port 3000 via a web browser.

Pin mux

The BeagleBone has far more peripherals than pins, despite being a BGA package. To get around this it assigns different functionality to each pin based on software selections like most other modern microcontrollers. The BeagleBone actually has a more flexible arrangement than previous BeagleBoard variants, allowing run-time customisation of most of the I/O pins from a set of files under the /sys/ folder. If you take a look at the BeagleBone System Reference Manual starting on page 48 is a description of the I/O pins. There are up to 66 3.3V GPIO pins available with several I²C, SPI and UART interfaces as well as timers and PWM etc available as options instead of GPIO on certain pins. There are a group of ADC pins as well but be careful the ADCs are only 1.8V tolerant so watch how many volts you put across them! The initial connector pinout mentions the most likely pin mode for each pin, but on the following pages each pin has details of the up to 8 functions available on each pin. The pin name as far as the kernel is concerned is always the MODE0 function, which, frustratingly, isn't always the "Signal Name" given in the connector pinout. The following examples are pins used in the BigTrak project so appologies if they seem to be stranded in the middle of a connector.

Reading pin mux setting

Shell

cat /sys/kernel/debug/omap_mux/uart1_txd
name: uart1_txd.(null) (0x44e10984/0x984 = 0x0037), b NA, t NA
mode: OMAP_PIN_OUTPUT | OMAP_MUX_MODE7
signals: uart1_txd | mmc2_sdwp | d_can1_rx | NA | NA | NA | NA | NA

The output shown tells you what pin mode the pin is in, also should display all the available pin modes. In some cases the mode names seem to be missing at the moment but I've found that the functionality is really there, so you can set the mode based on the Manual and use the pin. For all pins MODE7 is the GPIO mode.

Python

fr = file("/sys/kernel/debug/omap_mux/uart1_rxd", "r")
print fr.read()
name: uart1_rxd.(null) (0x44e10980/0x980 = 0x0037), b NA, t NA
mode: OMAP_PIN_OUTPUT | OMAP_MUX_MODE7
signals: uart1_rxd | mmc1_sdwp | d_can1_tx | NA | NA | NA | NA | NA

fr.close()

Setting pin mux

Setting pin mux is done with an ascii string representing a hexadecimal value which looks somewhat like an I/O register on a microcontroller.

Bit 5 4 3 2 1 0
Set (1) Input Pull Up Pull Enabled Mode
Clear (0) Output Pull Down Pull disabled

Example UART1 TX:
For a UART TX we want:

  • Output
  • Don't care if pull up or down
  • Pull resistor disabled
  • Mode 0

0 0 0 000 = 0x00
So we just need to write zero to the file:
Shell

echo 0 > /sys/kernel/debug/omap_mux/uart1_txd

Python

fw = file("/sys/kernel/debug/omap_mux/uart1_txd", "w")
fw.write("%X" % (0))
fw.close()

Example UART1 RX:
For a UART RX pin we want:

  • Input
  • Don't care about pull resistor
  • Pull resistor disabled
  • Mode 0

1 0 0 000 = 0x20
So we need to write the input control bit and mode zero to the file:
Shell

echo 20 > /sys/kernel/debug/omap_mux/uart1_rxd

Python

fw = file("/sys/kernel/debug/omap_mux/uart1_rxd", "w")
fw.write("%X" % ((1 << 5) | 0)
fw.close()

You don't need to worry about setting the input/output mode bit if you're using a GPIO pin, they have their own direction control syntax.

Serial ports

Serial ports have been around for decades and so have carved out a bit of a special place in the device structure of UNIX filesystems. Typically you'd probably expect hardware serial ports to appear as /dev/ttyS0, /dev/ttyS1 etc. But you'll notice there aren't six of these in the devices register on the BeagleBone. For some reason specific to the OMAP drivers the hardware serial ports can be found as /dev/ttyO0 to /dev/ttyO5 (that's a letter O for OMAP, not a zero before the number!). These map to the hardware UART0 to UART5. Note that UART0 is not available on the expansion headers, it is hard wired to the FTDI chip on the board and is used by getty to provide a login terminal from boot, so you shouldn't try and use it for anything else unless you're sure of what you're doing.

GPIO Pins

There are a total of 66 GPIO pins available on the BeagleBone making it a very capable controller for a lot of things. Using them without writing a Kernel module relies on toggling individual pins one at a time via control files though, so don't plan on driving big wide parallel busses or anything without significant effort!

GPIO pin naming

GPIO pins on the OMAP processor are known by their "GPIO chip number" and then pin number. In fact the on-board pins are all controlled from the host chip, but the Linux kernel treats all GPIO as pins on external I/O chips (think along the lines of the old 8255 PIO modules). In keeping with the 32 bit nature of this processor the GPIO "chips" each control up to 32 individual pins and there are 4 controller chips. To access a specific pin you need to know the pin's GPIO number, this is made up of the chip's base I/O number (i.e. the number assigned to pin 0 of the chip) and the pin number itself. Since there are 32 I/O per chip, and 4 chips starting from zero the base number is the chip number times 32 i.e. GPIO0 base is 0, GPIO1 base is 32, GPIO2 base is 64 and GPIO3 base is 96. So to find the actual GPIO pin number simply take the chip number, multiply by 32 and add the pin.

GPIO3_17 = 3 * 32 + 17 = 113

Exporting a GPIO pin

Before you can use a GPIO pin you need to make a file handle for it in the /sys/ filesystem. This is done by writing the pin number (as a decimal see GPIO pin naming above) to the special export file in the gpio folder:
Shell

echo 113 > /sys/class/gpio/export

Python

fw = file("/sys/class/gpio/export", "w")
fw.write("%d" % (113))
fw.close()

This will create a new folder for your GPIO pin (e.g. /sys/class/gpio/gpio113/) which contains a number of files and folders relating to the control of that pin. The two you should be interested in are the "direction" file and "value" file. These work as you'd expect, reading or writing direction will tell or set the direction using the words "in" and "out", value is set by writing a character 1 or cleared by writing a character 0. Reading from value will return the state as a character ('0' or '1') followed by a newline.
Shell

# set gpio pin to output
echo out > /sys/class/gpio/gpio113/direction
# set the pin high
echo 1 > /sys/class/gpio/gpio113/value
# set the pin low
echo 0 > /sys/class/gpio/gpio113/value

Python

# set the gpio pin to output
fw = file("/sys/class/gpio/gpio113/direction", "w")
fw.write("out")
fw.close()

# set the gpio pin high
fw = file("/sys/class/gpio/gpio113/value", "w")
fw.write("1")
fw.flush()

# set the pin low again
# Note: the flush() isn't necessary if you immediately close the file after writing
fw.write("0")
fw.close()

An excellent way to test this is to hook an LED and a resistor (about 330Ω should do) between the GPIO pin and ground on the header, then you can see the effect of your code in real time.

Once you're done with a GPIO pin you should "unexport" it again, this removes the folder for controlling it from the system. There's no restriction on using these control files once they've been created e.g. if you export a pin in one program you can set the value and direction in another, however this should be considered bad practice. If a GPIO pin has already been exported or is in use by a kernel driver (the disk activity LED is one example) then trying to export that pin again will cause a Unix "Device or Resource Busy" error when you write to /sys/class/gpio/export. In well written code this should indicate that someone else has already claimed that pin and taking it over should be avoided. Sticking to these rules will make your code robust and able to operate in different environments especially if you're writing code to work with different stacks of shields for example. Once you've done with a GPIO pin you can tidy up after yourself using the unexport file, which works the same way as export.

Shell

echo 113 > /sys/class/gpio/unexport

Python

fw = file("/sys/class/gpio/unexport", "w")
fw.write("%d" % (113))
fw.close()
Primary: 

Comments

Hi Nathan,
Very good notes, thank you!
I followed them and found that pull enable/disable setting is:
Bit: 3
Set(1): Pull disabled
Clear(0): Pull enabled
I'd like to ask which documents you used as references for these notes. I'd like to find by myself which shell commands and files should I use to use other peripherals like SPI, I2C, ,,,
Kind regards

Well I picked up bits of info from a few hours Google searching. The most useful single source was probably this site: https://groups.google.com/forum/?fromgroups#!msg/beagleboard/fz72c2K_2nE... unfortunately it doesn't have any info about any of the other peripherals, only UART. I'd guess they'll have a /dev/?? entry like UARTs do but since SPI/I2C aren't such an integral part of linux and Unix machines normally they are a lot less well documented.

i am facing problems with many GPIO pins, they are not working as GPIO, and inspecting their files where their mode7 should be GPIO, i find it NA !!
so now i have only 16 pins out of the 66 working as GPIO
the rest when exported are viewed as being used by sysfs as usual and change from high to low as usual, but that is not reflect in a high or low voltage output

I think I tried a couple and they worked even though the pin mode 7 was listed as N/A but I think there were also some that didn't work. I haven't checked for a while but there was talk of a new kernel with more of the I/O options exported properly was under way. Only thing I can suggest is see if you can get a newer boot image.

Everyone seams to have terminal stuff. How about some real world interfacing using C language and say a temp sensor or a accelerometer ? The Beaglebone Cape LCD is due out very soon. How about a tutorial with a sensor, a cape and GUI with GTK 3.0 or greater which uses css for styling.

Hi Nathan, This is an informative page. I have been trying to get some benchmarking info for the reaction/servicing of GPIO pins when mapped as interrupts. I can only reliably get a 100Hz duty cycle where I can react to all falling edge triggers.

Have you or anyone else on here done any similar tests ?

I am wanting to get down to 1ms (1KHz) duty cycle but finding it impossible with edge detect.

Hi Marc, no I've not tried any fast reacting GPIO code, I suspected that would be a problem which is why I used the ChipKIT for the real time inputs. I think you'd have to get into kernel module territory to get decent speed response.

Hi Nathan, great example.
but as newbee i have an qeustion about gpio.
what is the ascii value for a GPIO (IN,Pulldown,No PUll, MODE7)
thanks

From the notes above:

1 0 0 111 => 0x27

(To check this put "0b100111 in hex" into google and calculator will change from binary to hex for you)

So to make this the mode do "echo 27 > /sys/kernel/debug/omap_mux/YOUR_PIN_MUX"

Hi, I have been trying to use beaglebone timers for my robotic application. Unfortunately in the pin mux utility all the 4 timer modes are listed as NA. What can be the problem???

I've not tried using the timers. I did find that some functions listed as NA on other pins worked if you selected them anyway. If they still don't work it's possible that they aren't supported by the kernel image yet. If selecting them anyway makes them work don't worry about it, if not, there's some fairly hard-core kernel module tweaking to be done probably.

Hi, thanks for the info. I have another questions, and I hope you could help me with them.
1.How can I read the buffers of the serial ports? in the same way that I read them from Ubuntu (ie, reading from file /dev/ttyO2?, and how to set the baudrate?
2.How can I set the clock of the microprocessor through Angstrom distro?
3.How can I set the clock/timer for the PWM signals?
4.How can I set interruptions for the gpio inputs?
Thanks in advance.

I can answer some of your questions:

1. The serial port works exactly like it would on Ubuntu, use the same code. How you set the baudrate etc. will depend on the language you are using. Just look for examples in whatever language you want to use and assume it works the same on the BeagleBone.

2. Use the date command like any other Linux system or use NTP if you have a network connection. See here for an example using date: http://www.cyberciti.biz/faq/howto-set-date-time-from-linux-command-prompt/

3. I've not tried using PWM on the BeagleBone yet, when I wrote these articles there weren't drivers for it yet and I haven't had time to look at it lately I'm afraid.

4. I'm not sure if/how you can do interrupts on GPIO pins, I suspect you would have to be writing a kernel module to be able to service hardware interrupts but I'm not sure. I think this is probably quite hard to do but I haven't looked into it.

" Since there are 32 I/O per chip, and 4 chips starting from zero the base number is the chip number times 32 i.e. GPIO0 base is 0, GPIO1 base is 32, GPIO2 base is 64 and GPIO3 base is 96. So to find the actual GPIO pin number simply take the chip number, multiply by 32 and add the pin."

I want to use GPIO1_30 as an input pin for example, according to line above, I need to use "echo 62 > /sys/....." . However, I wanna use GPIO1_0 pin also, and Im confused. There 32 I/O per chips, but Im gonna use what for the GPIO1_0? It is "echo 32" or something else? In this case, I wonder "echo 62" also is true or not? Because I cant see any state change when I give input.
Thanks for reading. Please enlighten me.
P.S. I can see the state change from 0 to 1, when I give input to the GPIO0_7 which is already muxed.

Your pin numbers seem to be right there, GPIO1_30 should be 62 and GPIO1_0 should be 32. GPIO0 runs from 0 to 31, GPIO1 from 32 to 63 and so on.

I'm not sure why you're not seeing the state change on that pin, make sure the MUX is set right, are you using any capes with your beaglebone?

After writing comment I realized that its from 0 to 31 :) I solved the problem. I was like "echo 7 > /sys/..." .However I was missing something that I didnt set the 5th bit which determines it will be input or output. Then, I just changed nothing but "echo 27 > /sys...". Now everything is fine :)
Thank you for your concern. By the way its the best source I found about this topic. Thanks

You can use memory mapping to get very fast (5MHz or so) access to gpio pins. It's somewhat silly to use echo.

Here's a tutorial http://www.alexanderhiam.com/tutorials/beaglebone-io-using-python-mmap

That's a useful looking tutorial. I'd definitely recommend mmap for GPIO handling, it is a much more efficient way to do it. Using echo still has its place however, if you're only interested in turning an LED on for example it can be a lot quicker to write the code just using the kernel driver for GPIO pin access via files. Access to GPIO is consistent across loads of boards using the echo or writing to the /sys/class/gpio files, all you need to change is which GPIO number to get code to run on other boards like the ZedBoard.

Sir, I have just started small project with beaglebone black. I want to create small applet on BeagleBone Black linux desktop, which upon clicking should blink LEDs connected to some of GPIOs. I want to power up bone via adapter not PC. How should I start up. I am confused between using Bonescript or Python? I want something like, after writing code it get saved in the BeagleBone Black memeory itself.
Sir please suggest few steps I need to do so and any web resources to follow.

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions. Comments are still moderated just to remove advertising, your opinions/comments will always be approved.
Image CAPTCHA
Enter the characters shown in the image.