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
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.
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.
|Set (1)||Input||Pull Up||Pull Enabled||Mode|
|Clear (0)||Output||Pull Down||Pull disabled|
Example UART1 TX:
For a UART TX we want:
- 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:
echo 0 > /sys/kernel/debug/omap_mux/uart1_txd
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:
- 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:
echo 20 > /sys/kernel/debug/omap_mux/uart1_rxd
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 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/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/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.
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:
echo 113 > /sys/class/gpio/export
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.
# 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
# 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.
echo 113 > /sys/class/gpio/unexport
fw = file("/sys/class/gpio/unexport", "w") fw.write("%d" % (113)) fw.close()