Last time I wrote about the BeagleBone, a simple and inexpensive ARM Cortex-A8 development board that can run (among other things) Linux. Being Linux based, you don't have to worry about a lot of things you would when you are programming close to the "bare metal." For example, Ethernet, serial ports, USB, and the like just work, which is really very productive.
On the other hand, some devices aren't part of a normal desktop Linux OS. Luckily, the Angstrom distribution I mentioned last time even has drivers for these special devices (actually, the standard gpio-sysfs drivers). For example, consider general-purpose I/O ports. The BeagleBone can support up to 66 3.3V input/output pins. The board has 4 pins (GPIO1_21 to GPIO1_24) connected to LEDs. Well, connected to transistors that turn on LEDs. If you output a high, the LED turns on.
Turns out, there is already a device driver that handles the LEDs, but I am more interested in general-purpose I/O. The first step is to stop that and make the LEDs obey the state of the general-purpose I/O lines and release the leds-gpio driver. From a shell running on the BeagleBone, try this:
[email protected]:/sys/bus/platform/drivers/leds-gpio# echo leds-gpio >unbind
Even though the drivers make it easy to deal with I/O, you also have to account for the multiplexed nature of the pins (that is, each pin can have many different functions). Luckily, there is a driver for the system multiplexer too. The real trick is figuring out what's what, since the BeagleBone documentation is a little disorganized.
One idea is to go to the source: the TI AM3359 technical reference manual. On page 4183 (no, that's not a typo), you'll find a description of the GPIO ports. Note that there are several GPIO units (we are interested in unit 1) and each has 32 pins. However, the BeagleBone will call the ports by number starting with GPIO0_0 as pin 0 and GPIO1_0 as 32. In general, the formula is unit_number*32+pin, so for the first LED pin, the port number is 32*1+21=53. Although there is a lot of good information in the reference manual, it isn't clear (in that section) which other things share a pin with the GPIO ports in question. For that you need the datasheet (which you can download at the same link).
From the datasheet (page 22 on the version I'm reading), you can find a table which shows that gpio1_21 shares a pin with gpmc_a5, gmii2_txd0, and a bunch of other signals. All the gpio1 ports are in "mode 7" and that's the information you need to know. As you'd expect, gpmc_a6 shares with gpio1_22, and so on. The driver will tell you all you need to know if you know that name. Here's a shell running on my BeagleBone (note the working directory):
[email protected]:/sys/kernel/debug/omap_mux# cat gpmc_a5 name: gpmc_a5.gpio1_21 (0x44e10854/0x854 = 0x0007), b NA, t NA mode: OMAP_PIN_OUTPUT | OMAP_MUX_MODE7 signals: gpmc_a5 | mii2_txd0 | rgmii2_td0 | rmii2_txd0 | gpmc_a21 | NA | NA | gpio1_21
In this case, the pin is already set to mode 7 and it is also set as an output. You probably don't want to count on that, though. So:
[email protected]:/sys/kernel/debug/omap_mux# echo 7>gpmc_a5
That sets mode 7. You'd want to do that for all four pins (or, at least, the pins you plan to use).
The driver for the GPIO system doesn't show you all the possible pins, only the ones you "export" like this (note the directory change): [email protected]:/sys/class/gpio# echo 53 >export
Remember that 53 is actually GPIO1_21. Also note there is a space between 53 and the redirect operator. Things don't work without the space!
Now there is a directory called gpio53:
[email protected]:/sys/class/gpio# ls gpio53 active_low direction edge power subsystem uevent value
You can probably guess what at least some of these pseudo files do. The one of main interest here is "direction", which allows you to set the pin as an input or an output and set the output. Writing "low" to this file will set the pin to an output in the low state (as will writing "out"). You might guess that "high" will set the pin high (and thus light the LED). Writing "in" would turn the pin to an input, which isn't very useful for an LED. So:
[email protected]:/sys/class/gpio# echo high >gpio53/direction [email protected]:/sys/class/gpio# echo low >gpio53/direction [email protected]:/sys/class/gpio# echo 53 >unexport [email protected]:/sys/class/gpio# ls export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport
Obviously, you don't want to unexport until you are done. You could use ports 54, 55, and 56 to control the other LEDS. In summary:
- Unbind the leds-gpio driver so you can control the LEDs like regular ports
- Ensure the system mux is set correctly (since the leds-gpio driver did this already, it is a safe bet for the LEDs, but not so safe for other I/O pins)
- Ask the gpio driver to export the pins of interest
- Manipulate the pins through the direction device file
- Ask the driver to unexport the pins when done
Of course, if you really wanted to just use the LEDs, you could skip the unbind and use the leds-gpio driver directly. If you examine the /sys/class/leds directory, you'll easily see how it works (pay attention to the trigger and brightness device files).
Similar devices exist for other devices, like PWM. The nice thing about these being exposed as files is you can access them from almost any kind of programming language — even the shell as I did here and, of course, languages like C or Python. The disadvantage, of course, is you lose some potential performance. But if you were really trying to push the performance envelope, you are probably going to be using a different operating system to begin with.