EBC GPIO via mmap

In previous exercises (EBC Flashing an LED and EBC gpio Polling and Interrupts) we saw how to interact with the general purpose I/O pins via sysfs files. These are rather easy ways to work with GPIO; however they tend to be slow and require a lot of the CPU. In this exercise we explore accessing GPIO directly via mmap. First we'll do it via  and the command line and later via a C program.

Memory Map
One of the nice things about using GPIO via sysfs is you don't need to know little details like what GPIO pin is mapped to what memory address. Now you need to know those details. Let's flash the USR3 LED by directly writing to memory. First turn off the trigger.

bone$ cd /sys/class/leds/beaglebone\:green\:usr3 bone$ echo none > trigger bone$ echo 1 > brightness The USR3 LED should be on. Now find which GPIO it is attached to. bone$ cd ~/exercises/mmap bone$ ../gpio/findGPIO.js USR3 { name: 'USR3', gpio: 56, led: 'usr3', mux: 'gpmc_a8', key: 'USR3', muxRegOffset: '0x060', options: [ 'gpmc_a8', 'gmii2_rxd3', 'rgmii2_rd3', 'mmc2_dat6', 'gpmc_a24', 'pr1_mii1_rxd0', 'mcasp0_aclkx', 'gpio1_24' ] } USR3 (gpio 56) mode: 7 (gpio1_24) 0x060 pullup pin 24 (44e10860): (MUX UNCLAIMED) (GPIO UNCLAIMED) It's attached to gpio1_24, that is, gpio port 1, bit 24. To find the address of this register, look up the am335x Technical Reference Manual (Google it). Look for GPIO1 in the Memory Map table. You'll see its base address is 0x4804_C000. Click on the GPIO1 link and you'll see '''Table 25-5. GPIO REGISTERS'''. This shows you what to add to the base address to get the various registers for GPIO1. For example, to see how the 32 GPIO1 pins are set you read the GPIO_DATAOUT register who's offset is 0x13Ch. Therefore you want to access 0x4804c000 + 0x13c = 0x4804c13c.

devmem2
An easy way to read the contents of a memory location is with devmem2

Installing devmem2
bone$ wget http://free-electrons.com/pub/mirror/devmem2.c bone$ gcc -o devmem2 devmem2.c bone$ mv devmem2 /usr/bin

Using devmem2
Let's read the USR3 LED status.

bone$ devmem2 0x4804c13c /dev/mem opened. Memory mapped at address 0xb6f99000. Read at address 0x4804C13C (0xb6f9913c): 0x01800000 The address passed is the physical address. devmem2 returns the virtual address along with the contents of the memory location. USR3 is gpio1_24, which is the 24th bit of this value, which is 1 if the LED is on.

The GPIO_CLEARDATAOUT (0x190) register is used to clear given bits in a register. bone$ devmem2 0x4804c190 w 0x01000000 /dev/mem opened. Memory mapped at address 0xb6f53000. Read at address 0x4804C190 (0xb6f53190): 0x01800000 Write at address 0x4804C190 (0xb6f53190): 0x01000000, readback 0x01000000 The LED should be off now. Turn it on using the GPIO_SETDATAOUT (0x194) register. bone$ devmem2 0x4804c194 w 0x01000000 /dev/mem opened. Memory mapped at address 0xb6f9f000. Read at address 0x4804C194 (0xb6f9f194): 0x00800000 Write at address 0x4804C194 (0xb6f9f194): 0x01800000, readback 0x01800000 The USR3 LED should be back on again.

mmap
devmem2 is an easy way to access memory mapped devices from the shell prompt, but if you need speed, you need access from a C program. mmap is a way of mapping an address space into a user-space program. For example, the following code uses mmap to point gpio_addr to the base address for GPIO1. It then computes the addresses for the SETDATAOUT and CLEARDATAOUT registers.

volatile void *gpio_addr; volatile unsigned int *gpio_setdataout_addr; volatile unsigned int *gpio_cleardataout_addr; int fd = open("/dev/mem", O_RDWR); gpio_addr = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDR); gpio_setdataout_addr  = gpio_addr + GPIO_SETDATAOUT; gpio_cleardataout_addr = gpio_addr + GPIO_CLEARDATAOUT;
 * 1) define GPIO1_START_ADDR 0x4804C000
 * 2) define GPIO1_END_ADDR  0x4804e000
 * 3) define GPIO1_SIZE (GPIO1_END_ADDR - GPIO1_START_ADDR)
 * 1) define GPIO_SETDATAOUT 0x194
 * 2) define GPIO_CLEARDATAOUT 0x190
 * 3) define USR3 (1<<24)

gpio_setdataout_addr points to the register that sets the GPIO1 pins, therefore USR3 LED can be turned on with *gpio_setdataout_addr = USR3; and turned off with *gpio_cleardataout_addr = USR3;

gpioToggle.c
gpioToggle.c is a complete example using mmap. Run it with bone$ cd ~/exercises/mmap bone$ make cc -O3 -g -o gpioThru gpioThru.c cc -O3 -g  -o gpioToggle gpioToggle.c beagle$ ./gpioToggle Mapping 4804C000 - 4804E000 (size: 2000) GPIO mapped to 0xb6f40000 GPIO OE mapped to 0xb6f40134 GPIO SETDATAOUTADDR mapped to 0xb6f40194 GPIO CLEARDATAOUT mapped to 0xb6f40190 GPIO1 configuration: F60FFFFF GPIO1 configuration: F60FFFFF Start blinking LED USR3 ^C Ctrl-C pressed, cleaning up and exiting... Notice it reports addresses 0x4804C000 and 0xb6f40000. The first is the physical address of the GPIO1 base register. 0xb6f40000 is the virtual address of the same register. Running gpioToggle again will have the same physical address, but the virtual address might change.

gpioThru
gpioThru uses mmap to read an input pin and write it to an output pin. You can use this program to see how quickly the Beagle can respond to an input. Be sure to run setup.gpioThru.sh before the first time so the pin directions will be set. beagle$ ./setup.gpioThru.sh beagle$ ./gpioThru Mapping 44E07000 - 44E09000 (size: 2000) GPIO mapped to 0xb6f36000 GPIO OE mapped to 0xb6f36134 GPIO SETDATAOUTADDR mapped to 0xb6f36194 GPIO CLEARDATAOUT mapped to 0xb6f36190 Start copying GPIO_07 to GPIO_03 ^C Ctrl-C pressed, cleaning up and exiting...

I'm seeing delays between 220ns and 600ns. The GPIO subsystem runs at 100MHz, so I would expect delays in the 10's of ns, not 100's.